diff --git a/.mailmap b/.mailmap index 43fba8c532..6115568d80 100644 --- a/.mailmap +++ b/.mailmap @@ -10,9 +10,10 @@ Andrew Goetz andrew goetz anne-laure lugan Bruno Revelin bruno revelin Bruno Revelin bruno revelin -Bryan Cazabonne bryan cazabonne -Bryan Cazabonne bryan cazabonne -Bryan Cazabonne bryan +Bryan Cazabonne bryan cazabonne +Bryan Cazabonne bryan cazabonne +Bryan Cazabonne bryan cazabonne +Bryan Cazabonne bryan Chiara Rusconi chiara rusconi Chiara Rusconi crusconi Clément Jonglez clément jonglez @@ -49,6 +50,7 @@ Luc Maisonobe luc maisonobe luc maisonobe Luc Maisonobe maisonobe Lucian Barbulescu lucian barbulescu +Lucian Barbulescu lucian barbulescu Lukas Matt lukas matt Madalin Mamuleanu madalin mamuleanu MaksimP maksimp diff --git a/build.xml b/build.xml deleted file mode 100644 index 8f6a1e308c..0000000000 --- a/build.xml +++ /dev/null @@ -1,236 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/pom.xml b/pom.xml index 91c08b2338..84171e480f 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.orekit orekit jar - 12.0.2 + 12.1 OREKIT http://www.orekit.org/ @@ -22,24 +22,24 @@ UTF-8 4.2.3 - 0.8.11 + 0.8.12 5.1.9 2.12.1 - 3.2.2 + 3.4.0 9.3 3.3.2 - 3.11.0 - 3.6.0 - 3.3.0 - 3.3.1 + 3.13.0 + 3.7.0 + 3.4.1 + 3.4.0 1.2 - 1.2023.12 + 1.2024.5 3.3.1 3.12.1 - 3.4.5 + 3.6.0 3.5.3 - 3.3.0 + 3.3.1 2.22.2 @@ -48,13 +48,13 @@ https://github.com/mockito/mockito/releases --> 4.11.0 1.2.12 - 3.4.0 - 1.6.13 - 3.1.0 - 3.1.1 + 3.6.0 + 1.7.0 + 3.2.4 + 3.1.2 <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.0 + 3.1 5.10.0 2.2 1.8 @@ -267,6 +267,9 @@ Mikael Fillastre + + Andrea Fiorentino + Alberto Fossà @@ -277,7 +280,7 @@ Andrew Goetz - Andrea Fiorentino + David Gondelach Brad Hards @@ -351,6 +354,9 @@ Beatriz Salazar García + + Christopher Schank + Gabriele Serafini @@ -464,6 +470,12 @@ false test + + org.junit.jupiter + junit-jupiter-params + 5.10.0 + test + org.hamcrest hamcrest @@ -1079,9 +1091,6 @@ org.apache.maven.plugins maven-install-plugin ${orekit.maven-install-plugin.version} - - true - @@ -1152,7 +1161,7 @@ - + diff --git a/src/changes/changes.xml b/src/changes/changes.xml index f5de9f289f..80368fd93e 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -20,10 +20,450 @@ Orekit Changes + + + Use automatic differentiation when possible in nadir pointing. + + + Updated Romanian translation. + + + Add extended position provider. + + + Use SI units for frequencies (Hz instead of MHz). + + + Enable changing the comments in CCSDS messages. + + + Added RadioWave and GnssSignal interfaces above the Frequency enumerate. + + + Fixed FixedPointTleGenerationAlgorithm to not depend on gravity parameter of input orbit. + + + Remove unnecessary computations of attitude rates in numerical propagator. + + + Enabled setting altitude limit of atmosphere in DSSTAtmosphericDrag. + + + Remove unnecessary computations in transformFromInertial for frozen LOF. + + + Extend use of (Field)KinematicTransform. + + + Override getAttitudeRotation in all AttitudeProviderModifier. + + + Use toStaticTransform in FieldOfViewDetector. + + + Add constructor for (Field)ApsideDetector without an Orbit needed. + + + Promoted Dipole constructor to public visibility. + + + Added constructor in {Field}SpacecraftStateInterpolator to defined extrapolation threshold. + + + Deprecated AngularTroposphericDelayModifier because computation is wrong. + + + Add option for automatic differentiation of density for numerical and DSST drag forces. + + + Take advantage of dependsOnAtittudeRate in DSST. + + + Override getTargetPosition in LofOffSetPointing and NadirPointing. + + + Fixed interior point finder in ellipsoid tesselation. + + + Override getTargetPosition in some GroundPointing inheritors. + + + Add override for getAttitudeRotation in CelestialBodyPointed. + + + Add dependsOnAttitudeRate in ForceModel and use it in AbstractGradientConverter. + + + Added ElevationDetectionAdaptableIntervalFactory for elevation detector. + + + Check for constant FieldAbsoluteDate when building FieldKinematicTransform. + + + Add automatic differentiation for atmospheric density gradient in numerical drag. + + + Added public, light-weight routines for Keplerian motion with Cartesian coordinates. + + + Added support for parsing/writing SP3 files in inertial frames. + + + Updated SonarQube configuration in contribution guide. + + + Fixed NaN appearing in atmospheric models. + + + Added Post Seismic Deformation models, and parse ITRF2020 PSD sinex files + to retrieve their parameters. + + + Removed measurement Jacobian computation in UKF. + + + Add simpler signature for estimateWithoutDerivatives. + + + Fixed mismatch between LOFType.EQW javadoc and code. + + + Fixed regression in EphemerisPropagatorBuilder API. + + + Added FieldExtremumApproachDetector and improved detector efficiency. + + + Fixed station eccentricity reference system in tests. + + + Change cached angle type in (Field)KeplerianPropagator. + + + Override getPosition in (Field)Orbit. + + + Add SRP with cylindrical shadow. + + + Retrieve states from derivative providers in init of (Field)AbstractIntegratedIntegrator. + + + Improved performance of STM when there is no force model not dependent on position only. + + + Fixed ignored coordinates system in SP3 parser. + + + Add method to create constant (Field)AdaptableInterval. + + + Add eclipse detector for cylindrical shadow model. + + + Removed support for Ant. + + + Add event handler remembering last occurrence. + + + Switched to (Field)UnivariateDerivative1 instead of 1 in (Field)BrouwerLyddanePropagator. + + + Add wrapper for back and forth conversions from osculating elements for averaging theories. + + + Fixed creating an AbsoluteDate from JD in TDB timescale. + + + Allow parsing of ocean loading coefficients (BLQ files) from a DataSource. + + + Added method build() in PropagatorBuilder. + + + Made dependsOnlyOnPosition check RadiationSensitive in SolarRadiationPressure. + + + Added PerfectClockModel. + + + Fixed wrong attitude overriding in AggregateBoundedPropagator. + + + Allow {Field}AdditionalStateProvider to modify the basic + components of main state (orbit, attitude and mass). + + + Removed inner static class JacobiKey in JacobiPolynomials. + + + Added getTopocentricPosition from (Field)TrackingCoordinates. + + + Added (Field)RelativeDistanceDetector. + + + Added native AdaptableInterval for ApsideDetector. + + + Fixed wrong datation of measurements in Rinex observation + files with offsets not applied. + + + Added extraction of clock models from Rinex observation files. + + + Added splicing of Rinex clock files. + + + Added AggregatedClockModel. + + + Added get{First|Last}NonNullSpan to TimeSpanMap. + + + Added getters to LofOffset. + + + Fixed parsing of Rinex clock files with mixed satellite system. + + + Restored previous API for MeasurementBuilder with SpacecraftState. + + + Added validity range to clock models. + + + Fixed parsing of SP3 files with missing agency in header. + + + Added IGSUtils.guessFrame() to retrieve Earth frames with proper + year and associated IERS conventions. + + + Fixed parsing of ITR## frames with post-Y2K years on two digits. + + + Allow retrieval of clock model from Rinex clock and SP3 files. + + + Added SampledClockModel. + + + Added {Field}ClockOffset. + + + Added J2-only ForceModel for performance. + + + Fixed hasNonKeplerianAcceleration in FieldOrbit and improved performance. + + + Deprecated InterSatellitesPhaseAmbiguityModifier, OneWayGNSSPhaseAmbiguityModifier + and PhaseAmbiguityModifier. + + + Added AmbiguityDriver and AmbiguityCache. + + + Allow generation of Rinex observation files in receiver clock time scale. + + + Added ClockTimeScale. + + + {get|set}ClkOffset → {get|set}ClockOffsetApplied. + + + Added getBodyName in body-based attraction models, as well as common abstract class. + + + Added constructors with orbit type in SmallManeuverAnalyticalModel. + + + Added get(Un)normalizedC20. + + + Added separate access to original estimation and modifications in estimated measurements. + + + Added InterSatellitesOneWayRangeRate measurements. + + + Added constant and quadratic clock models for GNSS measurements. + + + Removed redundant code to create FieldOrbit from Orbit. + + + Improved performance with PositionAngleType in (Field)NumericalPropagator. + + + Removed unnecessary calls to (Field)Transform in (Field)ShortTermEncounter2DDefinition. + + + Added cache for position angle in FieldOrbit when applicable. + + + Manage clock offset as an additional state in propagators built from SP3 files. + + + Added TimeStampedDoubleAndDerivative and associated interpolator. + + + Allow direction-dependent phase centers in inter-satellites measurements. + + + Create two new EventDetector: LatitudeRangeCrossingDetector and LongitudeRangeCrossingDetector. + + + Added cache for position angle in Orbit when applicable. + + + Implement resetIntermediateState in (Field)TLEPropagator. + + + Add default implementation of getPosition in (Field)Propagator. + + + Add (Field)KinematicTransform. + + + Added support for Walker constellations, including in-orbit spares with shifted position. + + + Moved getFieldDate up from FieldTransform to FieldStaticTransform. + + + Added getStaticInverse to (Field)StaticTransform. + + + Make access to raw albedo and infrared radiation pressure in KnockeRediffusedForceModel public. + + + Reduce code duplication in (Field)Propagator inheritors. + + + Take azimuthal asymmetry into account in Vienna tropospheric models. + + + Added GlobalPressureTemperature3 model. + + + Added GlobalPressureTemperature2w (i.e. wet) model. + + + Allow to use any GPT grid file (GPT2, GPT2w, GPT3) in GlobalPressureTemperature2 model. + + + Added providers for horizontal gradient. + + + Added Askne-Nordius tropospheric model. + + + Replaced Vienna{One|Three}Model by Vienna{One|Three}. + + + Added ConstantTroposphericModel. + + + Added ChaoMappingFunction for tropospheric mapping function. + + + Added ModifiedHopfieldModel for tropospheric delay. + + + Added CanonicalSaastamoinenModel for tropospheric delay. + + + Replaced SaastamoinenModel by ModifiedSaastamoinenModel for tropospheric delay. + + + Added NBS/NRC steam table model for water vapor pressure. + + + Added Wang1988 model for water vapor pressure. + + + Added CIPM2007 model for water vapor pressure. + + + Added WaterVaporPressureProvider interface. + + + Added HeightDependentPressureTemperatureHumidityConverter for converting weather parameters. + + + Replaced WeatherModel by {Field}PressureTemperatureHumidityProvider. + + + Added {Field}PressureTemperature and {Field}PressureTemperatureHumidity containers. + + + Replaced GlobalPressureTemperature2Model by GlobalPressureTemperature2. + + + Replaced GlobalPressureTemperatureModel by GlobalPressureTemperature. + + + Replaced MappingFunction by TroposphereMappingFunction. + + + Replaced EstimatedTroposphericModel by EstimatedModel. + + + Replaced DiscreteTroposphericModel by TroposphericModel. + + + Added support for Intelsat's 11 elements propagation. + + + Added NsgfV00Filter to allow parsing some wrong SP3 files. + + + Started using new square method for Field. + + + Fixed parsing of SP3 files with partly missing standard deviations. + + + Added field versions of unit conversions from and to SI units. + + + Removed uses of scalar multiply on Field one. + + + Added utility classes for position angle conversions for (Field) CircularOrbit and EquinoctialOrbit. + + + Fixed exceptions occurring in EOP prediction with ill chosen fitting parameters. + + + Added translation of error messages in Catalan language. + + + Added EventDetector implementations for detecting beta angle crossing events. + + + It fixes issues related to SP3 files, interpolation, measurements, multi-threading + and DSST jacobian setup. Copyright year has been updated."> Change visibility of InertiaAxis and Inertia constructors to public. @@ -74,9 +514,6 @@ Fixed exceptions occurring in EOP prediction with ill chosen fitting parameters. - - Added translation of error messages in Catalan language. - Fixed regression in computation speed when using Ephemeris. @@ -1213,7 +1650,7 @@ 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. + Allowed custom setting of state to TLE conversion in propagator builder. Fixed handling of comments in CRD files. @@ -1895,13 +2332,13 @@ 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. @@ -2148,7 +2585,7 @@ Changing AbstractGNSSAttitudeProvider from public to package-private. - Fix the bug of attitude transition with analytical propagator + Fix the bug of attitude transition with analytical propagator by refreshing the attitude after the events triggering @@ -2196,7 +2633,7 @@ Deprecated unused DerivativeStructure acceleration computation methods. - In interfaces radiationPressureAcceleration and dragAcceleration, and all their implementations and their tests. + In interfaces radiationPressureAcceleration and dragAcceleration, and all their implementations and their tests. Take target radius into account in CircularFieldOfViewDetector and FieldOfViewDetector. @@ -2373,7 +2810,7 @@ Fixes issue #492. - Fixed reference value of parameter drivers updating in Kalman filter. + Fixed reference value of parameter drivers updating in Kalman filter. When resetting the orbit in the propagator builder, the reference values of the drivers are now reset too. Fixes issue #490. @@ -2755,7 +3192,7 @@ 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. + impulse but not directly mass itself. Added parametric acceleration force models, where acceleration amplitude is a @@ -3011,7 +3448,7 @@ file formats, and implemented the OEMWriter for CCSDS OEM file export support. - Added OrekitEphemerisFile object for encapsulating propagator outputs into an + Added OrekitEphemerisFile object for encapsulating propagator outputs into an EphemerisFile which can then be exported with EphemerisFileWriter classes. @@ -3497,7 +3934,7 @@ provided by NOAA. - Fixed javadoc of method "GeoMagneticField#calculateField(...)": + Fixed javadoc of method "GeoMagneticField#calculateField(...)": the provided altitude is expected to be a height above the WGS84 ellipsoid. @@ -3608,7 +4045,7 @@ Allow attitude overriding during impulsive maneuvers. - Fixes issue #176. + Fixes issue #176. Added general relativity force model. @@ -3774,7 +4211,7 @@ Merged all elevation detectors into one. The new detector supports all features from the previous (and now deprecated) ApparentElevationDetector and GroundMaskElevationDetector. - Fixes issue #144. + Fixes issue #144. Added a DetectorEventHandler interface aimed at handling only the @@ -4030,7 +4467,7 @@ IERS does not provide anymore data to link TIRF 2003 with ITRF, ITRF based on 2003 convention is not available. ITRF can only be based on either 2010 conventions for CIO-based paradigm or on 1996 conventions - for equinox-based paradigm. + for equinox-based paradigm. Atmosphere models now provide their central body frame. @@ -4082,7 +4519,7 @@ Streamlined the force models partial derivatives computation for numerical propagation. It is now far simpler to compute analytically the derivatives with respect to state and with respect to force models specific parameters, - thanks to the new Apache Commons Math differentiation framework. + thanks to the new Apache Commons Math differentiation framework. Added a new force model for central body gravity field, based on Holmes and Featherstone @@ -4167,7 +4604,7 @@ (fixes bug #116). - Make TidalCorrections class thread-safe by using the new TimeStampedCache. + Make TidalCorrections class thread-safe by using the new TimeStampedCache. (fixes bug #117). @@ -4244,7 +4681,7 @@ Removed too stringent test on trajectory in TLE propagator (fixes bug #86). - + Set the initial state for a TLEPropagator (fixes bug #85). diff --git a/src/design/available-propagators-class-diagram.puml b/src/design/available-propagators-class-diagram.puml index 12becb92e4..7c9ede409d 100644 --- a/src/design/available-propagators-class-diagram.puml +++ b/src/design/available-propagators-class-diagram.puml @@ -91,6 +91,10 @@ AbstractAnalyticalPropagator <|-- GNSSPropagator } + package intelsat #CCCCC7 { + AbstractAnalyticalPropagator <|-- IntelsatElevenElementsPropagator + } + } package integration #CBDBC8 { diff --git a/src/main/java/org/orekit/attitudes/AttitudeProviderModifier.java b/src/main/java/org/orekit/attitudes/AttitudeProviderModifier.java index fff303f9fa..7f3edc9bf3 100644 --- a/src/main/java/org/orekit/attitudes/AttitudeProviderModifier.java +++ b/src/main/java/org/orekit/attitudes/AttitudeProviderModifier.java @@ -17,6 +17,19 @@ package org.orekit.attitudes; +import org.hipparchus.CalculusFieldElement; +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.frames.Frame; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.AngularCoordinates; +import org.orekit.utils.FieldAngularCoordinates; +import org.orekit.utils.FieldPVCoordinatesProvider; +import org.orekit.utils.PVCoordinatesProvider; + /** This interface represents an attitude provider that modifies/wraps another underlying provider. * @author Luc Maisonobe * @since 5.1 @@ -28,4 +41,33 @@ public interface AttitudeProviderModifier extends AttitudeProvider { */ AttitudeProvider getUnderlyingAttitudeProvider(); + /** + * Wrap the input provider with a new one always returning attitudes with zero rotation rate and acceleration. + * It is not physically sound, but remains useful for performance when a full, physical attitude with time derivatives is not needed. + * @param attitudeProvider provider to wrap + * @return wrapping provider + * @since 12.1 + */ + static AttitudeProviderModifier getFrozenAttitudeProvider(final AttitudeProvider attitudeProvider) { + return new AttitudeProviderModifier() { + @Override + public Attitude getAttitude(final PVCoordinatesProvider pvProv, final AbsoluteDate date, final Frame frame) { + final Rotation rotation = attitudeProvider.getAttitudeRotation(pvProv, date, frame); + final AngularCoordinates angularCoordinates = new AngularCoordinates(rotation, Vector3D.ZERO); + return new Attitude(date, frame, angularCoordinates); + } + + @Override + public > FieldAttitude getAttitude(final FieldPVCoordinatesProvider pvProv, final FieldAbsoluteDate date, final Frame frame) { + final FieldRotation rotation = attitudeProvider.getAttitudeRotation(pvProv, date, frame); + final FieldAngularCoordinates angularCoordinates = new FieldAngularCoordinates<>(rotation, FieldVector3D.getZero(date.getField())); + return new FieldAttitude<>(date, frame, angularCoordinates); + } + + @Override + public AttitudeProvider getUnderlyingAttitudeProvider() { + return attitudeProvider; + } + }; + } } diff --git a/src/main/java/org/orekit/attitudes/AttitudesSequence.java b/src/main/java/org/orekit/attitudes/AttitudesSequence.java index 66291d6521..1ffb291d41 100644 --- a/src/main/java/org/orekit/attitudes/AttitudesSequence.java +++ b/src/main/java/org/orekit/attitudes/AttitudesSequence.java @@ -157,13 +157,13 @@ public void init(final FieldSpacecraftState s0, /** {@inheritDoc} */ @Override public T g(final FieldSpacecraftState s) { - return field.getZero().add(sw.g(s.toSpacecraftState())); + return field.getZero().newInstance(sw.g(s.toSpacecraftState())); } /** {@inheritDoc} */ @Override public T getThreshold() { - return field.getZero().add(sw.getThreshold()); + return field.getZero().newInstance(sw.getThreshold()); } /** {@inheritDoc} */ diff --git a/src/main/java/org/orekit/attitudes/BodyCenterPointing.java b/src/main/java/org/orekit/attitudes/BodyCenterPointing.java index 6e67372386..97b6cc691d 100644 --- a/src/main/java/org/orekit/attitudes/BodyCenterPointing.java +++ b/src/main/java/org/orekit/attitudes/BodyCenterPointing.java @@ -100,6 +100,24 @@ public TimeStampedPVCoordinates getTargetPV(final PVCoordinatesProvider pvProv, } + /** {@inheritDoc} */ + @Override + protected Vector3D getTargetPosition(final PVCoordinatesProvider pvProv, final AbsoluteDate date, final Frame frame) { + // spacecraft coordinates in body frame + final Vector3D scPositionInBodyFrame = pvProv.getPosition(date, getBodyFrame()); + + // central projection to ground (NOT the classical nadir point) + final double u = scPositionInBodyFrame.getX() / ellipsoid.getA(); + final double v = scPositionInBodyFrame.getY() / ellipsoid.getB(); + final double w = scPositionInBodyFrame.getZ() / ellipsoid.getC(); + final double d2 = u * u + v * v + w * w; + final double d = FastMath.sqrt(d2); + final double ratio = 1.0 / d; + final Vector3D projectedP = new Vector3D(ratio, scPositionInBodyFrame); + + return getBodyFrame().getStaticTransformTo(frame, date).transformPosition(projectedP); + } + /** {@inheritDoc} */ public > TimeStampedFieldPVCoordinates getTargetPV(final FieldPVCoordinatesProvider pvProv, final FieldAbsoluteDate date, final Frame frame) { @@ -143,4 +161,21 @@ public > TimeStampedFieldPVCoordinates getT } + /** {@inheritDoc} */ + @Override + protected > FieldVector3D getTargetPosition(final FieldPVCoordinatesProvider pvProv, + final FieldAbsoluteDate date, + final Frame frame) { + // spacecraft coordinates in body frame + final FieldVector3D scPositionInBodyFrame = pvProv.getPosition(date, getBodyFrame()); + + // central projection to ground (NOT the classical nadir point) + final T u = scPositionInBodyFrame.getX().divide(ellipsoid.getA()); + final T v = scPositionInBodyFrame.getY().divide(ellipsoid.getB()); + final T w = scPositionInBodyFrame.getZ().divide(ellipsoid.getC()); + final T d = new FieldVector3D<>(u, v, w).getNorm(); + final FieldVector3D projectedP = new FieldVector3D<>(d.reciprocal(), scPositionInBodyFrame); + + return getBodyFrame().getStaticTransformTo(frame, date).transformPosition(projectedP); + } } diff --git a/src/main/java/org/orekit/attitudes/CelestialBodyPointed.java b/src/main/java/org/orekit/attitudes/CelestialBodyPointed.java index fa54d1fef7..57a253bda9 100644 --- a/src/main/java/org/orekit/attitudes/CelestialBodyPointed.java +++ b/src/main/java/org/orekit/attitudes/CelestialBodyPointed.java @@ -22,8 +22,10 @@ import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.orekit.frames.FieldStaticTransform; import org.orekit.frames.FieldTransform; import org.orekit.frames.Frame; +import org.orekit.frames.StaticTransform; import org.orekit.frames.Transform; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; @@ -98,6 +100,7 @@ public CelestialBodyPointed(final Frame celestialFrame, } /** {@inheritDoc} */ + @Override public Attitude getAttitude(final PVCoordinatesProvider pvProv, final AbsoluteDate date, final Frame frame) { @@ -110,8 +113,7 @@ public Attitude getAttitude(final PVCoordinatesProvider pvProv, final double r2 = Vector3D.dotProduct(pointingP, pointingP); // evaluate instant rotation axis due to sat and body motion only (no phasing yet) - final Vector3D rotAxisCel = - new Vector3D(1 / r2, Vector3D.crossProduct(pointingP, pointing.getVelocity())); + final Vector3D rotAxisCel = new Vector3D(1 / r2, Vector3D.crossProduct(pointingP, pointing.getVelocity())); // fix instant rotation to take phasing constraint into account // (adding a rotation around pointing axis ensuring the motion of the phasing axis @@ -138,9 +140,32 @@ public Attitude getAttitude(final PVCoordinatesProvider pvProv, } /** {@inheritDoc} */ + @Override + public Rotation getAttitudeRotation(final PVCoordinatesProvider pvProv, + final AbsoluteDate date, + final Frame frame) { + final Vector3D satPosition = pvProv.getPosition(date, celestialFrame); + + // compute celestial references at the specified date + final Vector3D bodyPosition = pointedBody.getPosition(date, celestialFrame); + final Vector3D pointingP = bodyPosition.subtract(satPosition); + + // compute static transform from celestial frame to satellite frame + final Rotation celToSatRotation = new Rotation(pointingP, phasingCel, pointingSat, phasingSat); + StaticTransform staticTransform = StaticTransform.of(date, celToSatRotation); + + if (frame != celestialFrame) { + // prepend static transform from specified frame to celestial frame + staticTransform = StaticTransform.compose(date, frame.getStaticTransformTo(celestialFrame, date), staticTransform); + } + return staticTransform.getRotation(); + } + + /** {@inheritDoc} */ + @Override public > FieldAttitude getAttitude(final FieldPVCoordinatesProvider pvProv, - final FieldAbsoluteDate date, - final Frame frame) { + final FieldAbsoluteDate date, + final Frame frame) { final Field field = date.getField(); final FieldPVCoordinates satPV = pvProv.getPVCoordinates(date, celestialFrame); @@ -183,4 +208,30 @@ public > FieldAttitude getAttitude(final Fi } + /** {@inheritDoc} */ + @Override + public > FieldRotation getAttitudeRotation(final FieldPVCoordinatesProvider pvProv, + final FieldAbsoluteDate date, + final Frame frame) { + final Field field = date.getField(); + final FieldVector3D satPosition = pvProv.getPosition(date, celestialFrame); + + // compute celestial references at the specified date + final FieldVector3D bodyPosition = new FieldVector3D<>(field, + pointedBody.getPosition(date.toAbsoluteDate(), celestialFrame)); + final FieldVector3D pointingP = bodyPosition.subtract(satPosition); + + // compute rotation from celestial frame to satellite frame + final FieldRotation celToSatRotation = + new FieldRotation<>(pointingP, new FieldVector3D<>(field, phasingCel), + new FieldVector3D<>(field, pointingSat), new FieldVector3D<>(field, phasingSat)); + + // build static transform combining rotation and instant rotation axis + FieldStaticTransform staticTransform = FieldStaticTransform.of(date, celToSatRotation); + if (frame != celestialFrame) { + // prepend static transform from specified frame to celestial frame + staticTransform = FieldStaticTransform.compose(date, frame.getStaticTransformTo(celestialFrame, date), staticTransform); + } + return staticTransform.getRotation(); + } } diff --git a/src/main/java/org/orekit/attitudes/GroundPointingAttitudeModifier.java b/src/main/java/org/orekit/attitudes/GroundPointingAttitudeModifier.java new file mode 100644 index 0000000000..5eedc711ae --- /dev/null +++ b/src/main/java/org/orekit/attitudes/GroundPointingAttitudeModifier.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.attitudes; + +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.FieldPVCoordinatesProvider; +import org.orekit.utils.PVCoordinatesProvider; +import org.orekit.utils.TimeStampedFieldPVCoordinates; +import org.orekit.utils.TimeStampedPVCoordinates; + +/** + * Abstract class for attitude provider modifiers using an underlying ground pointing law. + * + * @see GroundPointing + * @see AttitudeProviderModifier + * @author Romain Serra + * @since 12.1 + */ +public abstract class GroundPointingAttitudeModifier extends GroundPointing implements AttitudeProviderModifier { + + /** + * Underlying ground pointing law. + */ + private final GroundPointing groundPointingLaw; + + /** Constructor. + * @param inertialFrame frame in which orbital velocities are computed + * @param bodyFrame the frame that rotates with the body + * @param groundPointingLaw underlying ground pointing attitude law + */ + protected GroundPointingAttitudeModifier(final Frame inertialFrame, final Frame bodyFrame, + final GroundPointing groundPointingLaw) { + super(inertialFrame, bodyFrame); + this.groundPointingLaw = groundPointingLaw; + } + + /** + * Getter for underlying ground pointing law. + * @return underlying attitude provider, which in this case is a {@link GroundPointing} instance + */ + @Override + public GroundPointing getUnderlyingAttitudeProvider() { + return groundPointingLaw; + } + + /** Compute the base system state at given date, without modifications. + * @param pvProv provider for PV coordinates + * @param date date at which state is requested + * @param frame reference frame from which attitude is computed + * @return satellite base attitude state. + */ + public Attitude getBaseState(final PVCoordinatesProvider pvProv, + final AbsoluteDate date, final Frame frame) { + return getUnderlyingAttitudeProvider().getAttitude(pvProv, date, frame); + } + + /** Compute the base system state at given date, without modifications. + * @param pvProv provider for PV coordinates + * @param date date at which state is requested + * @param frame reference frame from which attitude is computed + * @param type of the field elements + * @return satellite base attitude state. + */ + public > FieldAttitude getBaseState(final FieldPVCoordinatesProvider pvProv, + final FieldAbsoluteDate date, final Frame frame) { + return getUnderlyingAttitudeProvider().getAttitude(pvProv, date, frame); + } + + /** {@inheritDoc} */ + @Override + public TimeStampedPVCoordinates getTargetPV(final PVCoordinatesProvider pvProv, + final AbsoluteDate date, final Frame frame) { + return groundPointingLaw.getTargetPV(pvProv, date, frame); + } + + /** {@inheritDoc} */ + @Override + protected Vector3D getTargetPosition(final PVCoordinatesProvider pvProv, final AbsoluteDate date, final Frame frame) { + return groundPointingLaw.getTargetPosition(pvProv, date, frame); + } + + /** {@inheritDoc} */ + @Override + public > TimeStampedFieldPVCoordinates getTargetPV(final FieldPVCoordinatesProvider pvProv, + final FieldAbsoluteDate date, + final Frame frame) { + return groundPointingLaw.getTargetPV(pvProv, date, frame); + } + + /** {@inheritDoc} */ + @Override + protected > FieldVector3D getTargetPosition(final FieldPVCoordinatesProvider pvProv, + final FieldAbsoluteDate date, + final Frame frame) { + return groundPointingLaw.getTargetPosition(pvProv, date, frame); + } +} diff --git a/src/main/java/org/orekit/attitudes/LofOffset.java b/src/main/java/org/orekit/attitudes/LofOffset.java index ffc8a79865..3b73b1ee04 100644 --- a/src/main/java/org/orekit/attitudes/LofOffset.java +++ b/src/main/java/org/orekit/attitudes/LofOffset.java @@ -113,6 +113,29 @@ public LofOffset(final Frame inertialFrame, final LOF lof, this.inertialFrame = inertialFrame; } + /** + * Get the local orbital frame. + * @return the local orbital frame. + */ + public LOF getLof() { + return this.lof; + } + + /** + * Get the rotational offset. + * @return the rotational offset. + */ + public Rotation getOffset() { + return this.offset; + } + + /** + * Get the inertial frame. + * @return the inertial frame. + */ + public Frame getInertialFrame() { + return this.inertialFrame; + } /** {@inheritDoc} */ @Override diff --git a/src/main/java/org/orekit/attitudes/LofOffsetPointing.java b/src/main/java/org/orekit/attitudes/LofOffsetPointing.java index 242233df2a..07b81bd06d 100644 --- a/src/main/java/org/orekit/attitudes/LofOffsetPointing.java +++ b/src/main/java/org/orekit/attitudes/LofOffsetPointing.java @@ -126,14 +126,8 @@ public TimeStampedPVCoordinates getTargetPV(final PVCoordinatesProvider pvProv, final AbsoluteDate shifted = date.shiftedBy(i * h); // transform from specified reference frame to spacecraft frame - final StaticTransform refToSc = StaticTransform.compose( - shifted, - StaticTransform.of( - shifted, - pvProv.getPosition(shifted, frame).negate()), - StaticTransform.of( - shifted, - attitudeLaw.getAttitudeRotation(pvProv, shifted, frame))); + final StaticTransform refToSc = StaticTransform.of(shifted, pvProv.getPosition(shifted, frame).negate(), + attitudeLaw.getAttitudeRotation(pvProv, shifted, frame)); // transform from specified reference frame to body frame final StaticTransform refToBody; @@ -160,6 +154,22 @@ public TimeStampedPVCoordinates getTargetPV(final PVCoordinatesProvider pvProv, } + /** {@inheritDoc} */ + @Override + protected Vector3D getTargetPosition(final PVCoordinatesProvider pvProv, final AbsoluteDate date, final Frame frame) { + + // transform from specified reference frame to spacecraft frame + final StaticTransform refToSc = StaticTransform.of(date, pvProv.getPosition(date, frame).negate(), + attitudeLaw.getAttitudeRotation(pvProv, date, frame)); + + // transform from specified reference frame to body frame + final StaticTransform refToBody = frame.getStaticTransformTo(shape.getBodyFrame(), date); + final Vector3D targetBody = losIntersectionWithBody(StaticTransform.compose(date, refToSc.getInverse(), refToBody)).getPosition(); + + // convert back to caller specified frame + return refToBody.getInverse().transformPosition(targetBody); + } + /** {@inheritDoc} */ @Override public > TimeStampedFieldPVCoordinates getTargetPV(final FieldPVCoordinatesProvider pvProv, @@ -175,14 +185,8 @@ public > TimeStampedFieldPVCoordinates getT final FieldAbsoluteDate shifted = date.shiftedBy(i * h); // transform from specified reference frame to spacecraft frame - final FieldStaticTransform refToSc = FieldStaticTransform.compose( - shifted, - FieldStaticTransform.of( - shifted, - pvProv.getPVCoordinates(shifted, frame).getPosition().negate()), - FieldStaticTransform.of( - shifted, - attitudeLaw.getAttitudeRotation(pvProv, shifted, frame))); + final FieldStaticTransform refToSc = FieldStaticTransform.of(shifted, + pvProv.getPVCoordinates(shifted, frame).getPosition().negate(), attitudeLaw.getAttitudeRotation(pvProv, shifted, frame)); // transform from specified reference frame to body frame final FieldStaticTransform refToBody; @@ -209,6 +213,24 @@ public > TimeStampedFieldPVCoordinates getT } + /** {@inheritDoc} */ + @Override + protected > FieldVector3D getTargetPosition(final FieldPVCoordinatesProvider pvProv, + final FieldAbsoluteDate date, + final Frame frame) { + + // transform from specified reference frame to spacecraft frame + final FieldStaticTransform refToSc = FieldStaticTransform.of(date, pvProv.getPosition(date, frame).negate(), + attitudeLaw.getAttitudeRotation(pvProv, date, frame)); + + // transform from specified reference frame to body frame + final FieldStaticTransform refToBody = frame.getStaticTransformTo(shape.getBodyFrame(), date); + final FieldVector3D targetBody = losIntersectionWithBody(FieldStaticTransform.compose(date, refToSc.getInverse(), refToBody)).getPosition(); + + // convert back to caller specified frame + return refToBody.getInverse().transformPosition(targetBody); + } + /** Compute line of sight intersection with body. * @param scToBody transform from spacecraft frame to body frame * @return intersection point in body frame (only the position is set!) diff --git a/src/main/java/org/orekit/attitudes/NadirPointing.java b/src/main/java/org/orekit/attitudes/NadirPointing.java index b96c139e9c..5a35b670db 100644 --- a/src/main/java/org/orekit/attitudes/NadirPointing.java +++ b/src/main/java/org/orekit/attitudes/NadirPointing.java @@ -20,6 +20,11 @@ import java.util.List; 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; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.orekit.bodies.BodyShape; @@ -36,7 +41,9 @@ import org.orekit.time.TimeInterpolator; import org.orekit.utils.CartesianDerivativesFilter; import org.orekit.utils.FieldPVCoordinatesProvider; +import org.orekit.utils.FieldPVCoordinates; import org.orekit.utils.PVCoordinatesProvider; +import org.orekit.utils.PVCoordinates; import org.orekit.utils.TimeStampedFieldPVCoordinates; import org.orekit.utils.TimeStampedFieldPVCoordinatesHermiteInterpolator; import org.orekit.utils.TimeStampedPVCoordinates; @@ -71,9 +78,58 @@ public NadirPointing(final Frame inertialFrame, final BodyShape shape) { } /** {@inheritDoc} */ + @Override public TimeStampedPVCoordinates getTargetPV(final PVCoordinatesProvider pvProv, final AbsoluteDate date, final Frame frame) { + final TimeStampedPVCoordinates pvCoordinatesInRef = pvProv.getPVCoordinates(date, frame); + if (pvCoordinatesInRef.getAcceleration().equals(Vector3D.ZERO)) { + // let us assume that there is no proper acceleration available, so need to use interpolation for derivatives + return getTargetPVViaInterpolation(pvProv, date, frame); + + } else { // use automatic differentiation + // build time dependent transform + final UnivariateDerivative2Field ud2Field = UnivariateDerivative2Field.getInstance(); + final UnivariateDerivative2 dt = new UnivariateDerivative2(0., 1., 0.); + final FieldAbsoluteDate ud2Date = new FieldAbsoluteDate<>(ud2Field, date).shiftedBy(dt); + final FieldStaticTransform refToBody = frame.getStaticTransformTo(shape.getBodyFrame(), ud2Date); + + final FieldVector3D positionInRefFrame = pvCoordinatesInRef.toUnivariateDerivative2Vector(); + final FieldVector3D positionInBodyFrame = refToBody.transformPosition(positionInRefFrame); + + // satellite position in geodetic coordinates + final FieldGeodeticPoint gpSat = shape.transform(positionInBodyFrame, getBodyFrame(), ud2Date); + + // nadir position in geodetic coordinates + final FieldGeodeticPoint gpNadir = new FieldGeodeticPoint<>(gpSat.getLatitude(), + gpSat.getLongitude(), ud2Field.getZero()); + + // nadir point position in body frame + final FieldVector3D positionNadirInBodyFrame = shape.transform(gpNadir); + + // nadir point position in reference frame + final FieldStaticTransform bodyToRef = refToBody.getInverse(); + final FieldVector3D positionNadirInRefFrame = bodyToRef.transformPosition(positionNadirInBodyFrame); + + // put derivatives into proper object + final Vector3D velocity = new Vector3D(positionNadirInRefFrame.getX().getFirstDerivative(), + positionNadirInRefFrame.getY().getFirstDerivative(), positionNadirInRefFrame.getZ().getFirstDerivative()); + final Vector3D acceleration = new Vector3D(positionNadirInRefFrame.getX().getSecondDerivative(), + positionNadirInRefFrame.getY().getSecondDerivative(), positionNadirInRefFrame.getZ().getSecondDerivative()); + return new TimeStampedPVCoordinates(date, positionNadirInRefFrame.toVector3D(), velocity, acceleration); + } + } + + /** + * Compute target position-velocity-acceleration vector via interpolation. + * @param pvProv PV provider + * @param date date + * @param frame frame + * @return target position-velocity-acceleration + */ + public TimeStampedPVCoordinates getTargetPVViaInterpolation(final PVCoordinatesProvider pvProv, + final AbsoluteDate date, final Frame frame) { + // transform from specified reference frame to body frame final Transform refToBody = frame.getTransformTo(shape.getBodyFrame(), date); @@ -96,10 +152,77 @@ public TimeStampedPVCoordinates getTargetPV(final PVCoordinatesProvider pvProv, } /** {@inheritDoc} */ + @Override + protected Vector3D getTargetPosition(final PVCoordinatesProvider pvProv, final AbsoluteDate date, final Frame frame) { + + // transform from specified reference frame to body frame + final Vector3D position = pvProv.getPosition(date, frame); + final PVCoordinates pVWithoutDerivatives = new PVCoordinates(position); + final StaticTransform refToBody = frame.getStaticTransformTo(shape.getBodyFrame(), date); + + return nadirRef(new TimeStampedPVCoordinates(date, pVWithoutDerivatives), refToBody).getPosition(); + } + + /** {@inheritDoc} */ + @Override public > TimeStampedFieldPVCoordinates getTargetPV(final FieldPVCoordinatesProvider pvProv, final FieldAbsoluteDate date, final Frame frame) { + final TimeStampedFieldPVCoordinates pvCoordinatesInRef = pvProv.getPVCoordinates(date, frame); + final Field field = date.getField(); + if (pvCoordinatesInRef.getAcceleration().equals(FieldVector3D.getZero(field))) { + // let us assume that there is no proper acceleration available, so need to use interpolation for derivatives + return getTargetPVViaInterpolation(pvProv, date, frame); + + } 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 FieldStaticTransform> refToBody = frame.getStaticTransformTo(shape.getBodyFrame(), ud2Date); + + final FieldVector3D> positionInRefFrame = pvCoordinatesInRef.toUnivariateDerivative2Vector(); + final FieldVector3D> positionInBodyFrame = refToBody.transformPosition(positionInRefFrame); + + // satellite position in geodetic coordinates + final FieldGeodeticPoint> gpSat = shape.transform(positionInBodyFrame, getBodyFrame(), ud2Date); + + // nadir position in geodetic coordinates + final FieldGeodeticPoint> gpNadir = new FieldGeodeticPoint<>(gpSat.getLatitude(), + gpSat.getLongitude(), ud2Field.getZero()); + + // nadir point position in body frame + final FieldVector3D> positionNadirInBodyFrame = shape.transform(gpNadir); + + // nadir point position in reference frame + final FieldStaticTransform> bodyToRef = refToBody.getInverse(); + final FieldVector3D> positionNadirInRefFrame = bodyToRef.transformPosition(positionNadirInBodyFrame); + + // put derivatives into proper object + final FieldVector3D position = new FieldVector3D<>(positionNadirInRefFrame.getX().getValue(), + positionNadirInRefFrame.getY().getValue(), positionNadirInRefFrame.getZ().getValue()); + final FieldVector3D velocity = new FieldVector3D<>(positionNadirInRefFrame.getX().getFirstDerivative(), + positionNadirInRefFrame.getY().getFirstDerivative(), positionNadirInRefFrame.getZ().getFirstDerivative()); + final FieldVector3D acceleration = new FieldVector3D<>(positionNadirInRefFrame.getX().getSecondDerivative(), + positionNadirInRefFrame.getY().getSecondDerivative(), positionNadirInRefFrame.getZ().getSecondDerivative()); + return new TimeStampedFieldPVCoordinates<>(date, position, velocity, acceleration); + } + + } + + /** + * Compute target position-velocity-acceleration vector via interpolation (Field version). + * @param pvProv PV provider + * @param date date + * @param frame frame + * @param field type + * @return target position-velocity-acceleration + */ + public > TimeStampedFieldPVCoordinates getTargetPVViaInterpolation(final FieldPVCoordinatesProvider pvProv, + final FieldAbsoluteDate date, final Frame frame) { + // zero final T zero = date.getField().getZero(); @@ -109,11 +232,11 @@ public > TimeStampedFieldPVCoordinates getT // sample intersection points in current date neighborhood final double h = 0.01; final List> sample = new ArrayList<>(); - sample.add(nadirRef(pvProv.getPVCoordinates(date.shiftedBy(-2 * h), frame), refToBody.staticShiftedBy(zero.add(-2 * h)))); - sample.add(nadirRef(pvProv.getPVCoordinates(date.shiftedBy(-h), frame), refToBody.staticShiftedBy(zero.add(-h)))); + sample.add(nadirRef(pvProv.getPVCoordinates(date.shiftedBy(-2 * h), frame), refToBody.staticShiftedBy(zero.newInstance(-2 * h)))); + sample.add(nadirRef(pvProv.getPVCoordinates(date.shiftedBy(-h), frame), refToBody.staticShiftedBy(zero.newInstance(-h)))); sample.add(nadirRef(pvProv.getPVCoordinates(date, frame), refToBody)); - sample.add(nadirRef(pvProv.getPVCoordinates(date.shiftedBy(+h), frame), refToBody.staticShiftedBy(zero.add(+h)))); - sample.add(nadirRef(pvProv.getPVCoordinates(date.shiftedBy(+2 * h), frame), refToBody.staticShiftedBy(zero.add(+2 * h)))); + sample.add(nadirRef(pvProv.getPVCoordinates(date.shiftedBy(+h), frame), refToBody.staticShiftedBy(zero.newInstance(+h)))); + sample.add(nadirRef(pvProv.getPVCoordinates(date.shiftedBy(+2 * h), frame), refToBody.staticShiftedBy(zero.newInstance(+2 * h)))); // create interpolator final FieldTimeInterpolator, T> interpolator = @@ -124,6 +247,21 @@ public > TimeStampedFieldPVCoordinates getT } + /** {@inheritDoc} */ + @Override + protected > FieldVector3D getTargetPosition(final FieldPVCoordinatesProvider pvProv, + final FieldAbsoluteDate date, + final Frame frame) { + + // transform from specified reference frame to body frame + final FieldVector3D position = pvProv.getPosition(date, frame); + final FieldPVCoordinates pVWithoutDerivatives = new FieldPVCoordinates<>(position, FieldVector3D.getZero(date.getField())); + final FieldStaticTransform refToBody = frame.getStaticTransformTo(shape.getBodyFrame(), date); + + return nadirRef(new TimeStampedFieldPVCoordinates(date, pVWithoutDerivatives), refToBody).getPosition(); + + } + /** Compute ground point in nadir direction, in reference frame. * @param scRef spacecraft coordinates in reference frame * @param refToBody transform from reference frame to body frame diff --git a/src/main/java/org/orekit/attitudes/SpinStabilized.java b/src/main/java/org/orekit/attitudes/SpinStabilized.java index f0aeceaf99..a4364f735e 100644 --- a/src/main/java/org/orekit/attitudes/SpinStabilized.java +++ b/src/main/java/org/orekit/attitudes/SpinStabilized.java @@ -74,11 +74,13 @@ public SpinStabilized(final AttitudeProvider nonRotatingLaw, } /** {@inheritDoc} */ + @Override public AttitudeProvider getUnderlyingAttitudeProvider() { return nonRotatingLaw; } /** {@inheritDoc} */ + @Override public Attitude getAttitude(final PVCoordinatesProvider pvProv, final AbsoluteDate date, final Frame frame) { @@ -104,6 +106,20 @@ public Attitude getAttitude(final PVCoordinatesProvider pvProv, } /** {@inheritDoc} */ + @Override + public Rotation getAttitudeRotation(final PVCoordinatesProvider pvProv, final AbsoluteDate date, final Frame frame) { + // get rotation from underlying non-rotating law + final Rotation baseRotation = nonRotatingLaw.getAttitudeRotation(pvProv, date, frame); + + // compute spin rotation due to spin from reference to current date + final Rotation spinInfluence = new Rotation(axis, rate * date.durationFrom(start), RotationConvention.FRAME_TRANSFORM); + + // combine the two rotations + return baseRotation.compose(spinInfluence, RotationConvention.FRAME_TRANSFORM); + } + + /** {@inheritDoc} */ + @Override public > FieldAttitude getAttitude(final FieldPVCoordinatesProvider pvProv, final FieldAbsoluteDate date, final Frame frame) { @@ -129,4 +145,22 @@ public > FieldAttitude getAttitude(final Fi } + /** {@inheritDoc} */ + @Override + public > FieldRotation getAttitudeRotation(final FieldPVCoordinatesProvider pvProv, + final FieldAbsoluteDate date, + final Frame frame) { + + // get attitude from underlying non-rotating law + final FieldRotation baseRotation = nonRotatingLaw.getAttitudeRotation(pvProv, date, frame); + + // compute spin rotation due to spin from reference to current date + final FieldRotation spinInfluence = + new FieldRotation<>(new FieldVector3D<>(date.getField(), axis), + date.durationFrom(start).multiply(rate), + RotationConvention.FRAME_TRANSFORM); + + // combine the two rotations + return baseRotation.compose(spinInfluence, RotationConvention.FRAME_TRANSFORM); + } } diff --git a/src/main/java/org/orekit/attitudes/TargetPointing.java b/src/main/java/org/orekit/attitudes/TargetPointing.java index 3af22e3799..4a235015ef 100644 --- a/src/main/java/org/orekit/attitudes/TargetPointing.java +++ b/src/main/java/org/orekit/attitudes/TargetPointing.java @@ -21,8 +21,10 @@ import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.orekit.bodies.BodyShape; import org.orekit.bodies.GeodeticPoint; +import org.orekit.frames.FieldStaticTransform; import org.orekit.frames.FieldTransform; import org.orekit.frames.Frame; +import org.orekit.frames.StaticTransform; import org.orekit.frames.Transform; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; @@ -85,10 +87,17 @@ public TimeStampedPVCoordinates getTargetPV(final PVCoordinatesProvider pvProv, return t.transformPVCoordinates(pv); } + /** {@inheritDoc} */ + @Override + protected Vector3D getTargetPosition(final PVCoordinatesProvider pvProv, final AbsoluteDate date, final Frame frame) { + final StaticTransform staticTransform = getBodyFrame().getStaticTransformTo(frame, date); + return staticTransform.transformPosition(target); + } + /** {@inheritDoc} */ @Override public > TimeStampedFieldPVCoordinates getTargetPV(final FieldPVCoordinatesProvider pvProv, - final FieldAbsoluteDate date, final Frame frame) { + final FieldAbsoluteDate date, final Frame frame) { final FieldTransform t = getBodyFrame().getTransformTo(frame, date); final FieldVector3D zero = FieldVector3D.getZero(date.getField()); final TimeStampedFieldPVCoordinates pv = @@ -96,4 +105,12 @@ public > TimeStampedFieldPVCoordinates getT return t.transformPVCoordinates(pv); } + /** {@inheritDoc} */ + @Override + protected > FieldVector3D getTargetPosition(final FieldPVCoordinatesProvider pvProv, + final FieldAbsoluteDate date, + final Frame frame) { + final FieldStaticTransform staticTransform = getBodyFrame().getStaticTransformTo(frame, date); + return staticTransform.transformPosition(target); + } } diff --git a/src/main/java/org/orekit/attitudes/TorqueFree.java b/src/main/java/org/orekit/attitudes/TorqueFree.java index eb2e3fedef..f76cd84d12 100644 --- a/src/main/java/org/orekit/attitudes/TorqueFree.java +++ b/src/main/java/org/orekit/attitudes/TorqueFree.java @@ -516,11 +516,11 @@ private class FieldModel > { final T o1 = FieldVector3D.dotProduct(omega0, n1); final T o2 = FieldVector3D.dotProduct(omega0, n2); final T o3 = FieldVector3D.dotProduct(omega0, n3); - final T o12 = o1.multiply(o1); - final T o22 = o2.multiply(o2); - final T o32 = o3.multiply(o3); + final T o12 = o1.square(); + final T o22 = o2.square(); + final T o32 = o3.square(); final T twoE = fI1.multiply(o12).add(fI2.multiply(o22)).add(fI3.multiply(o32)); - final T m2 = fI1.multiply(fI1).multiply(o12).add(fI2.multiply(fI2).multiply(o22)).add(fI3.multiply(fI3).multiply(o32)); + final T m2 = fI1.square().multiply(o12).add(fI2.square().multiply(o22)).add(fI3.square().multiply(o32)); final T separatrixInertia = (twoE.isZero()) ? zero : m2.divide(twoE); final boolean clockwise; if (separatrixInertia.subtract(tmpInertia.getInertiaAxis2().getI()).getReal() < 0) { @@ -651,7 +651,7 @@ public int getDimension() { public T[] computeDerivatives(final T t, final T[] y) { final T sn = jacobi.valuesN(t.subtract(dtRef).multiply(tScale)).sn(); final T[] yDot = MathArrays.buildArray(dtRef.getField(), 1); - yDot[0] = b.divide(c.add(d.multiply(sn).multiply(sn))); + yDot[0] = b.divide(c.add(d.multiply(sn.square()))); return yDot; } diff --git a/src/main/java/org/orekit/attitudes/YawCompensation.java b/src/main/java/org/orekit/attitudes/YawCompensation.java index 6c6fc8a24e..73a1baa881 100644 --- a/src/main/java/org/orekit/attitudes/YawCompensation.java +++ b/src/main/java/org/orekit/attitudes/YawCompensation.java @@ -34,8 +34,6 @@ import org.orekit.utils.PVCoordinatesProvider; import org.orekit.utils.TimeStampedAngularCoordinates; import org.orekit.utils.TimeStampedFieldAngularCoordinates; -import org.orekit.utils.TimeStampedFieldPVCoordinates; -import org.orekit.utils.TimeStampedPVCoordinates; /** @@ -62,7 +60,7 @@ * @see GroundPointing * @author Véronique Pommier-Maurussane */ -public class YawCompensation extends GroundPointing implements AttitudeProviderModifier { +public class YawCompensation extends GroundPointingAttitudeModifier implements AttitudeProviderModifier { /** J axis. */ private static final PVCoordinates PLUS_J = @@ -72,61 +70,13 @@ public class YawCompensation extends GroundPointing implements AttitudeProviderM private static final PVCoordinates PLUS_K = new PVCoordinates(Vector3D.PLUS_K, Vector3D.ZERO, Vector3D.ZERO); - /** Underlying ground pointing attitude provider. */ - private final GroundPointing groundPointingLaw; - /** Creates a new instance. * @param inertialFrame frame in which orbital velocities are computed * @param groundPointingLaw ground pointing attitude provider without yaw compensation * @since 7.1 */ public YawCompensation(final Frame inertialFrame, final GroundPointing groundPointingLaw) { - super(inertialFrame, groundPointingLaw.getBodyFrame()); - this.groundPointingLaw = groundPointingLaw; - } - - /** Get the underlying (ground pointing) attitude provider. - * @return underlying attitude provider, which in this case is a {@link GroundPointing} instance - */ - public AttitudeProvider getUnderlyingAttitudeProvider() { - return groundPointingLaw; - } - - /** {@inheritDoc} */ - public TimeStampedPVCoordinates getTargetPV(final PVCoordinatesProvider pvProv, - final AbsoluteDate date, final Frame frame) { - return groundPointingLaw.getTargetPV(pvProv, date, frame); - } - - /** {@inheritDoc} */ - public > TimeStampedFieldPVCoordinates getTargetPV(final FieldPVCoordinatesProvider pvProv, - final FieldAbsoluteDate date, - final Frame frame) { - return groundPointingLaw.getTargetPV(pvProv, date, frame); - } - - /** Compute the base system state at given date, without compensation. - * @param pvProv provider for PV coordinates - * @param date date at which state is requested - * @param frame reference frame from which attitude is computed - * @return satellite base attitude state, i.e without compensation. - */ - public Attitude getBaseState(final PVCoordinatesProvider pvProv, - final AbsoluteDate date, final Frame frame) { - return groundPointingLaw.getAttitude(pvProv, date, frame); - } - - /** Compute the base system state at given date, without compensation. - * @param pvProv provider for PV coordinates - * @param date date at which state is requested - * @param frame reference frame from which attitude is computed - * @param type of the field elements - * @return satellite base attitude state, i.e without compensation. - * @since 9.0 - */ - public > FieldAttitude getBaseState(final FieldPVCoordinatesProvider pvProv, - final FieldAbsoluteDate date, final Frame frame) { - return groundPointingLaw.getAttitude(pvProv, date, frame); + super(inertialFrame, groundPointingLaw.getBodyFrame(), groundPointingLaw); } /** {@inheritDoc} */ diff --git a/src/main/java/org/orekit/attitudes/YawSteering.java b/src/main/java/org/orekit/attitudes/YawSteering.java index 15ef610503..80ee9ac298 100644 --- a/src/main/java/org/orekit/attitudes/YawSteering.java +++ b/src/main/java/org/orekit/attitudes/YawSteering.java @@ -29,8 +29,6 @@ import org.orekit.utils.PVCoordinatesProvider; import org.orekit.utils.TimeStampedAngularCoordinates; import org.orekit.utils.TimeStampedFieldAngularCoordinates; -import org.orekit.utils.TimeStampedFieldPVCoordinates; -import org.orekit.utils.TimeStampedPVCoordinates; /** @@ -47,7 +45,7 @@ * but gets more and more square like as the Sun gets closer to the * orbital plane. The degenerate extreme case with the Sun in the orbital * plane leads to a yaw angle switching between two steady states, with - * instantaneaous π radians rotations at each switch, two times per orbit. + * instantaneous π radians rotations at each switch, two times per orbit. * This degenerate case is clearly not operationally sound so another pointing * mode is chosen when Sun comes closer than some predefined threshold to the * orbital plane. @@ -68,15 +66,12 @@ * @see GroundPointing * @author Luc Maisonobe */ -public class YawSteering extends GroundPointing implements AttitudeProviderModifier { +public class YawSteering extends GroundPointingAttitudeModifier implements AttitudeProviderModifier { /** Pointing axis. */ private static final PVCoordinates PLUS_Z = new PVCoordinates(Vector3D.PLUS_K, Vector3D.ZERO, Vector3D.ZERO); - /** Underlying ground pointing attitude provider. */ - private final GroundPointing groundPointingLaw; - /** Sun motion model. */ private final PVCoordinatesProvider sun; @@ -95,58 +90,13 @@ public YawSteering(final Frame inertialFrame, final GroundPointing groundPointingLaw, final PVCoordinatesProvider sun, final Vector3D phasingAxis) { - super(inertialFrame, groundPointingLaw.getBodyFrame()); - this.groundPointingLaw = groundPointingLaw; + super(inertialFrame, groundPointingLaw.getBodyFrame(), groundPointingLaw); this.sun = sun; this.phasingNormal = new PVCoordinates(Vector3D.crossProduct(Vector3D.PLUS_K, phasingAxis).normalize(), Vector3D.ZERO, Vector3D.ZERO); } - /** Get the underlying (ground pointing) attitude provider. - * @return underlying attitude provider, which in this case is a {@link GroundPointing} instance - */ - public AttitudeProvider getUnderlyingAttitudeProvider() { - return groundPointingLaw; - } - - /** {@inheritDoc} */ - public TimeStampedPVCoordinates getTargetPV(final PVCoordinatesProvider pvProv, - final AbsoluteDate date, final Frame frame) { - return groundPointingLaw.getTargetPV(pvProv, date, frame); - } - - /** {@inheritDoc} */ - public > TimeStampedFieldPVCoordinates getTargetPV(final FieldPVCoordinatesProvider pvProv, - final FieldAbsoluteDate date, - final Frame frame) { - return groundPointingLaw.getTargetPV(pvProv, date, frame); - } - - /** Compute the base system state at given date, without compensation. - * @param pvProv provider for PV coordinates - * @param date date at which state is requested - * @param frame reference frame from which attitude is computed - * @return satellite base attitude state, i.e without compensation. - */ - public Attitude getBaseState(final PVCoordinatesProvider pvProv, - final AbsoluteDate date, final Frame frame) { - return groundPointingLaw.getAttitude(pvProv, date, frame); - } - - /** Compute the base system state at given date, without compensation. - * @param pvProv provider for PV coordinates - * @param date date at which state is requested - * @param frame reference frame from which attitude is computed - * @param type of the field elements - * @return satellite base attitude state, i.e without compensation. - * @since 9.0 - */ - public > FieldAttitude getBaseState(final FieldPVCoordinatesProvider pvProv, - final FieldAbsoluteDate date, final Frame frame) { - return groundPointingLaw.getAttitude(pvProv, date, frame); - } - /** {@inheritDoc} */ @Override public Attitude getAttitude(final PVCoordinatesProvider pvProv, diff --git a/src/main/java/org/orekit/bodies/FieldEllipse.java b/src/main/java/org/orekit/bodies/FieldEllipse.java index 789faed1e1..0066082a43 100644 --- a/src/main/java/org/orekit/bodies/FieldEllipse.java +++ b/src/main/java/org/orekit/bodies/FieldEllipse.java @@ -99,13 +99,13 @@ public FieldEllipse(final FieldVector3D center, final FieldVector3D u, this.a = a; this.b = b; this.frame = frame; - this.a2 = a.multiply(a); + this.a2 = a.square(); this.g = b.divide(a); this.g2 = g.multiply(g); this.e2 = g2.negate().add(1); - this.b2 = b.multiply(b); - this.evoluteFactorX = a2.subtract(b2).divide(a2.multiply(a2)); - this.evoluteFactorY = b2.subtract(a2).divide(b2.multiply(b2)); + this.b2 = b.square(); + this.evoluteFactorX = a2.subtract(b2).divide(a2.square()); + this.evoluteFactorY = b2.subtract(a2).divide(b2.square()); } /** Get the center of the 2D ellipse. @@ -273,7 +273,7 @@ public TimeStampedFieldPVCoordinates projectToEllipse(final TimeStampedFieldP // tangent to the ellipse final T fx = a2.negate().multiply(e2D.getY()); final T fy = b2.multiply(e2D.getX()); - final T f2 = fx.multiply(fx).add(fy.multiply(fy)); + final T f2 = fx.square().add(fy.square()); final T f = FastMath.sqrt(f2); final FieldVector2Dtangent = new FieldVector2D<>(fx.divide(f), fy.divide(f)); diff --git a/src/main/java/org/orekit/bodies/FieldGeodeticPoint.java b/src/main/java/org/orekit/bodies/FieldGeodeticPoint.java index 5e846714dd..805e82a4aa 100644 --- a/src/main/java/org/orekit/bodies/FieldGeodeticPoint.java +++ b/src/main/java/org/orekit/bodies/FieldGeodeticPoint.java @@ -19,6 +19,7 @@ import java.text.NumberFormat; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.util.CompositeFormat; import org.hipparchus.util.FastMath; @@ -61,10 +62,11 @@ public class FieldGeodeticPoint> { /** West direction. */ private FieldVector3D west; - /** - * Build a new instance. The angular coordinates will be normalized so that + /** Build a new instance. + *

+ * The angular coordinates will be normalized so that * the latitude is between ±π/2 and the longitude is between ±π. - * + *

* @param latitude latitude of the point (rad) * @param longitude longitude of the point (rad) * @param altitude altitude of the point (m) @@ -85,6 +87,17 @@ public FieldGeodeticPoint(final T latitude, final T longitude, this.altitude = altitude; } + /** Build a new instance from a {@link GeodeticPoint}. + * @param field field to which the elements belong + * @param geodeticPoint geodetic point to convert + * @since 12.1 + */ + public FieldGeodeticPoint(final Field field, final GeodeticPoint geodeticPoint) { + this(field.getZero().newInstance(geodeticPoint.getLatitude()), + field.getZero().newInstance(geodeticPoint.getLongitude()), + field.getZero().newInstance(geodeticPoint.getAltitude())); + } + /** Get the latitude. * @return latitude, an angular value in the range [-π/2, π/2] */ diff --git a/src/main/java/org/orekit/bodies/JPLCelestialBody.java b/src/main/java/org/orekit/bodies/JPLCelestialBody.java index 814cc6e855..bce94b3710 100644 --- a/src/main/java/org/orekit/bodies/JPLCelestialBody.java +++ b/src/main/java/org/orekit/bodies/JPLCelestialBody.java @@ -256,7 +256,7 @@ public Transform getTransform(final AbsoluteDate date) { @Override public StaticTransform getStaticTransform(final AbsoluteDate date) { // compute translation from parent frame to self - final PVCoordinates pv = getPVCoordinates(date, definingFrame); + final Vector3D position = getPosition(date, definingFrame); // compute rotation from ICRF frame to self, // as per the "Report of the IAU/IAG Working Group on Cartographic @@ -273,7 +273,7 @@ public StaticTransform getStaticTransform(final AbsoluteDate date) { new Rotation(pole, qNode, Vector3D.PLUS_K, Vector3D.PLUS_I); // update transform from parent to self - return StaticTransform.of(date, pv.getPosition().negate(), rotation); + return StaticTransform.of(date, position.negate(), rotation); } /** {@inheritDoc} */ @@ -314,7 +314,7 @@ public > FieldStaticTransform getStaticTran // field final Field field = date.getField(); // compute translation from parent frame to self - final FieldPVCoordinates pv = getPVCoordinates(date, definingFrame); + final FieldVector3D position = getPosition(date, definingFrame); // compute rotation from ICRF frame to self, // as per the "Report of the IAU/IAG Working Group on Cartographic @@ -331,7 +331,7 @@ public > FieldStaticTransform getStaticTran new FieldRotation<>(pole, qNode, FieldVector3D.getPlusK(field), FieldVector3D.getPlusI(field)); // update transform from parent to self - return FieldStaticTransform.of(date, pv.getPosition().negate(), rotation); + return FieldStaticTransform.of(date, position.negate(), rotation); } }, frameName == null ? name + INERTIAL_FRAME_SUFFIX : frameName, true); diff --git a/src/main/java/org/orekit/bodies/OneAxisEllipsoid.java b/src/main/java/org/orekit/bodies/OneAxisEllipsoid.java index 9a8e307b94..1a4e150a48 100644 --- a/src/main/java/org/orekit/bodies/OneAxisEllipsoid.java +++ b/src/main/java/org/orekit/bodies/OneAxisEllipsoid.java @@ -280,21 +280,21 @@ public > FieldVector3D getCartesianIntersec final T x = point.getX(); final T y = point.getY(); final T z = point.getZ(); - final T z2 = z.multiply(z); - final T r2 = x.multiply(x).add(y.multiply(y)); + final T z2 = z.square(); + final T r2 = x.square().add(y.square()); final FieldVector3D direction = lineInBodyFrame.getDirection(); final T dx = direction.getX(); final T dy = direction.getY(); final T dz = direction.getZ(); - final T cz2 = dx.multiply(dx).add(dy.multiply(dy)); + final T cz2 = dx.square().add(dy.square()); // abscissa of the intersection as a root of a 2nd degree polynomial : // a k^2 - 2 b k + c = 0 final T a = cz2.multiply(e2).subtract(1.0).negate(); final T b = x.multiply(dx).add(y.multiply(dy)).multiply(g2).add(z.multiply(dz)).negate(); final T c = r2.subtract(ae2).multiply(g2).add(z2); - final T b2 = b.multiply(b); + final T b2 = b.square(); final T ac = a.multiply(c); if (b2.getReal() < ac.getReal()) { return null; @@ -683,8 +683,8 @@ public > FieldGeodeticPoint transform(final sn = sn.scalb(-exp); cn = cn.scalb(-exp); - sn2 = sn.multiply(sn); - cn2 = cn.multiply(cn); + sn2 = sn.square(); + cn2 = cn.square(); an2 = cn2.add(sn2); an = an2.sqrt(); @@ -727,7 +727,7 @@ public FieldGeodeticPoint transform(final PVCoordinates poi final Transform toBody = frame.getTransformTo(bodyFrame, date); final PVCoordinates pointInBodyFrame = toBody.transformPVCoordinates(point); final FieldVector3D p = pointInBodyFrame.toDerivativeStructureVector(2); - final DerivativeStructure pr2 = p.getX().multiply(p.getX()).add(p.getY().multiply(p.getY())); + final DerivativeStructure pr2 = p.getX().square().add(p.getY().square()); final DerivativeStructure pr = pr2.sqrt(); final DerivativeStructure pz = p.getZ(); @@ -735,7 +735,7 @@ public FieldGeodeticPoint transform(final PVCoordinates poi final TimeStampedPVCoordinates groundPoint = projectToGround(new TimeStampedPVCoordinates(date, pointInBodyFrame), bodyFrame); final FieldVector3D gp = groundPoint.toDerivativeStructureVector(2); - final DerivativeStructure gpr2 = gp.getX().multiply(gp.getX()).add(gp.getY().multiply(gp.getY())); + final DerivativeStructure gpr2 = gp.getX().square().add(gp.getY().square()); final DerivativeStructure gpr = gpr2.sqrt(); final DerivativeStructure gpz = gp.getZ(); diff --git a/src/main/java/org/orekit/bodies/PosVelChebyshev.java b/src/main/java/org/orekit/bodies/PosVelChebyshev.java index 7310df0553..8fc11d2c08 100644 --- a/src/main/java/org/orekit/bodies/PosVelChebyshev.java +++ b/src/main/java/org/orekit/bodies/PosVelChebyshev.java @@ -169,9 +169,9 @@ > FieldVector3D getPosition(final FieldAbso // initialize Chebyshev polynomials recursion T pKm1 = one; T pK = t; - T xP = zero.add(xCoeffs[0]); - T yP = zero.add(yCoeffs[0]); - T zP = zero.add(zCoeffs[0]); + T xP = zero.newInstance(xCoeffs[0]); + T yP = zero.newInstance(yCoeffs[0]); + T zP = zero.newInstance(zCoeffs[0]); // combine polynomials by applying coefficients for (int k = 1; k < xCoeffs.length; ++k) { @@ -281,9 +281,9 @@ > FieldPVCoordinates getPositionVelocityAcc // initialize Chebyshev polynomials recursion T pKm1 = one; T pK = t; - T xP = zero.add(xCoeffs[0]); - T yP = zero.add(yCoeffs[0]); - T zP = zero.add(zCoeffs[0]); + T xP = zero.newInstance(xCoeffs[0]); + T yP = zero.newInstance(yCoeffs[0]); + T zP = zero.newInstance(zCoeffs[0]); // initialize Chebyshev polynomials derivatives recursion T qKm1 = zero; diff --git a/src/main/java/org/orekit/data/LineOrientedFilteringReader.java b/src/main/java/org/orekit/data/LineOrientedFilteringReader.java new file mode 100644 index 0000000000..498c2c149a --- /dev/null +++ b/src/main/java/org/orekit/data/LineOrientedFilteringReader.java @@ -0,0 +1,114 @@ +/* Copyright 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.data; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; + +import org.hipparchus.util.FastMath; + +/** Base class for implementing line-oriented data filtering readers. + *

+ * This reader is intended to be used in {@link DataFilter}. + *

+ * @author Luc Maisonobe + * @since 12.1 + */ +public abstract class LineOrientedFilteringReader extends Reader { + + /** Line-oriented input. */ + private final BufferedReader reader; + + /** Line number. */ + private int lastLineNumber; + + /** Pending filtered output lines. */ + private CharSequence pending; + + /** Number of characters already output in pending lines. */ + private int countOut; + + /** Simple constructor. + * @param name file name + * @param input underlying raw stream + * @exception IOException if first lines cannot be read + */ + public LineOrientedFilteringReader(final String name, final Reader input) throws IOException { + reader = new BufferedReader(input); + lastLineNumber = 0; + } + + /** Get the underlying line-oriented reader. + * @return underlying line-oriented reader + */ + protected BufferedReader getBufferedReader() { + return reader; + } + + /** {@inheritDoc} */ + @Override + public int read(final char[] b, final int offset, final int len) throws IOException { + + if (pending == null) { + // we need to read another line from the underlying characters stream and filter it + countOut = 0; + final String originalLine = reader.readLine(); + ++lastLineNumber; + if (originalLine == null) { + // there are no lines left + return -1; + } else { + pending = filterLine(lastLineNumber, originalLine); + } + } + + // copy as many characters as possible from current line + int n = FastMath.min(len, pending.length() - countOut); + for (int i = 0; i < n; ++i) { + b[offset + i] = pending.charAt(countOut + i); + } + + if (n < len) { + // line has been completed and we can still output end of line + b[offset + n] = '\n'; + pending = null; + ++n; + } else { + // there are still some pending characters + countOut += n; + } + + return n; + + } + + /** Filter one line. + * @param lineNumber line number + * @param originalLine original line + * @return filtered line + * @exception IOException if line cannot be parsed + */ + protected abstract CharSequence filterLine(int lineNumber, String originalLine) throws IOException; + + /** {@inheritDoc} */ + @Override + public void close() throws IOException { + reader.close(); + } + +} diff --git a/src/main/java/org/orekit/errors/OrekitMessages.java b/src/main/java/org/orekit/errors/OrekitMessages.java index eda4f46b09..3adf0318ea 100644 --- a/src/main/java/org/orekit/errors/OrekitMessages.java +++ b/src/main/java/org/orekit/errors/OrekitMessages.java @@ -17,16 +17,10 @@ package org.orekit.errors; import org.hipparchus.exception.Localizable; +import org.hipparchus.exception.UTF8Control; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.URL; -import java.net.URLConnection; -import java.nio.charset.StandardCharsets; import java.util.Locale; import java.util.MissingResourceException; -import java.util.PropertyResourceBundle; import java.util.ResourceBundle; /** @@ -489,6 +483,11 @@ public enum OrekitMessages implements Localizable { /** SP3_INCOMPATIBLE_SATELLITE_MEDATADA. */ SP3_INCOMPATIBLE_SATELLITE_MEDATADA("cannot splice sp3 files with incompatible satellite metadata for satellite {0}"), + /** FRAME_NOT_ALLOWED. + * @since 12.1 + */ + FRAME_NOT_ALLOWED("frame {0} not allowed here"), + /** STK_INVALID_OR_UNSUPPORTED_COORDINATE_SYSTEM. */ STK_INVALID_OR_UNSUPPORTED_COORDINATE_SYSTEM("STK coordinate system \"{0}\" is invalid or not yet supported"), @@ -537,6 +536,12 @@ public enum OrekitMessages implements Localizable { /** UNABLE_TO_COMPUTE_HYPERBOLIC_ECCENTRIC_ANOMALY. */ UNABLE_TO_COMPUTE_HYPERBOLIC_ECCENTRIC_ANOMALY("unable to compute hyperbolic eccentric anomaly from the mean anomaly after {0} iterations"), + /** UNABLE_TO_COMPUTE_ECCENTRIC_LONGITUDE_ARGUMENT. */ + UNABLE_TO_COMPUTE_ECCENTRIC_LONGITUDE_ARGUMENT("unable to compute eccentric longitude argument from the mean one after {0} iterations"), + + /** UNABLE_TO_COMPUTE_ECCENTRIC_LATITUDE_ARGUMENT. */ + UNABLE_TO_COMPUTE_ECCENTRIC_LATITUDE_ARGUMENT("unable to compute eccentric latitude argument from the mean one after {0} iterations"), + /** UNABLE_TO_COMPUTE_DSST_MEAN_PARAMETERS. */ UNABLE_TO_COMPUTE_DSST_MEAN_PARAMETERS("unable to compute mean orbit from osculating orbit after {0} iterations"), @@ -879,6 +884,9 @@ public enum OrekitMessages implements Localizable { /** DATES_MISMATCH. */ DATES_MISMATCH("first date {0} does not match second date {1}"), + /** WRONG ELEMENTS FOR AVERAGING THEORY. */ + WRONG_ELEMENTS_FOR_AVERAGING_THEORY("unexpected type of orbital elements for required averaging theory"), + /** ORBITS_MUS_MISMATCH. */ ORBITS_MUS_MISMATCH("first orbit mu {0} does not match second orbit mu {1}"), @@ -906,11 +914,20 @@ public enum OrekitMessages implements Localizable { /** CANNOT_START_PROPAGATION_FROM_INFINITY. */ CANNOT_START_PROPAGATION_FROM_INFINITY("Cannot start the propagation from an infinitely far date"), + /** TOO_LONG_TIME_GAP_BETWEEN_DATA_POINTS. */ + TOO_LONG_TIME_GAP_BETWEEN_DATA_POINTS("Too long time gap between data points: {0} s"), + /** INVALID_SATELLITE_ID. */ INVALID_SATELLITE_ID("invalid satellite id {0}"), /** WRONG_EOP_INTERPOLATION_DEGREE. */ - WRONG_EOP_INTERPOLATION_DEGREE("EOP interpolation degree must be of the form 4k-1, got {0}"); + WRONG_EOP_INTERPOLATION_DEGREE("EOP interpolation degree must be of the form 4k-1, got {0}"), + + /** WALKER_INCONSISTENT_PLANES. */ + 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"); /** Base name of the resource bundle in classpath. */ private static final String RESOURCE_BASE_NAME = "assets/org/orekit/localization/OrekitMessages"; @@ -938,8 +955,7 @@ public String getLocalizedString(final Locale locale) { final ResourceBundle bundle = ResourceBundle.getBundle(RESOURCE_BASE_NAME, locale, new UTF8Control()); if (bundle.getLocale().getLanguage().equals(locale.getLanguage())) { final String translated = bundle.getString(name()); - if (translated != null && translated.length() > 0 && - !translated.toLowerCase().contains("missing translation")) { + if (!(translated.isEmpty() || translated.toLowerCase().contains("missing translation"))) { // the value of the resource is the translated format return translated; } @@ -954,59 +970,4 @@ public String getLocalizedString(final Locale locale) { return sourceFormat; } - - /** - * Control class loading properties in UTF-8 encoding. - *

- * This class has been very slightly adapted from BalusC answer to question: - * - * How to use UTF-8 in resource properties with ResourceBundle. - *

- * @since 6.0 - */ - public static class UTF8Control extends ResourceBundle.Control { - - /** Empty constructor. - *

- * This constructor is not strictly necessary, but it prevents spurious - * javadoc warnings with JDK 18 and later. - *

- * @since 12.0 - */ - public UTF8Control() { - // nothing to do - } - - /** {@inheritDoc} */ - @Override - public ResourceBundle newBundle(final String baseName, final Locale locale, final String format, - final ClassLoader loader, final boolean reload) - throws IllegalAccessException, InstantiationException, IOException { - // The below is a copy of the default implementation. - final String bundleName = toBundleName(baseName, locale); - final String resourceName = toResourceName(bundleName, "utf8"); - ResourceBundle bundle = null; - InputStream stream = null; - if (reload) { - final URL url = loader.getResource(resourceName); - if (url != null) { - final URLConnection connection = url.openConnection(); - if (connection != null) { - connection.setUseCaches(false); - stream = connection.getInputStream(); - } - } - } else { - stream = loader.getResourceAsStream(resourceName); - } - if (stream != null) { - try (InputStreamReader inputStreamReader = new InputStreamReader(stream, StandardCharsets.UTF_8)) { - // Only this line is changed to make it to read properties files as UTF-8. - bundle = new PropertyResourceBundle(inputStreamReader); - } - } - return bundle; - } - } } diff --git a/src/main/java/org/orekit/estimation/leastsquares/AbstractBatchLSModel.java b/src/main/java/org/orekit/estimation/leastsquares/AbstractBatchLSModel.java index f2472103e5..37060fa9f3 100644 --- a/src/main/java/org/orekit/estimation/leastsquares/AbstractBatchLSModel.java +++ b/src/main/java/org/orekit/estimation/leastsquares/AbstractBatchLSModel.java @@ -16,14 +16,6 @@ */ package org.orekit.estimation.leastsquares; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.IdentityHashMap; -import java.util.List; -import java.util.Map; - import org.hipparchus.linear.Array2DRowRealMatrix; import org.hipparchus.linear.ArrayRealVector; import org.hipparchus.linear.MatrixUtils; @@ -47,8 +39,16 @@ import org.orekit.utils.ParameterDriver; import org.orekit.utils.ParameterDriversList; import org.orekit.utils.ParameterDriversList.DelegatingDriver; -import org.orekit.utils.TimeSpanMap.Span; import org.orekit.utils.TimeSpanMap; +import org.orekit.utils.TimeSpanMap.Span; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; /** Bridge between {@link ObservedMeasurement measurements} and {@link * org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresProblem @@ -227,13 +227,7 @@ public AbstractBatchLSModel(final PropagatorBuilder[] propagatorBuilders, lastDate = measurements.get(measurements.size() - 1).getDate(); // Decide the direction of propagation - if (FastMath.abs(refDate.durationFrom(firstDate)) <= FastMath.abs(refDate.durationFrom(lastDate))) { - // Propagate forward from firstDate - forwardPropagation = true; - } else { - // Propagate backward from lastDate - forwardPropagation = false; - } + forwardPropagation = FastMath.abs(refDate.durationFrom(firstDate)) <= FastMath.abs(refDate.durationFrom(lastDate)); } /** Set the counter for evaluations. @@ -307,7 +301,7 @@ public Pair value(final RealVector point) { observer.modelCalled(orbits, evaluations); - return new Pair(value, jacobian); + return new Pair<>(value, jacobian); } diff --git a/src/main/java/org/orekit/estimation/leastsquares/DSSTBatchLSModel.java b/src/main/java/org/orekit/estimation/leastsquares/DSSTBatchLSModel.java index ccb95f8b54..9d4b641c95 100644 --- a/src/main/java/org/orekit/estimation/leastsquares/DSSTBatchLSModel.java +++ b/src/main/java/org/orekit/estimation/leastsquares/DSSTBatchLSModel.java @@ -16,8 +16,6 @@ */ package org.orekit.estimation.leastsquares; -import java.util.List; - import org.orekit.estimation.measurements.ObservedMeasurement; import org.orekit.orbits.Orbit; import org.orekit.propagation.MatricesHarvester; @@ -25,10 +23,11 @@ import org.orekit.propagation.Propagator; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.conversion.PropagatorBuilder; -import org.orekit.propagation.semianalytical.dsst.DSSTHarvester; import org.orekit.propagation.semianalytical.dsst.DSSTPropagator; import org.orekit.utils.ParameterDriversList; +import java.util.List; + /** Bridge between {@link ObservedMeasurement measurements} and {@link * org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresProblem * least squares problems}. @@ -77,17 +76,10 @@ protected MatricesHarvester configureHarvester(final Propagator propagator) { protected Orbit configureOrbits(final MatricesHarvester harvester, final Propagator propagator) { // Cast final DSSTPropagator dsstPropagator = (DSSTPropagator) propagator; - final DSSTHarvester dsstHarvester = (DSSTHarvester) harvester; // Mean orbit final SpacecraftState initial = dsstPropagator.initialIsOsculating() ? DSSTPropagator.computeMeanState(dsstPropagator.getInitialState(), dsstPropagator.getAttitudeProvider(), dsstPropagator.getAllForceModels()) : dsstPropagator.getInitialState(); - dsstHarvester.initializeFieldShortPeriodTerms(initial); - // Compute short period derivatives at the beginning of the iteration - if (propagationType == PropagationType.OSCULATING) { - dsstHarvester.updateFieldShortPeriodTerms(initial); - dsstHarvester.setReferenceState(initial); - } // Compute short period derivatives at the beginning of the iteration harvester.setReferenceState(initial); return initial.getOrbit(); diff --git a/src/main/java/org/orekit/estimation/measurements/AbstractMeasurement.java b/src/main/java/org/orekit/estimation/measurements/AbstractMeasurement.java index 650f5242f6..bbacd09a1f 100644 --- a/src/main/java/org/orekit/estimation/measurements/AbstractMeasurement.java +++ b/src/main/java/org/orekit/estimation/measurements/AbstractMeasurement.java @@ -25,11 +25,18 @@ import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; +import org.orekit.annotation.DefaultDataContext; +import org.orekit.frames.Frame; +import org.orekit.frames.FramesFactory; import org.orekit.propagation.SpacecraftState; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.Constants; +import org.orekit.utils.FieldPVCoordinatesProvider; +import org.orekit.utils.PVCoordinatesProvider; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.FieldShiftingPVCoordinatesProvider; +import org.orekit.utils.ShiftingPVCoordinatesProvider; import org.orekit.utils.TimeStampedFieldPVCoordinates; import org.orekit.utils.TimeStampedPVCoordinates; @@ -272,15 +279,58 @@ public List> getModifiers() { * in the same frame as {@code adjustableEmitterPV} * @param signalArrivalDate date at which the signal arrives to receiver * @return positive delay between signal emission and signal reception dates + * @deprecated as of 12.1, replaced by either {@link #signalTimeOfFlight(TimeStampedPVCoordinates, + * Vector3D, AbsoluteDate, Frame)} or {@link #signalTimeOfFlight(PVCoordinatesProvider, AbsoluteDate, + * Vector3D, AbsoluteDate, Frame)} */ + @Deprecated + @DefaultDataContext public static double signalTimeOfFlight(final TimeStampedPVCoordinates adjustableEmitterPV, final Vector3D receiverPosition, final AbsoluteDate signalArrivalDate) { + return signalTimeOfFlight(adjustableEmitterPV, receiverPosition, signalArrivalDate, + FramesFactory.getGCRF()); + } + + /** Compute propagation delay on a link leg (typically downlink or uplink). + * @param adjustableEmitterPV position/velocity of emitter that may be adjusted + * @param receiverPosition fixed position of receiver at {@code signalArrivalDate} + * @param receiverFrame frame in which both {@code adjustableEmitterPV} and + * {@code receiver receiverPosition} are defined + * @param signalArrivalDate date at which the signal arrives to receiver + * @return positive delay between signal emission and signal reception dates + * @since 12.1 + */ + public static double signalTimeOfFlight(final TimeStampedPVCoordinates adjustableEmitterPV, + final Vector3D receiverPosition, + final AbsoluteDate signalArrivalDate, + final Frame receiverFrame) { + return signalTimeOfFlight(new ShiftingPVCoordinatesProvider(adjustableEmitterPV, + receiverFrame), + adjustableEmitterPV.getDate(), + receiverPosition, signalArrivalDate, + receiverFrame); + } + + /** Compute propagation delay on a link leg (typically downlink or uplink). + * @param adjustableEmitter position/velocity provider of emitter + * @param approxEmissionDate approximate emission date + * @param receiverPosition fixed position of receiver at {@code signalArrivalDate} + * @param signalArrivalDate date at which the signal arrives to receiver + * @param receiverFrame frame in which receiver is defined + * @return positive delay between signal emission and signal reception dates + * @since 12.1 + */ + public static double signalTimeOfFlight(final PVCoordinatesProvider adjustableEmitter, + final AbsoluteDate approxEmissionDate, + final Vector3D receiverPosition, + final AbsoluteDate signalArrivalDate, + final Frame receiverFrame) { // initialize emission date search loop assuming the state is already correct // this will be true for all but the first orbit determination iteration, // and even for the first iteration the loop will converge very fast - final double offset = signalArrivalDate.durationFrom(adjustableEmitterPV.getDate()); + final double offset = signalArrivalDate.durationFrom(approxEmissionDate); double delay = offset; // search signal transit date, computing the signal travel in inertial frame @@ -289,7 +339,8 @@ public static double signalTimeOfFlight(final TimeStampedPVCoordinates adjustabl int count = 0; do { final double previous = delay; - final Vector3D transitP = adjustableEmitterPV.shiftedBy(offset - delay).getPosition(); + final Vector3D transitP = adjustableEmitter.getPosition(approxEmissionDate.shiftedBy(offset - delay), + receiverFrame); delay = receiverPosition.distance(transitP) * cReciprocal; delta = FastMath.abs(delay - previous); } while (count++ < 10 && delta >= 2 * FastMath.ulp(delay)); @@ -305,15 +356,61 @@ public static double signalTimeOfFlight(final TimeStampedPVCoordinates adjustabl * @param signalArrivalDate date at which the signal arrives to receiver * @return positive delay between signal emission and signal reception dates * @param the type of the components + * @deprecated as of 12.1, replaced by either {@link #signalTimeOfFlight(TimeStampedFieldPVCoordinates, + * FieldVector3D, FieldAbsoluteDate, Frame)} or {@link #signalTimeOfFlight(FieldPVCoordinatesProvider, + * FieldAbsoluteDate, FieldVector3D, FieldAbsoluteDate, Frame)} */ + @Deprecated + @DefaultDataContext public static > T signalTimeOfFlight(final TimeStampedFieldPVCoordinates adjustableEmitterPV, final FieldVector3D receiverPosition, final FieldAbsoluteDate signalArrivalDate) { + return signalTimeOfFlight(adjustableEmitterPV, receiverPosition, signalArrivalDate, + FramesFactory.getGCRF()); + } + + /** Compute propagation delay on a link leg (typically downlink or uplink). + * @param adjustableEmitterPV position/velocity of emitter that may be adjusted + * @param receiverPosition fixed position of receiver at {@code signalArrivalDate}, + * in the same frame as {@code adjustableEmitterPV} + * @param signalArrivalDate date at which the signal arrives to receiver + * @return positive delay between signal emission and signal reception dates + * @param receiverFrame frame in which receiver is defined + * @param the type of the components + * @since 12.1 + */ + public static > T signalTimeOfFlight(final TimeStampedFieldPVCoordinates adjustableEmitterPV, + final FieldVector3D receiverPosition, + final FieldAbsoluteDate signalArrivalDate, + final Frame receiverFrame) { + return signalTimeOfFlight(new FieldShiftingPVCoordinatesProvider<>(adjustableEmitterPV, + receiverFrame), + adjustableEmitterPV.getDate(), + receiverPosition, signalArrivalDate, + receiverFrame); + } + + /** Compute propagation delay on a link leg (typically downlink or uplink). + * @param adjustableEmitter position/velocity provider of emitter + * @param approxEmissionDate approximate emission date + * @param receiverPosition fixed position of receiver at {@code signalArrivalDate}, + * in the same frame as {@code adjustableEmitterPV} + * @param signalArrivalDate date at which the signal arrives to receiver + * @param receiverFrame frame in which receiver is defined + * @return positive delay between signal emission and signal reception dates + * @param the type of the components + * @since 12.1 + */ + public static > T signalTimeOfFlight(final FieldPVCoordinatesProvider adjustableEmitter, + final FieldAbsoluteDate approxEmissionDate, + final FieldVector3D receiverPosition, + final FieldAbsoluteDate signalArrivalDate, + final Frame receiverFrame) { // Initialize emission date search loop assuming the emitter PV is almost correct // this will be true for all but the first orbit determination iteration, // and even for the first iteration the loop will converge extremely fast - final T offset = signalArrivalDate.durationFrom(adjustableEmitterPV.getDate()); + final T offset = signalArrivalDate.durationFrom(approxEmissionDate); T delay = offset; // search signal transit date, computing the signal travel in the frame shared by emitter and receiver @@ -322,7 +419,8 @@ public static > T signalTimeOfFlight(final Tim int count = 0; do { final double previous = delay.getReal(); - final FieldVector3D transitP = adjustableEmitterPV.shiftedBy(delay.negate().add(offset)).getPosition(); + final FieldVector3D transitP = adjustableEmitter.getPosition(approxEmissionDate.shiftedBy(offset.subtract(delay)), + receiverFrame); delay = receiverPosition.distance(transitP).multiply(cReciprocal); delta = FastMath.abs(delay.getReal() - previous); } while (count++ < 10 && delta >= 2 * FastMath.ulp(delay.getReal())); diff --git a/src/main/java/org/orekit/estimation/measurements/AngularAzEl.java b/src/main/java/org/orekit/estimation/measurements/AngularAzEl.java index 9928bb2cbb..b90c517720 100644 --- a/src/main/java/org/orekit/estimation/measurements/AngularAzEl.java +++ b/src/main/java/org/orekit/estimation/measurements/AngularAzEl.java @@ -147,15 +147,13 @@ protected EstimatedMeasurement theoreticalEvaluation(final int iter // azimuth - elevation values estimated.setEstimatedValue(azimuth.getValue(), elevation.getValue()); - // Partial derivatives of azimuth/elevation with respect to state - // (beware element at index 0 is the value, not a derivative) + // First order derivatives of azimuth/elevation with respect to state final double[] azDerivatives = azimuth.getGradient(); final double[] elDerivatives = elevation.getGradient(); estimated.setStateDerivatives(0, Arrays.copyOfRange(azDerivatives, 0, 6), Arrays.copyOfRange(elDerivatives, 0, 6)); - // Set partial derivatives with respect to parameters - // (beware element at index 0 is the value, not a derivative) + // Set first order derivatives of azimuth/elevation with respect to state for (final ParameterDriver driver : getParametersDrivers()) { for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { diff --git a/src/main/java/org/orekit/estimation/measurements/AngularRaDec.java b/src/main/java/org/orekit/estimation/measurements/AngularRaDec.java index 2d1b675d13..12c4e32ea2 100644 --- a/src/main/java/org/orekit/estimation/measurements/AngularRaDec.java +++ b/src/main/java/org/orekit/estimation/measurements/AngularRaDec.java @@ -165,14 +165,12 @@ protected EstimatedMeasurement theoreticalEvaluation(final int ite estimated.setEstimatedValue(rightAscension.getValue(), declination.getValue()); // Partial derivatives of right ascension/declination in reference frame with respect to state - // (beware element at index 0 is the value, not a derivative) final double[] raDerivatives = rightAscension.getGradient(); final double[] decDerivatives = declination.getGradient(); estimated.setStateDerivatives(0, Arrays.copyOfRange(raDerivatives, 0, 6), Arrays.copyOfRange(decDerivatives, 0, 6)); // Partial derivatives with respect to parameters - // (beware element at index 0 is the value, not a derivative) for (final ParameterDriver driver : getParametersDrivers()) { for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { final Integer index = common.getIndices().get(span.getData()); diff --git a/src/main/java/org/orekit/estimation/measurements/BistaticRange.java b/src/main/java/org/orekit/estimation/measurements/BistaticRange.java index 2aa57c841b..2f451e7408 100644 --- a/src/main/java/org/orekit/estimation/measurements/BistaticRange.java +++ b/src/main/java/org/orekit/estimation/measurements/BistaticRange.java @@ -126,7 +126,8 @@ protected EstimatedMeasurementBase theoreticalEvaluationWithoutDe Vector3D.ZERO, Vector3D.ZERO, Vector3D.ZERO)); // Uplink time of flight from emitter station to transit state - final double tauU = signalTimeOfFlight(emitterApprox, transitPV.getPosition(), transitDate); + final double tauU = signalTimeOfFlight(emitterApprox, transitPV.getPosition(), transitDate, + common.getState().getFrame()); // Secondary station PV in inertial frame at rebound date on secondary station final TimeStampedPVCoordinates emitterPV = emitterApprox.shiftedBy(-tauU); @@ -184,7 +185,8 @@ protected EstimatedMeasurement theoreticalEvaluation(final int it zero, zero, zero)); // Uplink time of flight from emiiter to transit state - final Gradient tauU = signalTimeOfFlight(emitterApprox, transitPV.getPosition(), transitPV.getDate()); + final Gradient tauU = signalTimeOfFlight(emitterApprox, transitPV.getPosition(), + transitPV.getDate(), state.getFrame()); // Emitter coordinates at transmit time final TimeStampedFieldPVCoordinates emitterPV = emitterApprox.shiftedBy(tauU.negate()); @@ -207,12 +209,11 @@ protected EstimatedMeasurement theoreticalEvaluation(final int it estimated.setEstimatedValue(range.getValue()); - // Range partial derivatives with respect to state + // Range first order derivatives with respect to state final double[] derivatives = range.getGradient(); estimated.setStateDerivatives(0, Arrays.copyOfRange(derivatives, 0, 6)); - // set partial derivatives with respect to parameters - // (beware element at index 0 is the value, not a derivative) + // Set first order derivatives with respect to parameters for (final ParameterDriver driver : getParametersDrivers()) { for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { final Integer index = common.getIndices().get(span.getData()); diff --git a/src/main/java/org/orekit/estimation/measurements/BistaticRangeRate.java b/src/main/java/org/orekit/estimation/measurements/BistaticRangeRate.java index 54365a86cf..03195b3ffc 100644 --- a/src/main/java/org/orekit/estimation/measurements/BistaticRangeRate.java +++ b/src/main/java/org/orekit/estimation/measurements/BistaticRangeRate.java @@ -121,7 +121,8 @@ protected EstimatedMeasurementBase theoreticalEvaluationWitho Vector3D.ZERO, Vector3D.ZERO, Vector3D.ZERO)); // Uplink time of flight from emitter station to transit state - final double tauU = signalTimeOfFlight(emitterApprox, transitPV.getPosition(), transitDate); + final double tauU = signalTimeOfFlight(emitterApprox, transitPV.getPosition(), transitDate, + common.getState().getFrame()); // Secondary station PV in inertial frame at rebound date on secondary station final TimeStampedPVCoordinates emitterPV = emitterApprox.shiftedBy(-tauU); @@ -189,7 +190,8 @@ protected EstimatedMeasurement theoreticalEvaluation(final in zero, zero, zero)); // Uplink time of flight from emiiter to transit state - final Gradient tauU = signalTimeOfFlight(emitterApprox, transitPV.getPosition(), transitPV.getDate()); + final Gradient tauU = signalTimeOfFlight(emitterApprox, transitPV.getPosition(), transitPV.getDate(), + state.getFrame()); // Emitter coordinates at transmit time final TimeStampedFieldPVCoordinates emitterPV = emitterApprox.shiftedBy(tauU.negate()); @@ -222,12 +224,11 @@ protected EstimatedMeasurement theoreticalEvaluation(final in .add(FieldVector3D.dotProduct(emitterDirection, emitterVelocity)); estimated.setEstimatedValue(rangeRate.getValue()); - // Range partial derivatives with respect to state + // Range first order derivatives with respect to state final double[] derivatives = rangeRate.getGradient(); estimated.setStateDerivatives(0, Arrays.copyOfRange(derivatives, 0, 6)); - // set partial derivatives with respect to parameters - // (beware element at index 0 is the value, not a derivative) + // Set first order derivatives with respect to parameters for (final ParameterDriver driver : getParametersDrivers()) { for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { final Integer index = common.getIndices().get(span.getData()); diff --git a/src/main/java/org/orekit/estimation/measurements/CommonParametersWithDerivatives.java b/src/main/java/org/orekit/estimation/measurements/CommonParametersWithDerivatives.java new file mode 100644 index 0000000000..eef46e361a --- /dev/null +++ b/src/main/java/org/orekit/estimation/measurements/CommonParametersWithDerivatives.java @@ -0,0 +1,100 @@ +/* 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.estimation.measurements; + +import org.hipparchus.analysis.differentiation.Gradient; +import org.orekit.propagation.SpacecraftState; +import org.orekit.utils.TimeStampedFieldPVCoordinates; + +import java.util.Map; + +/** Common intermediate parameters used to estimate measurements where receiver is a ground station. + * @author Luc Maisonobe + * @since 12.1 + */ +public class CommonParametersWithDerivatives { + + /** Spacecraft state. */ + private final SpacecraftState state; + + /** Derivatives indices map. */ + private final Map indices; + + /** Downlink delay. */ + private final Gradient tauD; + + /** Transit state. */ + private final SpacecraftState transitState; + + /** Transit state. */ + private final TimeStampedFieldPVCoordinates transitPV; + + /** Simple constructor. + * @param state spacecraft state + * @param indices derivatives indices map + * @param tauD downlink delay + * @param transitState transit state + * @param transitPV transit position/velocity as a gradient + */ + public CommonParametersWithDerivatives(final SpacecraftState state, + final Map indices, + final Gradient tauD, + final SpacecraftState transitState, + final TimeStampedFieldPVCoordinates transitPV) { + this.state = state; + this.indices = indices; + this.tauD = tauD; + this.transitState = transitState; + this.transitPV = transitPV; + } + + /** Get spacecraft state. + * @return spacecraft state + */ + public SpacecraftState getState() { + return state; + } + + /** Get derivatives indices map. + * @return derivatives indices map + */ + public Map getIndices() { + return indices; + } + + /** Get downlink delay. + * @return ownlink delay + */ + public Gradient getTauD() { + return tauD; + } + + /** Get transit state. + * @return transit state + */ + public SpacecraftState getTransitState() { + return transitState; + } + + /** Get transit position/velocity. + * @return transit position/velocity + */ + public TimeStampedFieldPVCoordinates getTransitPV() { + return transitPV; + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/CommonParametersWithoutDerivatives.java b/src/main/java/org/orekit/estimation/measurements/CommonParametersWithoutDerivatives.java new file mode 100644 index 0000000000..f8d2720dbe --- /dev/null +++ b/src/main/java/org/orekit/estimation/measurements/CommonParametersWithoutDerivatives.java @@ -0,0 +1,84 @@ +/* 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.estimation.measurements; + +import org.orekit.propagation.SpacecraftState; +import org.orekit.utils.TimeStampedPVCoordinates; + +/** Common intermediate parameters used to estimate measurements. + * @author Luc Maisonobe + * @since 12.1 + */ +public class CommonParametersWithoutDerivatives { + + /** Spacecraft state. */ + private final SpacecraftState state; + + /** Downlink delay. */ + private final double tauD; + + /** Transit state. */ + private final SpacecraftState transitState; + + /** Transit position/velocity. */ + private final TimeStampedPVCoordinates transitPV; + + /** Simple constructor. + * @param state spacecraft state + * @param tauD downlink delay + * @param transitState transit state + * @param transitPV transit position/velocity + */ + public CommonParametersWithoutDerivatives(final SpacecraftState state, + final double tauD, + final SpacecraftState transitState, + final TimeStampedPVCoordinates transitPV) { + this.state = state; + this.tauD = tauD; + this.transitState = transitState; + this.transitPV = transitPV; + } + + /** Get spacecraft state. + * @return spacecraft state + */ + public SpacecraftState getState() { + return state; + } + + /** Get downlink delay. + * @return ownlink delay + */ + public double getTauD() { + return tauD; + } + + /** Get transit state. + * @return transit state + */ + public SpacecraftState getTransitState() { + return transitState; + } + + /** Get transit position/velocity. + * @return transit position/velocity + */ + public TimeStampedPVCoordinates getTransitPV() { + return transitPV; + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/EstimatedEarthFrameProvider.java b/src/main/java/org/orekit/estimation/measurements/EstimatedEarthFrameProvider.java index 877486c478..7948279578 100644 --- a/src/main/java/org/orekit/estimation/measurements/EstimatedEarthFrameProvider.java +++ b/src/main/java/org/orekit/estimation/measurements/EstimatedEarthFrameProvider.java @@ -279,7 +279,7 @@ public > FieldTransform getTransform(final // prime meridian shift parameters final T theta = linearModel(date, primeMeridianOffsetDriver, primeMeridianDriftDriver); - final T thetaDot = zero.add(primeMeridianDriftDriver.getValue()); + final T thetaDot = zero.newInstance(primeMeridianDriftDriver.getValue()); // pole shift parameters final T xpNeg = linearModel(date, polarOffsetXDriver, polarDriftXDriver).negate(); diff --git a/src/main/java/org/orekit/estimation/measurements/EstimatedMeasurementBase.java b/src/main/java/org/orekit/estimation/measurements/EstimatedMeasurementBase.java index 1e9d65314c..6ad97d7086 100644 --- a/src/main/java/org/orekit/estimation/measurements/EstimatedMeasurementBase.java +++ b/src/main/java/org/orekit/estimation/measurements/EstimatedMeasurementBase.java @@ -20,6 +20,8 @@ import org.orekit.time.AbsoluteDate; import org.orekit.utils.TimeStampedPVCoordinates; +import java.util.IdentityHashMap; + /** Class holding an estimated theoretical value associated to an {@link ObservedMeasurement observed measurement}. * @param the type of the measurement * @author Luc Maisonobe @@ -42,9 +44,19 @@ public class EstimatedMeasurementBase> implemen /** Coordinates of the participants in signal travel order. */ private final TimeStampedPVCoordinates[] participants; + /** Original estimated value prior to any modification. + * @since 12.1 + */ + private double[] originalEstimatedValue; + /** Estimated value. */ private double[] estimatedValue; + /** Applied modifiers effects. + * @since 12.1 + */ + private final IdentityHashMap, double[]> appliedEffects; + /** Measurement status. */ private Status status; @@ -57,15 +69,16 @@ public class EstimatedMeasurementBase> implemen * in inertial frame */ public EstimatedMeasurementBase(final T observedMeasurement, - final int iteration, final int count, - final SpacecraftState[] states, - final TimeStampedPVCoordinates[] participants) { + final int iteration, final int count, + final SpacecraftState[] states, + final TimeStampedPVCoordinates[] participants) { this.observedMeasurement = observedMeasurement; this.iteration = iteration; this.count = count; this.states = states.clone(); this.participants = participants.clone(); this.status = Status.PROCESSED; + this.appliedEffects = new IdentityHashMap<>(); } /** Get the associated observed measurement. @@ -130,6 +143,25 @@ public double[] getObservedValue() { return observedMeasurement.getObservedValue(); } + /** Get the original estimated value prior to any modification. + * @return original estimated value prior to any modification + * @since 12.1 + */ + public double[] getOriginalEstimatedValue() { + return originalEstimatedValue.clone(); + } + + /** Get the applied effects of modifiers. + *

+ * The effects have already accounted for in {@link #getEstimatedValue()} + *

+ * @return applied modifier effects + * @since 12.1 + */ + public IdentityHashMap, double[]> getAppliedEffects() { + return appliedEffects; + } + /** Get the estimated value. * @return estimated value */ @@ -139,11 +171,39 @@ public double[] getEstimatedValue() { /** Set the estimated value. * @param estimatedValue estimated value + * @see #modifyEstimatedValue(EstimationModifier, double...) */ public void setEstimatedValue(final double... estimatedValue) { + if (originalEstimatedValue == null) { + this.originalEstimatedValue = estimatedValue.clone(); + } this.estimatedValue = estimatedValue.clone(); } + /** Modify the estimated value. + * @param modifier modifier that generates this estimated value + * @param newEstimatedValue new estimated value + * @since 12.1 + */ + public void modifyEstimatedValue(final EstimationModifier modifier, final double... newEstimatedValue) { + + if (modifier == null) { + setEstimatedValue(newEstimatedValue); + } else { + final double[] effect = new double[newEstimatedValue.length]; + for (int i = 0; i < effect.length; ++i) { + // compute effect + effect[i] = newEstimatedValue[i] - estimatedValue[i]; + // update value + estimatedValue[i] = newEstimatedValue[i]; + } + + // store effect + appliedEffects.put(modifier, effect); + } + + } + /** Get the status. *

* The status is set to {@link Status#PROCESSED PROCESSED} at construction, and @@ -171,7 +231,7 @@ public enum Status { PROCESSED, /** Status for rejected measurements. */ - REJECTED; + REJECTED } diff --git a/src/main/java/org/orekit/estimation/measurements/FDOA.java b/src/main/java/org/orekit/estimation/measurements/FDOA.java index 2729755b6a..751d93fa00 100644 --- a/src/main/java/org/orekit/estimation/measurements/FDOA.java +++ b/src/main/java/org/orekit/estimation/measurements/FDOA.java @@ -243,12 +243,11 @@ protected EstimatedMeasurement theoreticalEvaluation(final int iteration, final Gradient fdoa = rangeRateDifference.multiply(rangeRateToHz); estimated.setEstimatedValue(fdoa.getValue()); - // Range partial derivatives with respect to state + // Range first order derivatives with respect to state final double[] derivatives = fdoa.getGradient(); estimated.setStateDerivatives(0, Arrays.copyOfRange(derivatives, 0, 6)); - // set partial derivatives with respect to parameters - // (beware element at index 0 is the value, not a derivative) + // set first order derivatives with respect to parameters for (final ParameterDriver driver : getParametersDrivers()) { for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { final Integer index = common.getIndices().get(span.getData()); diff --git a/src/main/java/org/orekit/estimation/measurements/GroundReceiverCommonParametersWithDerivatives.java b/src/main/java/org/orekit/estimation/measurements/GroundReceiverCommonParametersWithDerivatives.java index 6f4eac4c41..84d9f84773 100644 --- a/src/main/java/org/orekit/estimation/measurements/GroundReceiverCommonParametersWithDerivatives.java +++ b/src/main/java/org/orekit/estimation/measurements/GroundReceiverCommonParametersWithDerivatives.java @@ -27,13 +27,7 @@ * @author Luc Maisonobe * @since 12.0 */ -public class GroundReceiverCommonParametersWithDerivatives { - - /** Spacecraft state. */ - private final SpacecraftState state; - - /** Derivatives indices map. */ - private final Map indices; +public class GroundReceiverCommonParametersWithDerivatives extends CommonParametersWithDerivatives { /** Transform between station and inertial frame. */ private final FieldTransform offsetToInertialDownlink; @@ -41,15 +35,6 @@ public class GroundReceiverCommonParametersWithDerivatives { /** Station position in inertial frame at end of the downlink leg. */ private final TimeStampedFieldPVCoordinates stationDownlink; - /** Downlink delay. */ - private final Gradient tauD; - - /** Transit state. */ - private final SpacecraftState transitState; - - /** Transit state. */ - private final TimeStampedFieldPVCoordinates transitPV; - /** Simple constructor. * @param state spacecraft state * @param indices derivatives indices map @@ -66,27 +51,9 @@ public GroundReceiverCommonParametersWithDerivatives(final SpacecraftState state final Gradient tauD, final SpacecraftState transitState, final TimeStampedFieldPVCoordinates transitPV) { - this.state = state; - this.indices = indices; + super(state, indices, tauD, transitState, transitPV); this.offsetToInertialDownlink = offsetToInertialDownlink; this.stationDownlink = stationDownlink; - this.tauD = tauD; - this.transitState = transitState; - this.transitPV = transitPV; - } - - /** Get spacecraft state. - * @return spacecraft state - */ - public SpacecraftState getState() { - return state; - } - - /** Get derivatives indices map. - * @return derivatives indices map - */ - public Map getIndices() { - return indices; } /** Get transform between station and inertial frame. @@ -103,25 +70,4 @@ public TimeStampedFieldPVCoordinates getStationDownlink() { return stationDownlink; } - /** Get downlink delay. - * @return ownlink delay - */ - public Gradient getTauD() { - return tauD; - } - - /** Get transit state. - * @return transit state - */ - public SpacecraftState getTransitState() { - return transitState; - } - - /** Get transit position/velocity. - * @return transit position/velocity - */ - public TimeStampedFieldPVCoordinates getTransitPV() { - return transitPV; - } - } diff --git a/src/main/java/org/orekit/estimation/measurements/GroundReceiverCommonParametersWithoutDerivatives.java b/src/main/java/org/orekit/estimation/measurements/GroundReceiverCommonParametersWithoutDerivatives.java index db5a612df8..0ea224e21d 100644 --- a/src/main/java/org/orekit/estimation/measurements/GroundReceiverCommonParametersWithoutDerivatives.java +++ b/src/main/java/org/orekit/estimation/measurements/GroundReceiverCommonParametersWithoutDerivatives.java @@ -24,10 +24,7 @@ * @author Luc Maisonobe * @since 12.0 */ -public class GroundReceiverCommonParametersWithoutDerivatives { - - /** Spacecraft state. */ - private final SpacecraftState state; +public class GroundReceiverCommonParametersWithoutDerivatives extends CommonParametersWithoutDerivatives { /** Transform between station and inertial frame. */ private final Transform offsetToInertialDownlink; @@ -35,15 +32,6 @@ public class GroundReceiverCommonParametersWithoutDerivatives { /** Station position in inertial frame at end of the downlink leg. */ private final TimeStampedPVCoordinates stationDownlink; - /** Downlink delay. */ - private final double tauD; - - /** Transit state. */ - private final SpacecraftState transitState; - - /** Transit position/velocity. */ - private final TimeStampedPVCoordinates transitPV; - /** Simple constructor. * @param state spacecraft state * @param offsetToInertialDownlink transform between station and inertial frame @@ -58,19 +46,9 @@ public GroundReceiverCommonParametersWithoutDerivatives(final SpacecraftState st final double tauD, final SpacecraftState transitState, final TimeStampedPVCoordinates transitPV) { - this.state = state; + super(state, tauD, transitState, transitPV); this.offsetToInertialDownlink = offsetToInertialDownlink; this.stationDownlink = stationDownlink; - this.tauD = tauD; - this.transitState = transitState; - this.transitPV = transitPV; - } - - /** Get spacecraft state. - * @return spacecraft state - */ - public SpacecraftState getState() { - return state; } /** Get transform between station and inertial frame. @@ -87,25 +65,4 @@ public TimeStampedPVCoordinates getStationDownlink() { return stationDownlink; } - /** Get downlink delay. - * @return ownlink delay - */ - public double getTauD() { - return tauD; - } - - /** Get transit state. - * @return transit state - */ - public SpacecraftState getTransitState() { - return transitState; - } - - /** Get transit position/velocity. - * @return transit position/velocity - */ - public TimeStampedPVCoordinates getTransitPV() { - return transitPV; - } - } diff --git a/src/main/java/org/orekit/estimation/measurements/GroundReceiverMeasurement.java b/src/main/java/org/orekit/estimation/measurements/GroundReceiverMeasurement.java index 5eea446ff4..ba97f2c04c 100644 --- a/src/main/java/org/orekit/estimation/measurements/GroundReceiverMeasurement.java +++ b/src/main/java/org/orekit/estimation/measurements/GroundReceiverMeasurement.java @@ -66,6 +66,7 @@ public GroundReceiverMeasurement(final GroundStation station, final boolean twoW super(date, observed, sigma, baseWeight, Collections.singletonList(satellite)); addParameterDriver(station.getClockOffsetDriver()); addParameterDriver(station.getClockDriftDriver()); + addParameterDriver(station.getClockAccelerationDriver()); addParameterDriver(station.getEastOffsetDriver()); addParameterDriver(station.getNorthOffsetDriver()); addParameterDriver(station.getZenithOffsetDriver()); @@ -79,6 +80,7 @@ public GroundReceiverMeasurement(final GroundStation station, final boolean twoW // for one way measurements, the satellite clock offset affects the measurement addParameterDriver(satellite.getClockOffsetDriver()); addParameterDriver(satellite.getClockDriftDriver()); + addParameterDriver(satellite.getClockAccelerationDriver()); } this.station = station; this.twoway = twoWay; @@ -99,6 +101,7 @@ public GroundReceiverMeasurement(final GroundStation station, final boolean twoW super(date, observed, sigma, baseWeight, Collections.singletonList(satellite)); addParameterDriver(station.getClockOffsetDriver()); addParameterDriver(station.getClockDriftDriver()); + addParameterDriver(station.getClockAccelerationDriver()); addParameterDriver(station.getEastOffsetDriver()); addParameterDriver(station.getNorthOffsetDriver()); addParameterDriver(station.getZenithOffsetDriver()); @@ -112,6 +115,7 @@ public GroundReceiverMeasurement(final GroundStation station, final boolean twoW // for one way measurements, the satellite clock offset affects the measurement addParameterDriver(satellite.getClockOffsetDriver()); addParameterDriver(satellite.getClockDriftDriver()); + addParameterDriver(satellite.getClockAccelerationDriver()); } this.station = station; this.twoway = twoWay; @@ -155,7 +159,7 @@ protected GroundReceiverCommonParametersWithoutDerivatives computeCommonParamete // we will have delta == tauD and transitState will be the same as state) // Downlink delay - final double tauD = signalTimeOfFlight(pva, stationDownlink.getPosition(), downlinkDate); + final double tauD = signalTimeOfFlight(pva, stationDownlink.getPosition(), downlinkDate, state.getFrame()); // Transit state & Transit state (re)computed with gradients final double delta = downlinkDate.durationFrom(state.getDate()); @@ -206,7 +210,8 @@ protected GroundReceiverCommonParametersWithDerivatives computeCommonParametersW // we will have delta == tauD and transitState will be the same as state) // Downlink delay - final Gradient tauD = signalTimeOfFlight(pva, stationDownlink.getPosition(), downlinkDate); + final Gradient tauD = signalTimeOfFlight(pva, stationDownlink.getPosition(), + downlinkDate, state.getFrame()); // Transit state & Transit state (re)computed with gradients final Gradient delta = downlinkDate.durationFrom(state.getDate()); diff --git a/src/main/java/org/orekit/estimation/measurements/GroundStation.java b/src/main/java/org/orekit/estimation/measurements/GroundStation.java index de16cebc02..330ab5ae27 100644 --- a/src/main/java/org/orekit/estimation/measurements/GroundStation.java +++ b/src/main/java/org/orekit/estimation/measurements/GroundStation.java @@ -18,6 +18,7 @@ import java.util.Map; +import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; import org.hipparchus.analysis.differentiation.Gradient; import org.hipparchus.geometry.euclidean.threed.FieldRotation; @@ -41,6 +42,10 @@ import org.orekit.frames.TopocentricFrame; import org.orekit.frames.Transform; import org.orekit.models.earth.displacement.StationDisplacement; +import org.orekit.models.earth.troposphere.TroposphericModelUtils; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; +import org.orekit.models.earth.weather.PressureTemperatureHumidity; +import org.orekit.models.earth.weather.PressureTemperatureHumidityProvider; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; import org.orekit.time.UT1Scale; @@ -92,6 +97,11 @@ public class GroundStation { /** Suffix for ground clock drift parameters name. */ public static final String DRIFT_SUFFIX = "-drift-clock"; + /** Suffix for ground clock drift parameters name. + * @since 12.1 + */ + public static final String ACCELERATION_SUFFIX = "-acceleration-clock"; + /** Suffix for ground station intermediate frame name. */ public static final String INTERMEDIATE_SUFFIX = "-intermediate"; @@ -114,6 +124,11 @@ public class GroundStation { /** Provider for Earth frame whose EOP parameters can be estimated. */ private final EstimatedEarthFrameProvider estimatedEarthFrameProvider; + /** Provider for weather parameters. + * @since 12.1 + */ + private final PressureTemperatureHumidityProvider pthProvider; + /** Earth frame whose EOP parameters can be estimated. */ private final Frame estimatedEarthFrame; @@ -132,6 +147,11 @@ public class GroundStation { /** Driver for clock drift. */ private final ParameterDriver clockDriftDriver; + /** Driver for clock acceleration. + * @since 12.1 + */ + private final ParameterDriver clockAccelerationDriver; + /** Driver for position offset along the East axis. */ private final ParameterDriver eastOffsetDriver; @@ -142,6 +162,10 @@ public class GroundStation { private final ParameterDriver zenithOffsetDriver; /** Build a ground station ignoring {@link StationDisplacement station displacements}. + *

+ * Calls {@link #GroundStation(TopocentricFrame, PressureTemperatureHumidityProvider)} + * with {@link TroposphericModelUtils#STANDARD_ATMOSPHERE_PROVIDER} as the provider. + *

*

* The initial values for the pole and prime meridian parametric linear models * ({@link #getPrimeMeridianOffsetDriver()}, {@link #getPrimeMeridianDriftDriver()}, @@ -159,7 +183,31 @@ public class GroundStation { * @see #GroundStation(TopocentricFrame, EOPHistory, StationDisplacement...) */ public GroundStation(final TopocentricFrame baseFrame) { - this(baseFrame, FramesFactory.findEOP(baseFrame), new StationDisplacement[0]); + this(baseFrame, TroposphericModelUtils.STANDARD_ATMOSPHERE_PROVIDER, + FramesFactory.findEOP(baseFrame), new StationDisplacement[0]); + } + + /** Build a ground station ignoring {@link StationDisplacement station displacements}. + *

+ * The initial values for the pole and prime meridian parametric linear models + * ({@link #getPrimeMeridianOffsetDriver()}, {@link #getPrimeMeridianDriftDriver()}, + * {@link #getPolarOffsetXDriver()}, {@link #getPolarDriftXDriver()}, + * {@link #getPolarOffsetXDriver()}, {@link #getPolarDriftXDriver()}) are set to 0. + * The initial values for the station offset model ({@link #getClockOffsetDriver()}, + * {@link #getEastOffsetDriver()}, {@link #getNorthOffsetDriver()}, + * {@link #getZenithOffsetDriver()}) are set to 0. + * This implies that as long as these values are not changed, the offset frame is + * the same as the {@link #getBaseFrame() base frame}. As soon as some of these models + * are changed, the offset frame moves away from the {@link #getBaseFrame() base frame}. + *

+ * @param baseFrame base frame associated with the station, without *any* parametric + * model (no station offset, no polar motion, no meridian shift) + * @param pthProvider provider for weather parameters + * @see #GroundStation(TopocentricFrame, EOPHistory, StationDisplacement...) + * @since 12.1 + */ + public GroundStation(final TopocentricFrame baseFrame, final PressureTemperatureHumidityProvider pthProvider) { + this(baseFrame, pthProvider, FramesFactory.findEOP(baseFrame), new StationDisplacement[0]); } /** Simple constructor. @@ -181,11 +229,43 @@ public GroundStation(final TopocentricFrame baseFrame) { * @param displacements ground station displacement model (tides, ocean loading, * atmospheric loading, thermal effects...) * @since 9.1 + * @deprecated as of 12.1, replaced by {@link #GroundStation(TopocentricFrame, + * PressureTemperatureHumidityProvider, EOPHistory, StationDisplacement...)} */ + @Deprecated public GroundStation(final TopocentricFrame baseFrame, final EOPHistory eopHistory, final StationDisplacement... displacements) { + this(baseFrame, TroposphericModelUtils.STANDARD_ATMOSPHERE_PROVIDER, eopHistory, displacements); + } - this.baseFrame = baseFrame; + /** Simple constructor. + *

+ * The initial values for the pole and prime meridian parametric linear models + * ({@link #getPrimeMeridianOffsetDriver()}, {@link #getPrimeMeridianDriftDriver()}, + * {@link #getPolarOffsetXDriver()}, {@link #getPolarDriftXDriver()}, + * {@link #getPolarOffsetXDriver()}, {@link #getPolarDriftXDriver()}) are set to 0. + * The initial values for the station offset model ({@link #getClockOffsetDriver()}, + * {@link #getEastOffsetDriver()}, {@link #getNorthOffsetDriver()}, + * {@link #getZenithOffsetDriver()}, {@link #getClockOffsetDriver()}) are set to 0. + * This implies that as long as these values are not changed, the offset frame is + * the same as the {@link #getBaseFrame() base frame}. As soon as some of these models + * are changed, the offset frame moves away from the {@link #getBaseFrame() base frame}. + *

+ * @param baseFrame base frame associated with the station, without *any* parametric + * model (no station offset, no polar motion, no meridian shift) + * @param pthProvider provider for weather parameters + * @param eopHistory EOP history associated with Earth frames + * @param displacements ground station displacement model (tides, ocean loading, + * atmospheric loading, thermal effects...) + * @since 12.1 + */ + public GroundStation(final TopocentricFrame baseFrame, + final PressureTemperatureHumidityProvider pthProvider, + final EOPHistory eopHistory, + final StationDisplacement... displacements) { + + this.baseFrame = baseFrame; + this.pthProvider = pthProvider; if (eopHistory == null) { throw new OrekitException(OrekitMessages.NO_EARTH_ORIENTATION_PARAMETERS); @@ -215,6 +295,10 @@ public GroundStation(final TopocentricFrame baseFrame, final EOPHistory eopHisto 0.0, CLOCK_OFFSET_SCALE, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); + this.clockAccelerationDriver = new ParameterDriver(baseFrame.getName() + ACCELERATION_SUFFIX, + 0.0, CLOCK_OFFSET_SCALE, + Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); + this.eastOffsetDriver = new ParameterDriver(baseFrame.getName() + OFFSET_SUFFIX + "-East", 0.0, POSITION_OFFSET_SCALE, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); @@ -229,6 +313,25 @@ public GroundStation(final TopocentricFrame baseFrame, final EOPHistory eopHisto } + /** Get the weather parameters. + * @param date date at which weather parameters are requested + * @return weather parameters + * @since 12.1 + */ + public PressureTemperatureHumidity getPressureTemperatureHumidity(final AbsoluteDate date) { + return pthProvider.getWeatherParamerers(getOffsetGeodeticPoint(date), date); + } + + /** Get the weather parameters. + * @param type of the field elements + * @param date date at which weather parameters are requested + * @return weather parameters + * @since 12.1 + */ + public > FieldPressureTemperatureHumidity getPressureTemperatureHumidity(final FieldAbsoluteDate date) { + return pthProvider.getWeatherParamerers(getOffsetGeodeticPoint(date), date); + } + /** Get the displacement models. * @return displacement models (empty if no model has been set up) * @since 9.1 @@ -253,6 +356,14 @@ public ParameterDriver getClockDriftDriver() { return clockDriftDriver; } + /** Get a driver allowing to change station clock acceleration (which is related to measurement date). + * @return driver for station clock acceleration + * @since 12.1 + */ + public ParameterDriver getClockAccelerationDriver() { + return clockAccelerationDriver; + } + /** Get a driver allowing to change station position along East axis. * @return driver for station position offset along East axis */ @@ -424,6 +535,28 @@ public GeodeticPoint getOffsetGeodeticPoint(final AbsoluteDate date) { } + /** Get the geodetic point at the center of the offset frame. + * @param type of the field elements + * @param date current date(must be non-null, which is a more stringent condition + * * than in {@link #getOffsetGeodeticPoint(AbsoluteDate)} + * @return geodetic point at the center of the offset frame + * @since 12.1 + */ + public > FieldGeodeticPoint getOffsetGeodeticPoint(final FieldAbsoluteDate date) { + + // take station offset into account + final double x = eastOffsetDriver.getValue(); + final double y = northOffsetDriver.getValue(); + final double z = zenithOffsetDriver.getValue(); + final BodyShape baseShape = baseFrame.getParentShape(); + final FieldStaticTransform baseToBody = baseFrame.getStaticTransformTo(baseShape.getBodyFrame(), date); + FieldVector3D origin = baseToBody.transformPosition(new Vector3D(x, y, z)); + origin = origin.add(computeDisplacement(date.toAbsoluteDate(), origin.toVector3D())); + + return baseShape.transform(origin, baseShape.getBodyFrame(), date); + + } + /** Get the transform between offset frame and inertial frame. *

* The offset frame takes the current position offset, diff --git a/src/main/java/org/orekit/estimation/measurements/InterSatellitesRange.java b/src/main/java/org/orekit/estimation/measurements/InterSatellitesRange.java index 203e46aa9e..a11c86792e 100644 --- a/src/main/java/org/orekit/estimation/measurements/InterSatellitesRange.java +++ b/src/main/java/org/orekit/estimation/measurements/InterSatellitesRange.java @@ -53,9 +53,9 @@ * offset is subtracted *

  • as range is evaluated using the total signal time of flight, for one-way * measurements the observed range is the real physical signal time of flight to - * which (Δtl - Δtr) ⨉ c is added, where Δtl (resp. Δtr) is the clock offset for the + * which (Δtl - Δtr) ⨯ c is added, where Δtl (resp. Δtr) is the clock offset for the * local satellite (resp. remote satellite). A similar effect exists in - * two-way measurements but it is computed as (Δtl - Δtl) ⨉ c / 2 as the local satellite + * two-way measurements but it is computed as (Δtl - Δtl) ⨯ c / 2 as the local satellite * clock is used for both initial emission and final reception and therefore it evaluates * to zero.
  • * @@ -94,9 +94,13 @@ public InterSatellitesRange(final ObservableSatellite local, super(date, range, sigma, baseWeight, Arrays.asList(local, remote)); // for one way and two ways measurements, the local satellite clock offsets affects the measurement addParameterDriver(local.getClockOffsetDriver()); + addParameterDriver(local.getClockDriftDriver()); + addParameterDriver(local.getClockAccelerationDriver()); if (!twoWay) { // for one way measurements, the remote satellite clock offsets also affects the measurement addParameterDriver(remote.getClockOffsetDriver()); + addParameterDriver(remote.getClockDriftDriver()); + addParameterDriver(remote.getClockAccelerationDriver()); } this.twoway = twoWay; } @@ -130,7 +134,7 @@ protected EstimatedMeasurementBase theoreticalEvaluationWi final TimeStampedPVCoordinates s1Downlink = pvaL.shiftedBy(arrivalDate.durationFrom(pvaL.getDate())); - final double tauD = signalTimeOfFlight(pvaR, s1Downlink.getPosition(), arrivalDate); + final double tauD = signalTimeOfFlight(pvaR, s1Downlink.getPosition(), arrivalDate, local.getFrame()); // Transit state final double delta = getDate().durationFrom(remote.getDate()); @@ -147,7 +151,8 @@ protected EstimatedMeasurementBase theoreticalEvaluationWi // uplink delay final double tauU = signalTimeOfFlight(pvaL, transitState.getPosition(), - transitState.getDate()); + transitState.getDate(), + local.getFrame()); estimated = new EstimatedMeasurementBase<>(this, iteration, evaluation, new SpacecraftState[] { local.shiftedBy(deltaMTauD), @@ -191,7 +196,7 @@ protected EstimatedMeasurement theoreticalEvaluation(final final int evaluation, final SpacecraftState[] states) { - // Range derivatives are computed with respect to spacecrafts states in inertial frame + // Range derivatives are computed with respect to spacecraft states in inertial frame // ---------------------- // // Parameters: @@ -230,7 +235,8 @@ protected EstimatedMeasurement theoreticalEvaluation(final final TimeStampedFieldPVCoordinates s1Downlink = pvaL.shiftedBy(arrivalDate.durationFrom(pvaL.getDate())); - final Gradient tauD = signalTimeOfFlight(pvaR, s1Downlink.getPosition(), arrivalDate); + final Gradient tauD = signalTimeOfFlight(pvaR, s1Downlink.getPosition(), + arrivalDate, local.getFrame()); // Transit state final double delta = getDate().durationFrom(remote.getDate()); @@ -247,7 +253,8 @@ protected EstimatedMeasurement theoreticalEvaluation(final // uplink delay final Gradient tauU = signalTimeOfFlight(pvaL, transitStateDS.getPosition(), - transitStateDS.getDate()); + transitStateDS.getDate(), + local.getFrame()); estimated = new EstimatedMeasurement<>(this, iteration, evaluation, new SpacecraftState[] { local.shiftedBy(deltaMTauD.getValue()), @@ -281,12 +288,12 @@ protected EstimatedMeasurement theoreticalEvaluation(final } estimated.setEstimatedValue(range.getValue()); - // Range partial derivatives with respect to states + // Range first order derivatives with respect to states final double[] derivatives = range.getGradient(); estimated.setStateDerivatives(0, Arrays.copyOfRange(derivatives, 0, 6)); estimated.setStateDerivatives(1, Arrays.copyOfRange(derivatives, 6, 12)); - // Set partial derivatives with respect to parameters + // Set first order derivatives with respect to parameters for (final ParameterDriver driver : getParametersDrivers()) { for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { final Integer index = indices.get(span.getData()); diff --git a/src/main/java/org/orekit/estimation/measurements/ObservableSatellite.java b/src/main/java/org/orekit/estimation/measurements/ObservableSatellite.java index 41ef013ba4..4436ce0067 100644 --- a/src/main/java/org/orekit/estimation/measurements/ObservableSatellite.java +++ b/src/main/java/org/orekit/estimation/measurements/ObservableSatellite.java @@ -32,6 +32,11 @@ public class ObservableSatellite { /** Prefix for clock drift parameter driver, the propagator index will be appended to it. */ public static final String CLOCK_DRIFT_PREFIX = "clock-drift-satellite-"; + /** Prefix for clock acceleration parameter driver, the propagator index will be appended to it. + * @since 12.1 + */ + public static final String CLOCK_ACCELERATION_PREFIX = "clock-acceleration-satellite-"; + /** Clock offset scaling factor. *

    * We use a power of 2 to avoid numeric noise introduction @@ -40,6 +45,9 @@ public class ObservableSatellite { */ private static final double CLOCK_OFFSET_SCALE = FastMath.scalb(1.0, -10); + /** Prefix for satellite names. */ + private static final String SAT_PREFIX = "sat-"; + /** Index of the propagator related to this satellite. */ private final int propagatorIndex; @@ -49,6 +57,11 @@ public class ObservableSatellite { /** Parameter driver for satellite clock drift. */ private final ParameterDriver clockDriftDriver; + /** Parameter driver for satellite clock acceleration. + * @since 12.1 + */ + private final ParameterDriver clockAccelerationDriver; + /** Simple constructor. * @param propagatorIndex index of the propagator related to this satellite */ @@ -60,6 +73,22 @@ public ObservableSatellite(final int propagatorIndex) { this.clockDriftDriver = new ParameterDriver(CLOCK_DRIFT_PREFIX + propagatorIndex, 0.0, CLOCK_OFFSET_SCALE, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); + this.clockAccelerationDriver = new ParameterDriver(CLOCK_ACCELERATION_PREFIX + propagatorIndex, + 0.0, CLOCK_OFFSET_SCALE, + Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); + } + + /** Build a name for the satellite. + *

    + * This is mainly useful to build the arguments for {@link + * org.orekit.estimation.measurements.gnss.AmbiguityCache#getAmbiguity(String, + * String, double)} + *

    + * @return name for the satellite (built from the propagator index) + * @since 12.1 + */ + public String getName() { + return SAT_PREFIX + propagatorIndex; } /** Get the index of the propagator related to this satellite. @@ -85,13 +114,31 @@ public ParameterDriver getClockOffsetDriver() { *

    * The drift is negative if the satellite clock is slowing down and positive if it is speeding up. *

    - * @return clock offset parameter driver + * @return clock drift parameter driver * @since 10.3 */ public ParameterDriver getClockDriftDriver() { return clockDriftDriver; } + /** Get the clock acceleration parameter driver. + * @return clock acceleration parameter driver + * @since 12.1 + */ + public ParameterDriver getClockAccelerationDriver() { + return clockAccelerationDriver; + } + + /** Get a quadratic clock model valid at some date. + * @return quadratic clock model + * @since 12.1 + */ + public QuadraticClockModel getQuadraticClockModel() { + return new QuadraticClockModel(clockOffsetDriver, + clockDriftDriver, + clockAccelerationDriver); + } + /** {@inheritDoc} * @since 12.0 */ diff --git a/src/main/java/org/orekit/estimation/measurements/ObservedMeasurement.java b/src/main/java/org/orekit/estimation/measurements/ObservedMeasurement.java index b885f5a30b..87e739a58f 100644 --- a/src/main/java/org/orekit/estimation/measurements/ObservedMeasurement.java +++ b/src/main/java/org/orekit/estimation/measurements/ObservedMeasurement.java @@ -120,6 +120,19 @@ public interface ObservedMeasurement> extends C List getSatellites(); /** Estimate the theoretical value of the measurement, without derivatives. + *

    + * The estimated value is the combination of the raw estimated + * value and all the modifiers that apply to the measurement. + *

    + * @param states orbital states corresponding to {@link #getSatellites()} at measurement date + * @return estimated measurement + * @since 12.1 + */ + default EstimatedMeasurementBase estimateWithoutDerivatives(SpacecraftState[] states) { + return estimateWithoutDerivatives(0, 0, states); + } + + /** Estimate the theoretical value of the measurement, without derivatives. For use in orbit determination. *

    * The estimated value is the combination of the raw estimated * value and all the modifiers that apply to the measurement. diff --git a/src/main/java/org/orekit/estimation/measurements/QuadraticClockModel.java b/src/main/java/org/orekit/estimation/measurements/QuadraticClockModel.java new file mode 100644 index 0000000000..5d6f293152 --- /dev/null +++ b/src/main/java/org/orekit/estimation/measurements/QuadraticClockModel.java @@ -0,0 +1,174 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.estimation.measurements; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.analysis.differentiation.Gradient; +import org.hipparchus.util.FastMath; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.ClockModel; +import org.orekit.time.ClockOffset; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.FieldClockOffset; +import org.orekit.utils.ParameterDriver; + +import java.util.Map; + +/** Quadratic clock model. + * + * @author Luc Maisonobe + * @since 12.1 + * + */ +public class QuadraticClockModel implements ClockModel { + + /** Clock offset scaling factor. + *

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

    + */ + private static final double CLOCK_OFFSET_SCALE = FastMath.scalb(1.0, -10); + + /** Constant term. */ + private final ParameterDriver a0; + + /** Linear term. */ + private final ParameterDriver a1; + + /** Quadratic term. */ + private final ParameterDriver a2; + + /** Simple constructor. + * @param referenceDate reference date + * @param a0 constant term + * @param a1 linear term + * @param a2 quadratic term + */ + public QuadraticClockModel(final AbsoluteDate referenceDate, + final double a0, final double a1, final double a2) { + this(new ParameterDriver("a0", + 0.0, CLOCK_OFFSET_SCALE, + Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY), + new ParameterDriver("a1", + 0.0, CLOCK_OFFSET_SCALE, + Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY), + new ParameterDriver("a2", + 0.0, CLOCK_OFFSET_SCALE, + Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY)); + this.a0.setValue(a0); + this.a0.setReferenceDate(referenceDate); + this.a1.setValue(a1); + this.a1.setReferenceDate(referenceDate); + this.a2.setValue(a2); + this.a2.setReferenceDate(referenceDate); + } + + /** Simple constructor. + * @param a0 constant term + * @param a1 linear term + * @param a2 quadratic term + */ + public QuadraticClockModel(final ParameterDriver a0, final ParameterDriver a1, final ParameterDriver a2) { + this.a0 = a0; + this.a1 = a1; + this.a2 = a2; + } + + /** {@inheritDoc} */ + @Override + public AbsoluteDate getValidityStart() { + return AbsoluteDate.PAST_INFINITY; + } + + /** {@inheritDoc} */ + @Override + public AbsoluteDate getValidityEnd() { + return AbsoluteDate.FUTURE_INFINITY; + } + + /** {@inheritDoc} */ + @Override + public ClockOffset getOffset(final AbsoluteDate date) { + final double dt = date.durationFrom(getSafeReference(date)); + final double c0 = a0.getValue(date); + final double c1 = a1.getValue(date); + final double c2 = a2.getValue(date); + return new ClockOffset(date, + (c2 * dt + c1) * dt + c0, + 2 * c2 * dt + c1, + 2 * c2); + } + + /** {@inheritDoc} */ + @Override + public > FieldClockOffset getOffset(final FieldAbsoluteDate date) { + final AbsoluteDate aDate = date.toAbsoluteDate(); + final T dt = date.durationFrom(getSafeReference(aDate)); + final double c0 = a0.getValue(aDate); + final double c1 = a1.getValue(aDate); + final double c2 = a2.getValue(aDate); + return new FieldClockOffset<>(date, + dt.multiply(dt.multiply(c2).add(c1)).add(c0), + dt.multiply(2 * c2).add(c1), + dt.newInstance(2 * c2)); + } + + /** Get a safe reference date. + *

    + * This method deals with parameters drivers for which no reference + * date has been set, which is acceptable if the model is not + * time-dependent. + *

    + * @param date date at which values are requested + * @return safe reference date + */ + private AbsoluteDate getSafeReference(final AbsoluteDate date) { + if (a0.getReferenceDate() == null) { + if (a1.getValue(date) == 0 && a2.getValue(date) == 0) { + // it is OK to not have a reference date is clock offset is constant + return date; + } else { + throw new OrekitException(OrekitMessages.NO_REFERENCE_DATE_FOR_PARAMETER, + a0.getName()); + } + } else { + return a0.getReferenceDate(); + } + } + + /** Convert to gradient model. + * @param freeParameters total number of free parameters in the gradient + * @param indices indices of the differentiation parameters in derivatives computations, + * must be span name and not driver name + * @param date date at which model must be valid + * @return converted clock model + */ + public QuadraticFieldClockModel toGradientModel(final int freeParameters, + final Map indices, + final AbsoluteDate date) { + final Gradient g0 = a0.getValue(freeParameters, indices, date); + final Gradient g1 = a1.getValue(freeParameters, indices, date); + final Gradient g2 = a2.getValue(freeParameters, indices, date); + final FieldAbsoluteDate referenceDate = + new FieldAbsoluteDate<>(g0.getField(), getSafeReference(date)); + return new QuadraticFieldClockModel<>(referenceDate, g0, g1, g2); + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/QuadraticFieldClockModel.java b/src/main/java/org/orekit/estimation/measurements/QuadraticFieldClockModel.java new file mode 100644 index 0000000000..3b867c2faa --- /dev/null +++ b/src/main/java/org/orekit/estimation/measurements/QuadraticFieldClockModel.java @@ -0,0 +1,70 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.estimation.measurements; + +import org.hipparchus.CalculusFieldElement; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.FieldClockOffset; + +/** Quadratic clock model. + * + * @param type of the field elements + * @author Luc Maisonobe + * @since 12.1 + * + */ +public class QuadraticFieldClockModel> { + + /** Clock model reference date. */ + private final FieldAbsoluteDate referenceDate; + + /** Constant term. */ + private final T a0; + + /** Linear term. */ + private final T a1; + + /** Quadratic term. */ + private final T a2; + + /** Simple constructor. + * @param referenceDate reference date + * @param a0 constant term + * @param a1 linear term + * @param a2 quadratic term + */ + public QuadraticFieldClockModel(final FieldAbsoluteDate referenceDate, + final T a0, final T a1, final T a2) { + this.referenceDate = referenceDate; + this.a0 = a0; + this.a1 = a1; + this.a2 = a2; + } + + /** Get the clock offset at date. + * @param date date at which offset is requested + * @return clock offset at specified date + */ + public FieldClockOffset getOffset(final FieldAbsoluteDate date) { + final T dt = date.durationFrom(referenceDate); + return new FieldClockOffset<>(date, + a2.multiply(dt).add(a1).multiply(dt).add(a0), + a2.multiply(dt).multiply(2).add(a1), + a2.multiply(2)); + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/Range.java b/src/main/java/org/orekit/estimation/measurements/Range.java index f9f9d5fb0c..5e97105cd7 100644 --- a/src/main/java/org/orekit/estimation/measurements/Range.java +++ b/src/main/java/org/orekit/estimation/measurements/Range.java @@ -57,9 +57,9 @@ * clock offset is subtracted *
  • as range is evaluated using the total signal time of flight, for one-way * measurements the observed range is the real physical signal time of flight to - * which (Δtg - Δts) ⨉ c is added, where Δtg (resp. Δts) is the clock offset for the + * which (Δtg - Δts) ⨯ c is added, where Δtg (resp. Δts) is the clock offset for the * receiving ground station (resp. emitting satellite). A similar effect exists in - * two-way measurements but it is computed as (Δtg - Δtg) ⨉ c / 2 as the same ground + * two-way measurements but it is computed as (Δtg - Δtg) ⨯ c / 2 as the same ground * station clock is used for initial emission and final reception and therefore it evaluates * to zero.
  • * @@ -107,7 +107,8 @@ protected EstimatedMeasurementBase theoreticalEvaluationWithoutDerivative // Station at transit state date (derivatives of tauD taken into account) final TimeStampedPVCoordinates stationAtTransitDate = common.getStationDownlink().shiftedBy(-common.getTauD()); // Uplink delay - final double tauU = signalTimeOfFlight(stationAtTransitDate, transitPV.getPosition(), transitPV.getDate()); + final double tauU = signalTimeOfFlight(stationAtTransitDate, transitPV.getPosition(), + transitPV.getDate(), common.getState().getFrame()); final TimeStampedPVCoordinates stationUplink = common.getStationDownlink().shiftedBy(-common.getTauD() - tauU); // Prepare the evaluation @@ -182,7 +183,8 @@ protected EstimatedMeasurement theoreticalEvaluation(final int iteration, common.getStationDownlink().shiftedBy(common.getTauD().negate()); // Uplink delay final Gradient tauU = - signalTimeOfFlight(stationAtTransitDate, transitPV.getPosition(), transitPV.getDate()); + signalTimeOfFlight(stationAtTransitDate, transitPV.getPosition(), transitPV.getDate(), + state.getFrame()); final TimeStampedFieldPVCoordinates stationUplink = common.getStationDownlink().shiftedBy(-common.getTauD().getValue() - tauU.getValue()); @@ -223,12 +225,11 @@ protected EstimatedMeasurement theoreticalEvaluation(final int iteration, estimated.setEstimatedValue(range.getValue()); - // Range partial derivatives with respect to state + // Range first order derivatives with respect to state final double[] derivatives = range.getGradient(); estimated.setStateDerivatives(0, Arrays.copyOfRange(derivatives, 0, 6)); - // set partial derivatives with respect to parameters - // (beware element at index 0 is the value, not a derivative) + // Set first order derivatives with respect to parameters for (final ParameterDriver driver : getParametersDrivers()) { for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { final Integer index = common.getIndices().get(span.getData()); diff --git a/src/main/java/org/orekit/estimation/measurements/RangeRate.java b/src/main/java/org/orekit/estimation/measurements/RangeRate.java index 7e4d29d065..5d4e85e636 100644 --- a/src/main/java/org/orekit/estimation/measurements/RangeRate.java +++ b/src/main/java/org/orekit/estimation/measurements/RangeRate.java @@ -96,7 +96,8 @@ protected EstimatedMeasurementBase theoreticalEvaluationWithoutDeriva offsetToInertialApproxUplink.transformPVCoordinates(new TimeStampedPVCoordinates(approxUplinkDate, Vector3D.ZERO, Vector3D.ZERO, Vector3D.ZERO)); - final double tauU = signalTimeOfFlight(stationApproxUplink, transitPV.getPosition(), transitPV.getDate()); + final double tauU = signalTimeOfFlight(stationApproxUplink, transitPV.getPosition(), + transitPV.getDate(), common.getState().getFrame()); final TimeStampedPVCoordinates stationUplink = stationApproxUplink.shiftedBy(transitPV.getDate().durationFrom(approxUplinkDate) - tauU); @@ -162,7 +163,8 @@ protected EstimatedMeasurement theoreticalEvaluation(final int iterat offsetToInertialApproxUplink.transformPVCoordinates(new TimeStampedFieldPVCoordinates<>(approxUplinkDateDS, zero, zero, zero)); - final Gradient tauU = signalTimeOfFlight(stationApproxUplink, transitPV.getPosition(), transitPV.getDate()); + final Gradient tauU = signalTimeOfFlight(stationApproxUplink, transitPV.getPosition(), transitPV.getDate(), + state.getFrame()); final TimeStampedFieldPVCoordinates stationUplink = stationApproxUplink.shiftedBy(transitPV.getDate().durationFrom(approxUplinkDateDS).subtract(tauU)); @@ -222,7 +224,6 @@ protected EstimatedMeasurement theoreticalEvaluation(final int iterat * @param transitPV spacecraft coordinates at onboard signal transit * @param transitState orbital state at onboard signal transit * @return theoretical value - * @see #evaluate(SpacecraftStatet) * @since 12.0 */ private EstimatedMeasurementBase oneWayTheoreticalEvaluation(final int iteration, final int evaluation, final boolean downlink, @@ -283,7 +284,6 @@ private EstimatedMeasurementBase oneWayTheoreticalEvaluation(final in * @param indices indices of the estimated parameters in derivatives computations * @param nbParams the number of estimated parameters in derivative computations * @return theoretical value - * @see #evaluate(SpacecraftStatet) */ private EstimatedMeasurement oneWayTheoreticalEvaluation(final int iteration, final int evaluation, final boolean downlink, final TimeStampedFieldPVCoordinates stationPV, @@ -294,13 +294,13 @@ private EstimatedMeasurement oneWayTheoreticalEvaluation(final int it // prepare the evaluation final EstimatedMeasurement estimated = - new EstimatedMeasurement(this, iteration, evaluation, - new SpacecraftState[] { - transitState - }, new TimeStampedPVCoordinates[] { - (downlink ? transitPV : stationPV).toTimeStampedPVCoordinates(), - (downlink ? stationPV : transitPV).toTimeStampedPVCoordinates() - }); + new EstimatedMeasurement<>(this, iteration, evaluation, + new SpacecraftState[] { + transitState + }, new TimeStampedPVCoordinates[] { + (downlink ? transitPV : stationPV).toTimeStampedPVCoordinates(), + (downlink ? stationPV : transitPV).toTimeStampedPVCoordinates() + }); // range rate value final FieldVector3D stationPosition = stationPV.getPosition(); @@ -331,12 +331,11 @@ private EstimatedMeasurement oneWayTheoreticalEvaluation(final int it estimated.setEstimatedValue(rangeRate.getValue()); - // compute partial derivatives of (rr) with respect to spacecraft state Cartesian coordinates + // compute first order derivatives of (rr) with respect to spacecraft state Cartesian coordinates final double[] derivatives = rangeRate.getGradient(); estimated.setStateDerivatives(0, Arrays.copyOfRange(derivatives, 0, 6)); - // set partial derivatives with respect to parameters - // (beware element at index 0 is the value, not a derivative) + // Set first order derivatives with respect to parameters for (final ParameterDriver driver : getParametersDrivers()) { for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { final Integer index = indices.get(span.getData()); diff --git a/src/main/java/org/orekit/estimation/measurements/TDOA.java b/src/main/java/org/orekit/estimation/measurements/TDOA.java index 04aa22b6f2..de1ad5789c 100644 --- a/src/main/java/org/orekit/estimation/measurements/TDOA.java +++ b/src/main/java/org/orekit/estimation/measurements/TDOA.java @@ -208,11 +208,11 @@ protected EstimatedMeasurement theoreticalEvaluation(final int iteration, // set TDOA value estimated.setEstimatedValue(tdoa); - // set partial derivatives with respect to state + // set first order derivatives with respect to state final double[] derivatives = tdoaG.getGradient(); estimated.setStateDerivatives(0, Arrays.copyOfRange(derivatives, 0, 6)); - // set partial derivatives with respect to parameters + // Set first order derivatives with respect to parameters for (final ParameterDriver driver : getParametersDrivers()) { for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { final Integer index = common.getIndices().get(span.getData()); diff --git a/src/main/java/org/orekit/estimation/measurements/TurnAroundRange.java b/src/main/java/org/orekit/estimation/measurements/TurnAroundRange.java index 096260272f..ccd65f0469 100644 --- a/src/main/java/org/orekit/estimation/measurements/TurnAroundRange.java +++ b/src/main/java/org/orekit/estimation/measurements/TurnAroundRange.java @@ -156,7 +156,8 @@ protected EstimatedMeasurementBase theoreticalEvaluationWithout Vector3D.ZERO, Vector3D.ZERO, Vector3D.ZERO)); // Compute propagation times - final double primaryTauD = signalTimeOfFlight(pva, primaryArrival.getPosition(), measurementDate); + final double primaryTauD = signalTimeOfFlight(pva, primaryArrival.getPosition(), measurementDate, + state.getFrame()); // Elapsed time between state date t' and signal arrival to the transit state of the 2nd leg final double dtLeg2 = delta - primaryTauD; @@ -180,8 +181,9 @@ protected EstimatedMeasurementBase theoreticalEvaluationWithout // Uplink time of flight from secondary station to transit state of leg2 final double secondaryTauU = signalTimeOfFlight(QSecondaryApprox, - transitStateLeg2PV.getPosition(), - transitStateLeg2PV.getDate()); + transitStateLeg2PV.getPosition(), + transitStateLeg2PV.getDate(), + state.getFrame()); // Total time of flight for leg 2 final double tauLeg2 = primaryTauD + secondaryTauU; @@ -201,7 +203,8 @@ protected EstimatedMeasurementBase theoreticalEvaluationWithout // Downlink time of flight from transitStateLeg1 to secondary station at rebound date final double secondaryTauD = signalTimeOfFlight(transitStateLeg2PV, secondaryRebound.getPosition(), - reboundDate); + reboundDate, + state.getFrame()); // Elapsed time between state date t' and signal arrival to the transit state of the 1st leg @@ -222,7 +225,8 @@ protected EstimatedMeasurementBase theoreticalEvaluationWithout // Uplink time of flight from primary station to transit state of leg1 final double primaryTauU = signalTimeOfFlight(QPrimaryApprox, transitStateLeg1PV.getPosition(), - transitStateLeg1PV.getDate()); + transitStateLeg1PV.getDate(), + state.getFrame()); // Primary station PV in inertial frame at exact emission date final AbsoluteDate emissionDate = transitStateLeg1PV.getDate().shiftedBy(-primaryTauU); @@ -340,7 +344,8 @@ protected EstimatedMeasurement theoreticalEvaluation(final int zero, zero, zero)); // Compute propagation times - final Gradient primaryTauD = signalTimeOfFlight(pvaDS, primaryArrival.getPosition(), measurementDateDS); + final Gradient primaryTauD = signalTimeOfFlight(pvaDS, primaryArrival.getPosition(), + measurementDateDS, state.getFrame()); // Elapsed time between state date t' and signal arrival to the transit state of the 2nd leg final Gradient dtLeg2 = primaryTauD.negate().add(delta); @@ -365,7 +370,8 @@ protected EstimatedMeasurement theoreticalEvaluation(final int // Uplink time of flight from secondary station to transit state of leg2 final Gradient secondaryTauU = signalTimeOfFlight(QSecondaryApprox, transitStateLeg2PV.getPosition(), - transitStateLeg2PV.getDate()); + transitStateLeg2PV.getDate(), + state.getFrame()); // Total time of flight for leg 2 final Gradient tauLeg2 = primaryTauD.add(secondaryTauU); @@ -386,7 +392,8 @@ protected EstimatedMeasurement theoreticalEvaluation(final int // Downlink time of flight from transitStateLeg1 to secondary station at rebound date final Gradient secondaryTauD = signalTimeOfFlight(transitStateLeg2PV, secondaryRebound.getPosition(), - reboundDateDS); + reboundDateDS, + state.getFrame()); // Elapsed time between state date t' and signal arrival to the transit state of the 1st leg @@ -410,7 +417,8 @@ protected EstimatedMeasurement theoreticalEvaluation(final int // Uplink time of flight from primary station to transit state of leg1 final Gradient primaryTauU = signalTimeOfFlight(QPrimaryApprox, transitStateLeg1PV.getPosition(), - transitStateLeg1PV.getDate()); + transitStateLeg1PV.getDate(), + state.getFrame()); // Primary station PV in inertial frame at exact emission date final AbsoluteDate emissionDate = transitStateLeg1PV.getDate().toAbsoluteDate().shiftedBy(-primaryTauU.getValue()); @@ -454,12 +462,11 @@ protected EstimatedMeasurement theoreticalEvaluation(final int final Gradient turnAroundRange = (tauLeg2.add(tauLeg1)).multiply(cOver2); estimated.setEstimatedValue(turnAroundRange.getValue()); - // Turn-around range partial derivatives with respect to state + // Turn-around range first order derivatives with respect to state final double[] derivatives = turnAroundRange.getGradient(); estimated.setStateDerivatives(0, Arrays.copyOfRange(derivatives, 0, 6)); - // set partial derivatives with respect to parameters - // (beware element at index 0 is the value, not a derivative) + // Set first order derivatives with respect to parameters for (final ParameterDriver driver : getParametersDrivers()) { for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { final Integer index = indices.get(span.getData()); diff --git a/src/main/java/org/orekit/estimation/measurements/filtering/ResidualFilter.java b/src/main/java/org/orekit/estimation/measurements/filtering/ResidualFilter.java index e814194c35..bf7bc69cb0 100644 --- a/src/main/java/org/orekit/estimation/measurements/filtering/ResidualFilter.java +++ b/src/main/java/org/orekit/estimation/measurements/filtering/ResidualFilter.java @@ -51,7 +51,7 @@ public void filter(final ObservedMeasurement measurement, final SpacecraftSta // Computation of the estimated value of the measurement final SpacecraftState[] sc = new SpacecraftState[] {state}; - final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(0, 0, sc); + final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(sc); final double[] estimatedValue = estimated.getEstimatedValue(); // Observed parameters (i.e. value and standard deviation) diff --git a/src/main/java/org/orekit/estimation/measurements/generation/AngularAzElBuilder.java b/src/main/java/org/orekit/estimation/measurements/generation/AngularAzElBuilder.java index 97f031ca4a..506f1a5706 100644 --- a/src/main/java/org/orekit/estimation/measurements/generation/AngularAzElBuilder.java +++ b/src/main/java/org/orekit/estimation/measurements/generation/AngularAzElBuilder.java @@ -86,7 +86,7 @@ public AngularAzEl build(final AbsoluteDate date, final Map { + + /** Satellite which receives the signal and performs the measurement. */ + private final ObservableSatellite local; + + /** Satellite which simply emits the signal. */ + private final ObservableSatellite remote; + + /** Simple constructor. + * @param noiseSource noise source, may be null for generating perfect measurements + * @param local satellite which receives the signal and performs the measurement + * @param remote satellite which simply emits the signal + * @param sigma theoretical standard deviation + * @param baseWeight base weight + */ + public InterSatellitesOneWayRangeRateBuilder(final CorrelatedRandomVectorGenerator noiseSource, + final ObservableSatellite local, final ObservableSatellite remote, + final double sigma, final double baseWeight) { + super(noiseSource, sigma, baseWeight, local, remote); + this.local = local; + this.remote = remote; + } + + /** {@inheritDoc} */ + @Override + public InterSatellitesOneWayRangeRate build(final AbsoluteDate date, + final Map interpolators) { + + final double sigma = getTheoreticalStandardDeviation()[0]; + final double baseWeight = getBaseWeight()[0]; + final SpacecraftState[] relevant = new SpacecraftState[] { + interpolators.get(local).getInterpolatedState(date), + interpolators.get(remote).getInterpolatedState(date) + }; + + // create a dummy measurement + final InterSatellitesOneWayRangeRate dummy = new InterSatellitesOneWayRangeRate(local, remote, relevant[0].getDate(), + Double.NaN, sigma, baseWeight); + for (final EstimationModifier modifier : getModifiers()) { + dummy.addModifier(modifier); + } + + // set a reference date for parameters missing one + for (final ParameterDriver driver : dummy.getParametersDrivers()) { + if (driver.getReferenceDate() == null) { + final AbsoluteDate start = getStart(); + final AbsoluteDate end = getEnd(); + driver.setReferenceDate(start.durationFrom(end) <= 0 ? start : end); + } + } + + // estimate the perfect value of the measurement + double rangeRate = dummy.estimateWithoutDerivatives(relevant).getEstimatedValue()[0]; + + // add the noise + final double[] noise = getNoise(); + if (noise != null) { + rangeRate += noise[0]; + } + + // generate measurement + final InterSatellitesOneWayRangeRate measurement = new InterSatellitesOneWayRangeRate(local, remote, relevant[0].getDate(), + rangeRate, sigma, baseWeight); + for (final EstimationModifier modifier : getModifiers()) { + measurement.addModifier(modifier); + } + return measurement; + + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/generation/InterSatellitesPhaseBuilder.java b/src/main/java/org/orekit/estimation/measurements/generation/InterSatellitesPhaseBuilder.java index 96091f019a..2cb481c4e8 100644 --- a/src/main/java/org/orekit/estimation/measurements/generation/InterSatellitesPhaseBuilder.java +++ b/src/main/java/org/orekit/estimation/measurements/generation/InterSatellitesPhaseBuilder.java @@ -21,6 +21,7 @@ import org.hipparchus.random.CorrelatedRandomVectorGenerator; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.ObservableSatellite; +import org.orekit.estimation.measurements.gnss.AmbiguityCache; import org.orekit.estimation.measurements.gnss.InterSatellitesPhase; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.sampling.OrekitStepInterpolator; @@ -34,6 +35,11 @@ */ public class InterSatellitesPhaseBuilder extends AbstractMeasurementBuilder { + /** Cache for ambiguities. + * @since 12.1 + */ + private final AmbiguityCache cache; + /** Wavelength of the phase observed value [m]. */ private final double wavelength; @@ -54,11 +60,33 @@ public class InterSatellitesPhaseBuilder extends AbstractMeasurementBuilder modifier : getModifiers()) { dummy.addModifier(modifier); } @@ -92,7 +121,7 @@ public InterSatellitesPhase build(final AbsoluteDate date, final Map modifier : getModifiers()) { measurement.addModifier(modifier); } diff --git a/src/main/java/org/orekit/estimation/measurements/generation/InterSatellitesRangeBuilder.java b/src/main/java/org/orekit/estimation/measurements/generation/InterSatellitesRangeBuilder.java index 3d6b05ed7f..a9006d23e2 100644 --- a/src/main/java/org/orekit/estimation/measurements/generation/InterSatellitesRangeBuilder.java +++ b/src/main/java/org/orekit/estimation/measurements/generation/InterSatellitesRangeBuilder.java @@ -94,7 +94,7 @@ public InterSatellitesRange build(final AbsoluteDate date, final Map> { */ T build(AbsoluteDate date, Map interpolators); + /** Generate a single measurement.

    + * + * Warning: This method uses "shiftedBy" so it is not as accurate as the method above that uses interpolators. + * + * @param date measurement date + * @param states all spacecraft states (i.e. including ones that may not be relevant for the current builder) + * @return generated measurement + * @since 12.1 + */ + default T build(AbsoluteDate date, SpacecraftState[] states) { + final Map interpolators = new ConcurrentHashMap<>(); + + for (int i = 0; i < states.length; i++) { + final ObservableSatellite sat = getSatellites()[i]; + final SpacecraftState state = states[i]; + + final OrekitStepInterpolator interpolator = new OrekitStepInterpolator() { + /** {@inheritDoc} */ + @Override + public OrekitStepInterpolator restrictStep(final SpacecraftState newPreviousState, final SpacecraftState newCurrentState) { + return null; + } + /** {@inheritDoc} */ + @Override + public boolean isPreviousStateInterpolated() { + return false; + } + /** {@inheritDoc} */ + @Override + public boolean isForward() { + return true; + } + /** {@inheritDoc} */ + @Override + public boolean isCurrentStateInterpolated() { + return false; + } + /** {@inheritDoc} */ + @Override + public SpacecraftState getPreviousState() { + return state; + } + /** {@inheritDoc} */ + @Override + public SpacecraftState getInterpolatedState(final AbsoluteDate date) { + return state.shiftedBy(date.durationFrom(state)); + } + /** {@inheritDoc} */ + @Override + public SpacecraftState getCurrentState() { + return state; + } + }; + interpolators.put(sat, interpolator); + } + + return build( date, interpolators); + } } diff --git a/src/main/java/org/orekit/estimation/measurements/generation/OneWayGNSSPhaseBuilder.java b/src/main/java/org/orekit/estimation/measurements/generation/OneWayGNSSPhaseBuilder.java index 600cc60704..2afb488a91 100644 --- a/src/main/java/org/orekit/estimation/measurements/generation/OneWayGNSSPhaseBuilder.java +++ b/src/main/java/org/orekit/estimation/measurements/generation/OneWayGNSSPhaseBuilder.java @@ -17,11 +17,14 @@ package org.orekit.estimation.measurements.generation; import java.util.Map; +import java.util.function.Function; import java.util.function.ToDoubleFunction; import org.hipparchus.random.CorrelatedRandomVectorGenerator; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.ObservableSatellite; +import org.orekit.estimation.measurements.QuadraticClockModel; +import org.orekit.estimation.measurements.gnss.AmbiguityCache; import org.orekit.estimation.measurements.gnss.OneWayGNSSPhase; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.sampling.OrekitStepInterpolator; @@ -35,6 +38,11 @@ */ public class OneWayGNSSPhaseBuilder extends AbstractMeasurementBuilder { + /** Cache for ambiguities. + * @since 12.1 + */ + private final AmbiguityCache cache; + /** Wavelength of the phase observed value [m]. */ private final double wavelength; @@ -45,7 +53,13 @@ public class OneWayGNSSPhaseBuilder extends AbstractMeasurementBuilder remoteClockModel; + private final QuadraticClockModel remoteClockModel; + + /** Temporary builder for clock models. + * @deprecated this is a temporary field, it will be removed in Orekit 13.0 + */ + @Deprecated + private Function clockBuilder; /** Simple constructor. * @param noiseSource noise source, may be null for generating perfect measurements @@ -55,21 +69,53 @@ public class OneWayGNSSPhaseBuilder extends AbstractMeasurementBuilder remoteClockModel, final double wavelength, final double sigma, final double baseWeight) { + this(noiseSource, local, remote, null, wavelength, sigma, baseWeight, + AmbiguityCache.DEFAULT_CACHE); + this.clockBuilder = date -> { + final double cM = remoteClockModel.applyAsDouble(date.shiftedBy(-1)); + final double c0 = remoteClockModel.applyAsDouble(date); + final double cP = remoteClockModel.applyAsDouble(date.shiftedBy(1)); + return new QuadraticClockModel(date, c0, 0.5 * (cP - cM), 0.5 * (cP + cM) - c0); + }; + } + + /** Simple constructor. + * @param noiseSource noise source, may be null for generating perfect measurements + * @param local satellite which receives the signal and performs the measurement + * @param remote satellite which simply emits the signal + * @param remoteClockModel clock model of the remote satellite that provides clock offset + * @param wavelength phase observed value wavelength (m) + * @param sigma theoretical standard deviation + * @param baseWeight base weight + * @param cache from which ambiguity drive should come + * @since 12.1 + */ + public OneWayGNSSPhaseBuilder(final CorrelatedRandomVectorGenerator noiseSource, + final ObservableSatellite local, final ObservableSatellite remote, + final QuadraticClockModel remoteClockModel, + final double wavelength, final double sigma, final double baseWeight, + final AmbiguityCache cache) { super(noiseSource, sigma, baseWeight, local, remote); this.wavelength = wavelength; this.local = local; this.remote = remote; this.remoteClockModel = remoteClockModel; + this.cache = cache; } /** {@inheritDoc} */ @Override - public OneWayGNSSPhase build(final AbsoluteDate date, final Map interpolators) { + public OneWayGNSSPhase build(final AbsoluteDate date, + final Map interpolators) { final double sigma = getTheoreticalStandardDeviation()[0]; final double baseWeight = getBaseWeight()[0]; @@ -77,11 +123,20 @@ public OneWayGNSSPhase build(final AbsoluteDate date, final Map + // for compatibility purposes + final QuadraticClockModel clockModel = remoteClockModel != null ? + remoteClockModel : + clockBuilder.apply(date); // create a dummy measurement - final OneWayGNSSPhase dummy = new OneWayGNSSPhase(interpolators.get(remote), offset, date, - Double.NaN, wavelength, sigma, baseWeight, local); + final OneWayGNSSPhase dummy = new OneWayGNSSPhase(interpolators.get(remote), + remote.getName(), + clockModel, date, + Double.NaN, wavelength, + sigma, baseWeight, local, + cache); for (final EstimationModifier modifier : getModifiers()) { dummy.addModifier(modifier); } @@ -96,7 +151,7 @@ public OneWayGNSSPhase build(final AbsoluteDate date, final Map modifier : getModifiers()) { measurement.addModifier(modifier); } diff --git a/src/main/java/org/orekit/estimation/measurements/generation/OneWayGNSSRangeBuilder.java b/src/main/java/org/orekit/estimation/measurements/generation/OneWayGNSSRangeBuilder.java index f530e42c4b..45ebcbea49 100644 --- a/src/main/java/org/orekit/estimation/measurements/generation/OneWayGNSSRangeBuilder.java +++ b/src/main/java/org/orekit/estimation/measurements/generation/OneWayGNSSRangeBuilder.java @@ -91,7 +91,7 @@ public OneWayGNSSRange build(final AbsoluteDate date, final Map { + + /** Satellite which receives the signal and performs the measurement. */ + private final ObservableSatellite local; + + /** Satellite which simply emits the signal. */ + private final ObservableSatellite remote; + + /** Simple constructor. + * @param noiseSource noise source, may be null for generating perfect measurements + * @param local satellite which receives the signal and performs the measurement + * @param remote satellite which simply emits the signal + * @param sigma theoretical standard deviation + * @param baseWeight base weight + */ + public OneWayGNSSRangeRateBuilder(final CorrelatedRandomVectorGenerator noiseSource, + final ObservableSatellite local, final ObservableSatellite remote, + final double sigma, final double baseWeight) { + super(noiseSource, sigma, baseWeight, local, remote); + this.local = local; + this.remote = remote; + } + + /** {@inheritDoc} */ + @Override + public OneWayGNSSRangeRate build(final AbsoluteDate date, + final Map interpolators) { + + final double sigma = getTheoreticalStandardDeviation()[0]; + final double baseWeight = getBaseWeight()[0]; + final SpacecraftState[] relevant = new SpacecraftState[] { + interpolators.get(local).getInterpolatedState(date), + interpolators.get(remote).getInterpolatedState(date) + }; + + // create a dummy measurement + final OneWayGNSSRangeRate dummy = new OneWayGNSSRangeRate(interpolators.get(remote), + remote.getQuadraticClockModel(), relevant[0].getDate(), + Double.NaN, sigma, baseWeight, local); + for (final EstimationModifier modifier : getModifiers()) { + dummy.addModifier(modifier); + } + + // set a reference date for parameters missing one + for (final ParameterDriver driver : dummy.getParametersDrivers()) { + if (driver.getReferenceDate() == null) { + final AbsoluteDate start = getStart(); + final AbsoluteDate end = getEnd(); + driver.setReferenceDate(start.durationFrom(end) <= 0 ? start : end); + } + } + + // estimate the perfect value of the measurement + double rangeRate = dummy.estimateWithoutDerivatives(relevant).getEstimatedValue()[0]; + + // add the noise + final double[] noise = getNoise(); + if (noise != null) { + rangeRate += noise[0]; + } + + // generate measurement + final OneWayGNSSRangeRate measurement = new OneWayGNSSRangeRate(interpolators.get(remote), + remote.getQuadraticClockModel(), relevant[0].getDate(), + rangeRate, sigma, baseWeight, local); + for (final EstimationModifier modifier : getModifiers()) { + measurement.addModifier(modifier); + } + return measurement; + + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/generation/PVBuilder.java b/src/main/java/org/orekit/estimation/measurements/generation/PVBuilder.java index e05445b353..63ca5f1266 100644 --- a/src/main/java/org/orekit/estimation/measurements/generation/PVBuilder.java +++ b/src/main/java/org/orekit/estimation/measurements/generation/PVBuilder.java @@ -84,7 +84,7 @@ public PV build(final AbsoluteDate date, final Map + * The measurement is considered to be a signal emitted from + * a remote satellite and received by a local satellite. + * Its value is the number of cycles between emission and reception. + * The motion of both spacecraft during the signal flight time + * are taken into account. The date of the measurement corresponds to the + * reception on ground of the emitted signal. + *

    + * @param type of the measurement + * @author Luc Maisonobe + * @since 12.1 + */ +public abstract class AbstractInterSatellitesMeasurement> extends AbstractOnBoardMeasurement { + + /** Constructor. + * @param date date of the measurement + * @param observed observed value + * @param sigma theoretical standard deviation + * @param baseWeight base weight + * @param local satellite which receives the signal and performs the measurement + * @param remote remote satellite which simply emits the signal + */ + public AbstractInterSatellitesMeasurement(final AbsoluteDate date, final double observed, + final double sigma, final double baseWeight, + final ObservableSatellite local, + final ObservableSatellite remote) { + // Call to super constructor + super(date, observed, sigma, baseWeight, Arrays.asList(local, remote)); + } + + /** {@inheritDoc} */ + @Override + protected PVCoordinatesProvider getRemotePV(final SpacecraftState[] states) { + return new ShiftingPVCoordinatesProvider(states[1].getPVCoordinates(), states[1].getFrame()); + } + + /** {@inheritDoc} */ + @Override + protected QuadraticClockModel getRemoteClock() { + return getSatellites().get(1).getQuadraticClockModel(); + } + + /** {@inheritDoc} */ + @Override + protected FieldPVCoordinatesProvider getRemotePV(final SpacecraftState[] states, + final int freeParameters) { + // convert the SpacecraftState to a FieldPVCoordinatesProvider + return (date, frame) -> { + + // set up the derivatives with respect to remote state at its date + final TimeStampedFieldPVCoordinates pv0 = getCoordinates(states[1], 6, freeParameters); + + // shift to desired date + return pv0.shiftedBy(date.durationFrom(states[1].getDate())); + + }; + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/AbstractOnBoardMeasurement.java b/src/main/java/org/orekit/estimation/measurements/gnss/AbstractOnBoardMeasurement.java new file mode 100644 index 0000000000..1e4237744d --- /dev/null +++ b/src/main/java/org/orekit/estimation/measurements/gnss/AbstractOnBoardMeasurement.java @@ -0,0 +1,200 @@ +/* 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.estimation.measurements.gnss; + +import org.hipparchus.analysis.differentiation.Gradient; +import org.hipparchus.analysis.differentiation.GradientField; +import org.orekit.estimation.measurements.AbstractMeasurement; +import org.orekit.estimation.measurements.ObservableSatellite; +import org.orekit.estimation.measurements.ObservedMeasurement; +import org.orekit.estimation.measurements.QuadraticClockModel; +import org.orekit.estimation.measurements.QuadraticFieldClockModel; +import org.orekit.propagation.SpacecraftState; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.ClockOffset; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.FieldClockOffset; +import org.orekit.utils.FieldPVCoordinatesProvider; +import org.orekit.utils.PVCoordinatesProvider; +import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TimeSpanMap.Span; +import org.orekit.utils.TimeStampedFieldPVCoordinates; +import org.orekit.utils.TimeStampedPVCoordinates; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** Base class modeling a measurement where receiver is a satellite. + * @param type of the measurement + * @author Luc Maisonobe + * @since 12.1 + */ +public abstract class AbstractOnBoardMeasurement> extends AbstractMeasurement { + + /** Constructor. + * @param date date of the measurement + * @param observed observed value + * @param sigma theoretical standard deviation + * @param baseWeight base weight + * @param satellites satellites related to this measurement + */ + public AbstractOnBoardMeasurement(final AbsoluteDate date, final double observed, + final double sigma, final double baseWeight, + final List satellites) { + // Call to super constructor + super(date, observed, sigma, baseWeight, satellites); + + // Add parameter drivers + satellites.forEach(s -> { + addParameterDriver(s.getClockOffsetDriver()); + addParameterDriver(s.getClockDriftDriver()); + addParameterDriver(s.getClockAccelerationDriver()); + }); + + } + + /** Get emitting satellite clock provider. + * @return emitting satellite clock provider + */ + protected abstract QuadraticClockModel getRemoteClock(); + + /** Get emitting satellite position/velocity provider. + * @param states states of all spacecraft involved in the measurement + * @return emitting satellite position/velocity provider + */ + protected abstract PVCoordinatesProvider getRemotePV(SpacecraftState[] states); + + /** Get emitting satellite position/velocity provider. + * @param states states of all spacecraft involved in the measurement + * @param freeParameters total number of free parameters in the gradient + * @return emitting satellite position/velocity provider + */ + protected abstract FieldPVCoordinatesProvider getRemotePV(SpacecraftState[] states, + int freeParameters); + + /** Get emitting satellite clock provider. + * @param freeParameters total number of free parameters in the gradient + * @param indices indices of the differentiation parameters in derivatives computations, + * must be span name and not driver name + * @return emitting satellite clock provider + */ + protected QuadraticFieldClockModel getRemoteClock(final int freeParameters, + final Map indices) { + return getRemoteClock().toGradientModel(freeParameters, indices, getDate()); + } + + /** Compute common estimation parameters. + * @param states states of all spacecraft involved in the measurement + * @param clockOffsetAlreadyApplied if true, the specified {@code date} is as read + * by the receiver clock (i.e. clock offset not compensated), if false, + * the specified {@code date} was already compensated and is a physical absolute date + * @return common parameters + */ + protected OnBoardCommonParametersWithoutDerivatives computeCommonParametersWithout(final SpacecraftState[] states, + final boolean clockOffsetAlreadyApplied) { + + // local and remote satellites + final TimeStampedPVCoordinates pvaLocal = states[0].getPVCoordinates(); + final ClockOffset localClock = getSatellites(). + get(0). + getQuadraticClockModel(). + getOffset(getDate()); + final double localClockOffset = localClock.getOffset(); + final double localClockRate = localClock.getRate(); + final PVCoordinatesProvider remotePV = getRemotePV(states); + + // take clock offset into account + final AbsoluteDate arrivalDate = clockOffsetAlreadyApplied ? getDate() : getDate().shiftedBy(-localClockOffset); + + // Downlink delay + 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()); + + // Remote satellite at signal emission + final AbsoluteDate emissionDate = arrivalDate.shiftedBy(-tauD); + final ClockOffset remoteClock = getRemoteClock().getOffset(emissionDate); + final double remoteClockOffset = remoteClock.getOffset(); + final double remoteClockRate = remoteClock.getRate(); + return new OnBoardCommonParametersWithoutDerivatives(states[0], + localClockOffset, localClockRate, + remoteClockOffset, remoteClockRate, + tauD, pvaDownlink, + remotePV.getPVCoordinates(emissionDate, states[0].getFrame())); + + } + + /** Compute common estimation parameters. + * @param states states of all spacecraft involved in the measurement + * @param clockOffsetAlreadyApplied if true, the specified {@code date} is as read + * by the receiver clock (i.e. clock offset not compensated), if false, + * the specified {@code date} was already compensated and is a physical absolute date + * @return common parameters + */ + protected OnBoardCommonParametersWithDerivatives computeCommonParametersWith(final SpacecraftState[] states, + final boolean clockOffsetAlreadyApplied) { + + // 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 + // - 6k+3..6k+5 - Velocity of spacecraft k (counting k from 0 to nbSat-1) in inertial frame + // - 6nbSat..n - measurements parameters (clock offset, etc) + int nbEstimatedParams = 6 * states.length; + final Map parameterIndices = new HashMap<>(); + for (ParameterDriver measurementDriver : getParametersDrivers()) { + if (measurementDriver.isSelected()) { + for (Span span = measurementDriver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + parameterIndices.put(span.getData(), nbEstimatedParams++); + } + } + } + final FieldAbsoluteDate gDate = new FieldAbsoluteDate<>(GradientField.getField(nbEstimatedParams), + getDate()); + + // local and remote satellites + final TimeStampedFieldPVCoordinates pvaLocal = getCoordinates(states[0], 0, nbEstimatedParams); + final QuadraticFieldClockModel localClock = getSatellites().get(0).getQuadraticClockModel(). + toGradientModel(nbEstimatedParams, parameterIndices, getDate()); + final FieldClockOffset localClockOffset = localClock.getOffset(gDate); + final FieldPVCoordinatesProvider remotePV = getRemotePV(states, nbEstimatedParams); + + // take clock offset into account + final FieldAbsoluteDate arrivalDate = clockOffsetAlreadyApplied ? + gDate : gDate.shiftedBy(localClockOffset.getOffset().negate()); + + // Downlink delay + final Gradient deltaT = arrivalDate.durationFrom(states[0].getDate()); + final TimeStampedFieldPVCoordinates pvaDownlink = pvaLocal.shiftedBy(deltaT); + final Gradient tauD = signalTimeOfFlight(remotePV, arrivalDate, + pvaDownlink.getPosition(), arrivalDate, + states[0].getFrame()); + + // Remote satellite at signal emission + final FieldAbsoluteDate emissionDate = arrivalDate.shiftedBy(tauD.negate()); + final QuadraticFieldClockModel remoteClock = getRemoteClock(nbEstimatedParams, parameterIndices); + final FieldClockOffset remoteClockOffset = remoteClock.getOffset(emissionDate); + return new OnBoardCommonParametersWithDerivatives(states[0], parameterIndices, + localClockOffset.getOffset(), localClockOffset.getRate(), + remoteClockOffset.getOffset(), remoteClockOffset.getRate(), + tauD, pvaDownlink, + remotePV.getPVCoordinates(emissionDate, states[0].getFrame())); + + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/AbstractOneWayGNSSMeasurement.java b/src/main/java/org/orekit/estimation/measurements/gnss/AbstractOneWayGNSSMeasurement.java new file mode 100644 index 0000000000..34e64d2e60 --- /dev/null +++ b/src/main/java/org/orekit/estimation/measurements/gnss/AbstractOneWayGNSSMeasurement.java @@ -0,0 +1,121 @@ +/* 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.estimation.measurements.gnss; + +import java.util.Collections; + +import org.hipparchus.analysis.differentiation.Gradient; +import org.orekit.estimation.measurements.ObservableSatellite; +import org.orekit.estimation.measurements.ObservedMeasurement; +import org.orekit.estimation.measurements.QuadraticClockModel; +import org.orekit.propagation.SpacecraftState; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.FieldPVCoordinatesProvider; +import org.orekit.utils.PVCoordinatesProvider; +import org.orekit.utils.TimeStampedFieldPVCoordinates; +import org.orekit.utils.TimeStampedPVCoordinates; + +/** Base class for one-way GNSS measurement. + *

    + * This class can be used in precise orbit determination applications + * for modeling a range measurement between a GNSS satellite (emitter) + * and a LEO satellite (receiver). + *

    + *

    + * The one-way GNSS range measurement assumes knowledge of the orbit and + * the clock offset of the emitting GNSS satellite. For instance, it is + * possible to use a SP3 file or a GNSS navigation message to recover + * the satellite's orbit and clock. + *

    + *

    + * This class is very similar to {@link AbstractInterSatellitesMeasurement} measurement + * class. However, using the one-way GNSS range measurement, the orbit and clock + * of the emitting GNSS satellite are NOT estimated simultaneously with + * LEO satellite coordinates. + *

    + * + * @param type of the measurement + * @author Luc Maisonobe + * @since 12.1 + */ +public abstract class AbstractOneWayGNSSMeasurement> + extends AbstractOnBoardMeasurement { + + /** Emitting satellite. */ + private final PVCoordinatesProvider remotePV; + + /** Clock offset of the emitting satellite. */ + private final QuadraticClockModel remoteClock; + + /** Simple constructor. + * @param remotePV provider for GNSS satellite which simply emits the signal + * @param remoteClock clock offset of the GNSS satellite + * @param date date of the measurement + * @param range observed value + * @param sigma theoretical standard deviation + * @param baseWeight base weight + * @param local satellite which receives the signal and perform the measurement + */ + public AbstractOneWayGNSSMeasurement(final PVCoordinatesProvider remotePV, + final QuadraticClockModel remoteClock, + final AbsoluteDate date, + final double range, final double sigma, + final double baseWeight, final ObservableSatellite local) { + // Call super constructor + super(date, range, sigma, baseWeight, Collections.singletonList(local)); + // The local satellite clock offset affects the measurement + addParameterDriver(local.getClockOffsetDriver()); + addParameterDriver(local.getClockDriftDriver()); + addParameterDriver(local.getClockAccelerationDriver()); + // Initialise fields + this.remotePV = remotePV; + this.remoteClock = remoteClock; + } + + /** {@inheritDoc} */ + @Override + protected PVCoordinatesProvider getRemotePV(final SpacecraftState[] states) { + return remotePV; + } + + /** {@inheritDoc} */ + @Override + protected QuadraticClockModel getRemoteClock() { + return remoteClock; + } + + /** {@inheritDoc} */ + @Override + protected FieldPVCoordinatesProvider getRemotePV(final SpacecraftState[] states, + final int freeParameters) { + // convert the PVCoordinatesProvider to a FieldPVCoordinatesProvider + return (date, frame) -> { + + // apply the raw (no derivatives) remote provider + final AbsoluteDate dateBase = date.toAbsoluteDate(); + final TimeStampedPVCoordinates pvBase = remotePV.getPVCoordinates(dateBase, frame); + final TimeStampedFieldPVCoordinates pvWithoutDerivatives = + new TimeStampedFieldPVCoordinates<>(date.getField(), pvBase); + + // add derivatives, using a trick: we shift the date by 0, with derivatives + final Gradient zeroWithDerivatives = date.durationFrom(dateBase); + return pvWithoutDerivatives.shiftedBy(zeroWithDerivatives); + + }; + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/AbstractWindUp.java b/src/main/java/org/orekit/estimation/measurements/gnss/AbstractWindUp.java index 6d8a4b1b41..0c33173a8d 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/AbstractWindUp.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/AbstractWindUp.java @@ -38,10 +38,10 @@ public abstract class AbstractWindUp> implements EstimationModifier { /** Emitter dipole. */ - private Dipole emitter; + private final Dipole emitter; /** Receiver dipole. */ - private Dipole receiver; + private final Dipole receiver; /** Cached angular value of wind-up. */ private double angularWindUp; @@ -104,13 +104,13 @@ public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated final double correction = FastMath.copySign(Vector3D.angle(dEmitter, dReceiver), Vector3D.dotProduct(los, Vector3D.crossProduct(dEmitter, dReceiver))); - // ensure continuity accross measurements + // ensure continuity across measurements // we assume the various measurements are close enough in time // (less the one satellite half-turn) so the angles remain close angularWindUp = MathUtils.normalizeAngle(correction, angularWindUp); // update estimate - estimated.setEstimatedValue(estimated.getEstimatedValue()[0] + angularWindUp / MathUtils.TWO_PI); + estimated.modifyEstimatedValue(this, estimated.getEstimatedValue()[0] + angularWindUp / MathUtils.TWO_PI); } diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/AmbiguityCache.java b/src/main/java/org/orekit/estimation/measurements/gnss/AmbiguityCache.java new file mode 100644 index 0000000000..575254b05b --- /dev/null +++ b/src/main/java/org/orekit/estimation/measurements/gnss/AmbiguityCache.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.estimation.measurements.gnss; + +import org.hipparchus.util.Precision; + +import java.util.HashMap; +import java.util.Map; + +/** Cache for {@link AmbiguityDriver}. + * @author Luc Maisonobe + * @since 12.1 + */ +public class AmbiguityCache { + + /** Default cache. + * @deprecated this default cache is only a temporary hack for compatibility purposes + * it will be removed in Orekit 13.0 + */ + @Deprecated + public static final AmbiguityCache DEFAULT_CACHE = new AmbiguityCache(); + + /** Cache map. */ + private final Map cache; + + /** Simple constructor. + */ + public AmbiguityCache() { + cache = new HashMap<>(); + } + + /** Get a cached driver for ambiguity. + *

    + * A new parameter driver is created and cached the first time an + * emitter/receiver/wavelength triplet is used; after that, the cached + * driver will be returned when the same triplet is passed again + *

    + * @param emitter emitter id + * @param receiver receiver id + * @param wavelength signal wavelength + * @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)); + } + + /** Key for the map. */ + private static class Key { + + /** Emitter id. */ + private final String emitter; + + /** Receiver id. */ + private final String receiver; + + /** Wavelength. */ + private final double wavelength; + + /** Simple constructor. + * @param emitter emitter id + * @param receiver receiver id + * @param wavelength signal wavelength + */ + Key(final String emitter, final String receiver, final double wavelength) { + this.emitter = emitter; + this.receiver = receiver; + this.wavelength = wavelength; + } + + /** {@inheritDoc} */ + @Override + public int hashCode() { + return (emitter.hashCode() ^ receiver.hashCode()) ^ Double.hashCode(wavelength); + } + + /** {@inheritDoc} */ + @Override + public boolean equals(final Object object) { + if (object instanceof Key) { + final Key other = (Key) object; + return emitter.equals(other.emitter) && receiver.equals(other.receiver) && + Precision.equals(wavelength, other.wavelength, 1); + } + return false; + } + + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/AmbiguityDriver.java b/src/main/java/org/orekit/estimation/measurements/gnss/AmbiguityDriver.java new file mode 100644 index 0000000000..e4f114e598 --- /dev/null +++ b/src/main/java/org/orekit/estimation/measurements/gnss/AmbiguityDriver.java @@ -0,0 +1,90 @@ +/* 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.estimation.measurements.gnss; + +import org.hipparchus.util.FastMath; +import org.orekit.gnss.GnssSignal; +import org.orekit.utils.Constants; +import org.orekit.utils.ParameterDriver; + +import java.util.Locale; + +/** Specialized {@link ParameterDriver} for ambiguity. + * @author Luc Maisonobe + * @since 12.1 + */ +public class AmbiguityDriver extends ParameterDriver { + + /** Prefix for parameter drivers names. */ + public static final String PREFIX = "ambiguity"; + + /** Ambiguity scale factor. + *

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

    + */ + private static final double AMBIGUITY_SCALE = FastMath.scalb(1.0, 26); + + /** Emitter id. */ + private final String emitter; + + /** Receiver id. */ + private final String receiver; + + /** Wavelength. */ + private final double wavelength; + + /** Simple constructor. + * @param emitter emitter id + * @param receiver receiver id + * @param wavelength signal wavelength + */ + public AmbiguityDriver(final String emitter, final String receiver, final double wavelength) { + // the name is built from emitter, receiver and the multiplier + // with respect to common frequency F0 (10.23 MHz) + super(String.format(Locale.US, "%s-%s-%s-%.2f", + PREFIX, emitter, receiver, + Constants.SPEED_OF_LIGHT / (wavelength * GnssSignal.F0)), + 0.0, AMBIGUITY_SCALE, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); + this.emitter = emitter; + this.receiver = receiver; + this.wavelength = wavelength; + } + + /** Get emitter id. + * @return emitter id + */ + public String getEmitter() { + return emitter; + } + + /** Get receiver id. + * @return receiver id + */ + public String getReceiver() { + return receiver; + } + + /** Get signal wavelength. + * @return signal wavelength + */ + public double getWavelength() { + return wavelength; + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/CombinedObservationData.java b/src/main/java/org/orekit/estimation/measurements/gnss/CombinedObservationData.java index 4cff504f30..300d5cbcdb 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/CombinedObservationData.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/CombinedObservationData.java @@ -35,9 +35,9 @@ public class CombinedObservationData { private final MeasurementType measurementType; /** Combined observed value. */ - private double value; + private final double value; - /** Frequency of the combined observation data [MHz]. */ + /** Frequency of the combined observation data [Hz]. */ private final double combinedFrequency; /** Observation data used to perform the combination of measurements. */ @@ -49,12 +49,33 @@ public class CombinedObservationData { * @param measurementType measurement type used for the combination of measurement * @param combinedValue combined observed value * (may be {@code Double.NaN} if combined observation not available) - * @param combinedFrequency frequency of the combined observation data in MHz + * @param combinedFrequencyMHz frequency of the combined observation data in MHz * (may be {@code Double.NaN} if combined frequency is not available) * @param usedData observation data used to perform the combination of measurements + * @deprecated as of 12.1, replaced by {@link #CombinedObservationData(double, double, + * CombinationType, MeasurementType, List)} */ + @Deprecated public CombinedObservationData(final CombinationType combinationType, final MeasurementType measurementType, - final double combinedValue, final double combinedFrequency, + final double combinedValue, final double combinedFrequencyMHz, + final List usedData) { + this(combinedValue, combinedFrequencyMHz * AbstractDualFrequencyCombination.MHZ_TO_HZ, + combinationType, measurementType, usedData); + } + + /** + * Constructor. + * @param combinedValue combined observed value + * (may be {@code Double.NaN} if combined observation not available) + * @param combinedFrequency frequency of the combined observation data in Hz + * (may be {@code Double.NaN} if combined frequency is not available) + * @param combinationType combination of measurements used to build the combined observation data + * @param measurementType measurement type used for the combination of measurement + * @param usedData observation data used to perform the combination of measurements + * @since 12.1 + */ + public CombinedObservationData(final double combinedValue, final double combinedFrequency, + final CombinationType combinationType, final MeasurementType measurementType, final List usedData) { this.combinationType = combinationType; this.measurementType = measurementType; @@ -70,15 +91,29 @@ public double getValue() { return value; } + /** Get the value of the combined frequency in MHz. + *

    + * For the single frequency combinations, this method returns + * the common frequency of both measurements. + *

    + * @return value of the combined frequency in Hz + * @since 12.1 + */ + public double getCombinedFrequency() { + return combinedFrequency; + } + /** Get the value of the combined frequency in MHz. *

    * For the single frequency combinations, this method returns * the common frequency of both measurements. *

    * @return value of the combined frequency in MHz + * @deprecated as of 12.1, replaced by {@link #getCombinedFrequency()} */ + @Deprecated public double getCombinedMHzFrequency() { - return combinedFrequency; + return getCombinedFrequency() / AbstractDualFrequencyCombination.MHZ_TO_HZ; } /** Get the type of the combination of measurements used to build the instance. diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/Dipole.java b/src/main/java/org/orekit/estimation/measurements/gnss/Dipole.java index 253228b4e7..6a5f657956 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/Dipole.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/Dipole.java @@ -44,7 +44,7 @@ public class Dipole { * @param primary primary dipole vector * @param secondary secondary dipole vector */ - Dipole(final Vector3D primary, final Vector3D secondary) { + public Dipole(final Vector3D primary, final Vector3D secondary) { this.primary = primary; this.secondary = secondary; } diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/InterSatellitesOneWayRangeRate.java b/src/main/java/org/orekit/estimation/measurements/gnss/InterSatellitesOneWayRangeRate.java new file mode 100644 index 0000000000..8956729226 --- /dev/null +++ b/src/main/java/org/orekit/estimation/measurements/gnss/InterSatellitesOneWayRangeRate.java @@ -0,0 +1,139 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.estimation.measurements.gnss; + +import org.hipparchus.analysis.differentiation.Gradient; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; +import org.orekit.estimation.measurements.ObservableSatellite; +import org.orekit.propagation.SpacecraftState; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.Constants; +import org.orekit.utils.FieldPVCoordinates; +import org.orekit.utils.PVCoordinates; +import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TimeSpanMap.Span; +import org.orekit.utils.TimeStampedPVCoordinates; + +import java.util.Arrays; + +/** One way range-rate measurement between two satellites. + * @author Luc Maisonobe + * @since 12.1 + */ +public class InterSatellitesOneWayRangeRate + extends AbstractInterSatellitesMeasurement { + + /** Type of the measurement. */ + public static final String MEASUREMENT_TYPE = "InterSatellitesOneWayRangeRate"; + + /** Constructor. + * @param local satellite which receives the signal and performs the measurement + * @param remote remote satellite which simply emits the signal + * @param date date of the measurement + * @param rangeRate observed value (m/s) + * @param sigma theoretical standard deviation + * @param baseWeight base weight + */ + public InterSatellitesOneWayRangeRate(final ObservableSatellite local, + final ObservableSatellite remote, + final AbsoluteDate date, final double rangeRate, + final double sigma, final double baseWeight) { + // Call to super constructor + super(date, rangeRate, sigma, baseWeight, local, remote); + } + + /** {@inheritDoc} */ + @Override + protected EstimatedMeasurementBase theoreticalEvaluationWithoutDerivatives(final int iteration, + final int evaluation, + final SpacecraftState[] states) { + + final OnBoardCommonParametersWithoutDerivatives common = computeCommonParametersWithout(states, false); + + // prepare the evaluation + final EstimatedMeasurementBase estimatedPhase = + new EstimatedMeasurementBase<>(this, iteration, evaluation, + new SpacecraftState[] { + common.getState(), + states[1] + }, new TimeStampedPVCoordinates[] { + common.getRemotePV(), + common.getTransitPV() + }); + + // Range rate value + final PVCoordinates delta = new PVCoordinates(common.getRemotePV(), common.getTransitPV()); + final double rangeRate = Vector3D.dotProduct(delta.getVelocity(), delta.getPosition().normalize()) + + Constants.SPEED_OF_LIGHT * (common.getLocalRate() - common.getRemoteRate()); + + estimatedPhase.setEstimatedValue(rangeRate); + + // Return the estimated measurement + return estimatedPhase; + + } + + /** {@inheritDoc} */ + @Override + protected EstimatedMeasurement theoreticalEvaluation(final int iteration, + final int evaluation, + final SpacecraftState[] states) { + + final OnBoardCommonParametersWithDerivatives common = computeCommonParametersWith(states, false); + + // prepare the evaluation + final EstimatedMeasurement estimatedPhase = + new EstimatedMeasurement<>(this, iteration, evaluation, + new SpacecraftState[] { + common.getState(), + states[1] + }, new TimeStampedPVCoordinates[] { + common.getRemotePV().toTimeStampedPVCoordinates(), + common.getTransitPV().toTimeStampedPVCoordinates() + }); + + // Range rate value + final FieldPVCoordinates delta = new FieldPVCoordinates<>(common.getRemotePV(), common.getTransitPV()); + final Gradient rangeRate = FieldVector3D.dotProduct(delta.getVelocity(), delta.getPosition().normalize()). + add(common.getLocalRate().subtract(common.getRemoteRate()).multiply(Constants.SPEED_OF_LIGHT)); + + estimatedPhase.setEstimatedValue(rangeRate.getValue()); + + // Range first order derivatives with respect to states + final double[] derivatives = rangeRate.getGradient(); + estimatedPhase.setStateDerivatives(0, Arrays.copyOfRange(derivatives, 0, 6)); + estimatedPhase.setStateDerivatives(1, Arrays.copyOfRange(derivatives, 6, 12)); + + // Set first order derivatives with respect to parameters + for (final ParameterDriver driver : getParametersDrivers()) { + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + final Integer index = common.getIndices().get(span.getData()); + if (index != null) { + estimatedPhase.setParameterDerivatives(driver, span.getStart(), derivatives[index]); + } + } + } + + // Return the estimated measurement + return estimatedPhase; + + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/InterSatellitesPhase.java b/src/main/java/org/orekit/estimation/measurements/gnss/InterSatellitesPhase.java index ecbf8dd2c1..e9531deaa1 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/InterSatellitesPhase.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/InterSatellitesPhase.java @@ -17,21 +17,16 @@ package org.orekit.estimation.measurements.gnss; import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; import org.hipparchus.analysis.differentiation.Gradient; -import org.orekit.estimation.measurements.AbstractMeasurement; import org.orekit.estimation.measurements.EstimatedMeasurement; import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.ObservableSatellite; import org.orekit.propagation.SpacecraftState; import org.orekit.time.AbsoluteDate; -import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.Constants; import org.orekit.utils.ParameterDriver; import org.orekit.utils.TimeSpanMap.Span; -import org.orekit.utils.TimeStampedFieldPVCoordinates; import org.orekit.utils.TimeStampedPVCoordinates; /** Phase measurement between two satellites. @@ -39,23 +34,26 @@ * The measurement is considered to be a signal emitted from * a remote satellite and received by a local satellite. * Its value is the number of cycles between emission and reception. - * The motion of both spacecrafts during the signal flight time + * The motion of both spacecraft during the signal flight time * are taken into account. The date of the measurement corresponds to the * reception on ground of the emitted signal. *

    * @author Bryan Cazabonne * @since 10.3 */ -public class InterSatellitesPhase extends AbstractMeasurement { +public class InterSatellitesPhase extends AbstractInterSatellitesMeasurement { /** Type of the measurement. */ public static final String MEASUREMENT_TYPE = "InterSatellitesPhase"; - /** Name for ambiguity driver. */ + /** Name for ambiguity driver. + * @deprecated as of 12.1 not used anymore + */ + @Deprecated public static final String AMBIGUITY_NAME = "ambiguity"; /** Driver for ambiguity. */ - private final ParameterDriver ambiguityDriver; + private final AmbiguityDriver ambiguityDriver; /** Wavelength of the phase observed value [m]. */ private final double wavelength; @@ -68,23 +66,45 @@ public class InterSatellitesPhase extends AbstractMeasurement theoreticalEvaluationWi final int evaluation, final SpacecraftState[] states) { - // Coordinates of both satellites - final SpacecraftState local = states[0]; - final TimeStampedPVCoordinates pvaL = local.getPVCoordinates(); - final SpacecraftState remote = states[1]; - final TimeStampedPVCoordinates pvaR = remote.getPVCoordinates(); - - // Compute propagation times - // Downlink delay - final double dtl = getSatellites().get(0).getClockOffsetDriver().getValue(AbsoluteDate.ARBITRARY_EPOCH); - final AbsoluteDate arrivalDate = getDate().shiftedBy(-dtl); - - final TimeStampedPVCoordinates s1Downlink = pvaL.shiftedBy(arrivalDate.durationFrom(pvaL.getDate())); - final double tauD = signalTimeOfFlight(pvaR, s1Downlink.getPosition(), arrivalDate); - - // Transit state - final double delta = getDate().durationFrom(remote.getDate()); - final double deltaMTauD = delta - tauD; + final OnBoardCommonParametersWithoutDerivatives common = computeCommonParametersWithout(states, false); // prepare the evaluation final EstimatedMeasurementBase estimatedPhase = new EstimatedMeasurementBase<>(this, iteration, evaluation, new SpacecraftState[] { - local.shiftedBy(deltaMTauD), - remote.shiftedBy(deltaMTauD) + common.getState(), + states[1] }, new TimeStampedPVCoordinates[] { - remote.shiftedBy(delta - tauD).getPVCoordinates(), - local.shiftedBy(delta).getPVCoordinates() + common.getRemotePV(), + common.getTransitPV() }); - // Clock offsets - final double dtr = getSatellites().get(1).getClockOffsetDriver().getValue(AbsoluteDate.ARBITRARY_EPOCH); - // Phase value final double cOverLambda = Constants.SPEED_OF_LIGHT / wavelength; - final double ambiguity = ambiguityDriver.getValue(AbsoluteDate.ARBITRARY_EPOCH); - final double phase = (tauD + dtl - dtr) * cOverLambda + ambiguity; + final double ambiguity = ambiguityDriver.getValue(common.getState().getDate()); + final double phase = (common.getTauD() + common.getLocalOffset() - common.getRemoteOffset()) * cOverLambda + + ambiguity; estimatedPhase.setEstimatedValue(phase); @@ -160,76 +162,39 @@ protected EstimatedMeasurement theoreticalEvaluation(final final int evaluation, final SpacecraftState[] states) { - // Phase derivatives are computed with respect to spacecrafts states in inertial frame - // ---------------------- - // - // Parameters: - // - 0..2 - Position of the receiver satellite in inertial frame - // - 3..5 - Velocity of the receiver satellite in inertial frame - // - 6..8 - Position of the remote satellite in inertial frame - // - 9..11 - Velocity of the remote satellite in inertial frame - // - 12.. - Measurement parameters: ambiguity, local clock offset, remote clock offset... - int nbParams = 12; - final Map indices = new HashMap<>(); - for (ParameterDriver phaseMeasurementDriver : getParametersDrivers()) { - if (phaseMeasurementDriver.isSelected()) { - for (Span span = phaseMeasurementDriver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { - - indices.put(span.getData(), nbParams++); - } - } - } - - // Coordinates of both satellites - final SpacecraftState local = states[0]; - final TimeStampedFieldPVCoordinates pvaL = getCoordinates(local, 0, nbParams); - final SpacecraftState remote = states[1]; - final TimeStampedFieldPVCoordinates pvaR = getCoordinates(remote, 6, nbParams); - - // Compute propagation times - // Downlink delay - final Gradient dtl = getSatellites().get(0).getClockOffsetDriver().getValue(nbParams, indices, AbsoluteDate.ARBITRARY_EPOCH); - final FieldAbsoluteDate arrivalDate = new FieldAbsoluteDate<>(getDate(), dtl.negate()); - - final TimeStampedFieldPVCoordinates s1Downlink = - pvaL.shiftedBy(arrivalDate.durationFrom(pvaL.getDate())); - final Gradient tauD = signalTimeOfFlight(pvaR, s1Downlink.getPosition(), arrivalDate); + final OnBoardCommonParametersWithDerivatives common = computeCommonParametersWith(states, false); - // Transit state - final double delta = getDate().durationFrom(remote.getDate()); - final Gradient deltaMTauD = tauD.negate().add(delta); - - // prepare the evaluation + // prepare the evaluation final EstimatedMeasurement estimatedPhase = new EstimatedMeasurement<>(this, iteration, evaluation, new SpacecraftState[] { - local.shiftedBy(deltaMTauD.getValue()), - remote.shiftedBy(deltaMTauD.getValue()) + common.getState(), + states[1] }, new TimeStampedPVCoordinates[] { - remote.shiftedBy(delta - tauD.getValue()).getPVCoordinates(), - local.shiftedBy(delta).getPVCoordinates() + common.getRemotePV().toTimeStampedPVCoordinates(), + common.getTransitPV().toTimeStampedPVCoordinates() }); - // Clock offsets - final Gradient dtr = getSatellites().get(1).getClockOffsetDriver().getValue(nbParams, indices, AbsoluteDate.ARBITRARY_EPOCH); - // Phase value final double cOverLambda = Constants.SPEED_OF_LIGHT / wavelength; - final Gradient ambiguity = ambiguityDriver.getValue(nbParams, indices, AbsoluteDate.ARBITRARY_EPOCH); - final Gradient phase = tauD.add(dtl).subtract(dtr).multiply(cOverLambda).add(ambiguity); + final Gradient ambiguity = ambiguityDriver.getValue(common.getTauD().getFreeParameters(), common.getIndices(), + common.getState().getDate()); + final Gradient phase = common.getTauD().add(common.getLocalOffset()).subtract(common.getRemoteOffset()). + multiply(cOverLambda). + add(ambiguity); estimatedPhase.setEstimatedValue(phase.getValue()); - // Range partial derivatives with respect to states + // Range first order derivatives with respect to states final double[] derivatives = phase.getGradient(); estimatedPhase.setStateDerivatives(0, Arrays.copyOfRange(derivatives, 0, 6)); estimatedPhase.setStateDerivatives(1, Arrays.copyOfRange(derivatives, 6, 12)); - // Set partial derivatives with respect to parameters + // Set first order derivatives with respect to parameters for (final ParameterDriver driver : getParametersDrivers()) { for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { - final Integer index = indices.get(span.getData()); + final Integer index = common.getIndices().get(span.getData()); if (index != null) { estimatedPhase.setParameterDerivatives(driver, span.getStart(), derivatives[index]); } diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/MelbourneWubbenaCombination.java b/src/main/java/org/orekit/estimation/measurements/gnss/MelbourneWubbenaCombination.java index e0e5780cef..bc3936353d 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/MelbourneWubbenaCombination.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/MelbourneWubbenaCombination.java @@ -60,7 +60,7 @@ public class MelbourneWubbenaCombination implements MeasurementCombination { /** Threshold for frequency comparison. */ - private static final double THRESHOLD = 1.0e-10; + private static final double THRESHOLD = 1.0e-4; /** Satellite system used for the combination. */ private final SatelliteSystem system; @@ -101,17 +101,18 @@ public CombinedObservationDataSet combine(final ObservationDataSet observations) if (isCombinationPossible) { // Combined value and frequency final double combinedValue = odWL.getValue() - odNL.getValue(); - final double combinedFrequency = odWL.getCombinedMHzFrequency(); + final double combinedFrequency = odWL.getCombinedFrequency(); // Used observation data to build the Melbourn-Wübbena measurement - final List usedData = new ArrayList(4); + final List usedData = new ArrayList<>(4); usedData.add(0, odWL.getUsedObservationData().get(0)); usedData.add(1, odWL.getUsedObservationData().get(1)); usedData.add(2, odNL.getUsedObservationData().get(0)); usedData.add(3, odNL.getUsedObservationData().get(1)); // Update the combined observation data list - combined.add(new CombinedObservationData(CombinationType.MELBOURNE_WUBBENA, + combined.add(new CombinedObservationData(combinedValue, combinedFrequency, + CombinationType.MELBOURNE_WUBBENA, MeasurementType.COMBINED_RANGE_PHASE, - combinedValue, combinedFrequency, usedData)); + usedData)); } } } @@ -143,8 +144,8 @@ private boolean isCombinationPossible(final CombinedObservationData odWL, final final double[] frequency = new double[4]; int j = 0; for (int i = 0; i < odWL.getUsedObservationData().size(); i++) { - frequency[j++] = odWL.getUsedObservationData().get(i).getObservationType().getFrequency(system).getMHzFrequency(); - frequency[j++] = odNL.getUsedObservationData().get(i).getObservationType().getFrequency(system).getMHzFrequency(); + frequency[j++] = odWL.getUsedObservationData().get(i).getObservationType().getFrequency(system).getFrequency(); + frequency[j++] = odNL.getUsedObservationData().get(i).getObservationType().getFrequency(system).getFrequency(); } // Verify if used frequencies are the same. // Possible numerical error is taken into account by using a threshold of acceptance diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/OnBoardCommonParametersWithDerivatives.java b/src/main/java/org/orekit/estimation/measurements/gnss/OnBoardCommonParametersWithDerivatives.java new file mode 100644 index 0000000000..a37adbb4f6 --- /dev/null +++ b/src/main/java/org/orekit/estimation/measurements/gnss/OnBoardCommonParametersWithDerivatives.java @@ -0,0 +1,109 @@ +/* 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.estimation.measurements.gnss; + +import org.hipparchus.analysis.differentiation.Gradient; +import org.orekit.estimation.measurements.CommonParametersWithDerivatives; +import org.orekit.propagation.SpacecraftState; +import org.orekit.utils.TimeStampedFieldPVCoordinates; + +import java.util.Map; + +/** Common intermediate parameters used to estimate measurements where receiver is a satellite. + * @author Luc Maisonobe + * @since 12.1 + */ +public class OnBoardCommonParametersWithDerivatives + extends CommonParametersWithDerivatives { + + /** Local clock offset. */ + private final Gradient localOffset; + + /** Local clock rate. */ + private final Gradient localRate; + + /** Remote clock offset. */ + private final Gradient remoteOffset; + + /** Remote clock rate. */ + private final Gradient remoteRate; + + /** Remote satellite position/velocity. */ + private final TimeStampedFieldPVCoordinates remotePV; + + /** Simple constructor. + * @param localState local spacecraft state + * @param indices derivatives indices map + * @param localOffset local clock offset + * @param localRate local clock rate + * @param remoteOffset remote clock offset + * @param remoteRate remote clock rate + * @param tauD downlink delay + * @param localPV local satellite position/velocity + * @param remotePV remote satellite position/velocity + */ + public OnBoardCommonParametersWithDerivatives(final SpacecraftState localState, + final Map indices, + final Gradient localOffset, final Gradient localRate, + final Gradient remoteOffset, final Gradient remoteRate, + final Gradient tauD, + final TimeStampedFieldPVCoordinates localPV, + final TimeStampedFieldPVCoordinates remotePV) { + super(localState, indices, tauD, localState, localPV); + this.localOffset = localOffset; + this.localRate = localRate; + this.remoteOffset = remoteOffset; + this.remoteRate = remoteRate; + this.remotePV = remotePV; + } + + /** Get local clock offset. + * @return local clock offset + */ + public Gradient getLocalOffset() { + return localOffset; + } + + /** Get local clock rate. + * @return local clock rate + */ + public Gradient getLocalRate() { + return localRate; + } + + /** Get remote clock offset. + * @return remote clock offset + */ + public Gradient getRemoteOffset() { + return remoteOffset; + } + + /** Get remote clock rate. + * @return remote clock rate + */ + public Gradient getRemoteRate() { + return remoteRate; + } + + /** Get remote satellite position/velocity. + * @return remote satellite position/velocity + */ + public TimeStampedFieldPVCoordinates getRemotePV() { + return remotePV; + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/OnBoardCommonParametersWithoutDerivatives.java b/src/main/java/org/orekit/estimation/measurements/gnss/OnBoardCommonParametersWithoutDerivatives.java new file mode 100644 index 0000000000..33d5f30c82 --- /dev/null +++ b/src/main/java/org/orekit/estimation/measurements/gnss/OnBoardCommonParametersWithoutDerivatives.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.estimation.measurements.gnss; + +import org.orekit.estimation.measurements.CommonParametersWithoutDerivatives; +import org.orekit.propagation.SpacecraftState; +import org.orekit.utils.TimeStampedPVCoordinates; + +/** Common intermediate parameters used to estimate measurements where receiver is a satellite. + * @author Luc Maisonobe + * @since 12.1 + */ +public class OnBoardCommonParametersWithoutDerivatives + extends CommonParametersWithoutDerivatives { + + /** Local clock offset. */ + private final double localOffset; + + /** Local clock rate. */ + private final double localRate; + + /** Remote clock offset. */ + private final double remoteOffset; + + /** Remote clock rate. */ + private final double remoteRate; + + /** Remote satellite position/velocity. */ + private final TimeStampedPVCoordinates remotePV; + + /** Simple constructor. + * @param localState local spacecraft state + * @param localOffset local clock offset + * @param localRate local clock rate + * @param remoteOffset remote clock offset + * @param remoteRate remote clock rate + * @param tauD downlink delay + * @param localPV local satellite position/velocity + * @param remotePV remote satellite position/velocity + */ + public OnBoardCommonParametersWithoutDerivatives(final SpacecraftState localState, + final double localOffset, final double localRate, + final double remoteOffset, final double remoteRate, + final double tauD, + final TimeStampedPVCoordinates localPV, + final TimeStampedPVCoordinates remotePV) { + super(localState, tauD, localState, localPV); + this.localOffset = localOffset; + this.localRate = localRate; + this.remoteOffset = remoteOffset; + this.remoteRate = remoteRate; + this.remotePV = remotePV; + } + + /** Get local clock offset. + * @return local clock offset + */ + public double getLocalOffset() { + return localOffset; + } + + /** Get local clock rate. + * @return local clock rate + */ + public double getLocalRate() { + return localRate; + } + + /** Get remote clock offset. + * @return remote clock offset + */ + public double getRemoteOffset() { + return remoteOffset; + } + + /** Get remote clock rate. + * @return remote clock rate + */ + public double getRemoteRate() { + return remoteRate; + } + + /** Get remote satellite position/velocity. + * @return remote satellite position/velocity + */ + public TimeStampedPVCoordinates getRemotePV() { + return remotePV; + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/OneWayGNSSPhase.java b/src/main/java/org/orekit/estimation/measurements/gnss/OneWayGNSSPhase.java index 36b4f14265..66069d554a 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/OneWayGNSSPhase.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/OneWayGNSSPhase.java @@ -17,23 +17,20 @@ package org.orekit.estimation.measurements.gnss; import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; +import java.util.IdentityHashMap; import java.util.Map; import org.hipparchus.analysis.differentiation.Gradient; -import org.orekit.estimation.measurements.AbstractMeasurement; import org.orekit.estimation.measurements.EstimatedMeasurement; import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.ObservableSatellite; +import org.orekit.estimation.measurements.QuadraticClockModel; import org.orekit.propagation.SpacecraftState; import org.orekit.time.AbsoluteDate; -import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.Constants; import org.orekit.utils.PVCoordinatesProvider; import org.orekit.utils.ParameterDriver; import org.orekit.utils.TimeSpanMap.Span; -import org.orekit.utils.TimeStampedFieldPVCoordinates; import org.orekit.utils.TimeStampedPVCoordinates; /** One-way GNSS phase measurement. @@ -55,22 +52,27 @@ * @author Bryan Cazabonne * @since 10.3 */ -public class OneWayGNSSPhase extends AbstractMeasurement { +public class OneWayGNSSPhase extends AbstractOneWayGNSSMeasurement { /** Type of the measurement. */ public static final String MEASUREMENT_TYPE = "OneWayGNSSPhase"; - /** Name for ambiguity driver. */ + /** Name for ambiguity driver. + * @deprecated as of 12.1 not used anymore + */ + @Deprecated public static final String AMBIGUITY_NAME = "ambiguity"; - /** Driver for ambiguity. */ - private final ParameterDriver ambiguityDriver; - - /** Emitting satellite. */ - private final PVCoordinatesProvider remote; + /** Temporary map to build remote provider names. + * @since 12.1 + * @deprecated this map is only a temporary hack for compatibility purposes + * it will be removed in Orekit 13.0 + */ + @Deprecated + private static final Map REMOTE_NAMES = new IdentityHashMap<>(); - /** Clock offset of the emitting satellite. */ - private final double dtRemote; + /** Driver for ambiguity. */ + private final AmbiguityDriver ambiguityDriver; /** Wavelength of the phase observed value [m]. */ private final double wavelength; @@ -84,26 +86,54 @@ public class OneWayGNSSPhase extends AbstractMeasurement { * @param sigma theoretical standard deviation * @param baseWeight base weight * @param local satellite which receives the signal and perform the measurement + * @deprecated as of 12.1, replaced by {@link #OneWayGNSSPhase(PVCoordinatesProvider, + * String, QuadraticClockModel, AbsoluteDate, double, double, double, double, + * ObservableSatellite, AmbiguityCache)} */ + @Deprecated public OneWayGNSSPhase(final PVCoordinatesProvider remote, final double dtRemote, final AbsoluteDate date, final double phase, final double wavelength, final double sigma, final double baseWeight, final ObservableSatellite local) { + this(remote, + REMOTE_NAMES.computeIfAbsent(remote, r -> "remote-" + REMOTE_NAMES.size()), + new QuadraticClockModel(date, dtRemote, 0.0, 0.0), + date, phase, wavelength, sigma, baseWeight, local, + AmbiguityCache.DEFAULT_CACHE); + } + + /** Simple constructor. + * @param remote provider for GNSS satellite which simply emits the signal + * @param remoteName name of the remote + * @param remoteClock clock offset of the GNSS satellite + * @param date date of the measurement + * @param phase observed value, in cycles + * @param wavelength phase observed value wavelength, in meters + * @param sigma theoretical standard deviation + * @param baseWeight base weight + * @param local satellite which receives the signal and perform the measurement + * @param cache from which ambiguity drive should come + * @since 12.1 + */ + public OneWayGNSSPhase(final PVCoordinatesProvider remote, + final String remoteName, + final QuadraticClockModel remoteClock, + final AbsoluteDate date, + final double phase, final double wavelength, final double sigma, + final double baseWeight, final ObservableSatellite local, + final AmbiguityCache cache) { // Call super constructor - super(date, phase, sigma, baseWeight, Collections.singletonList(local)); + super(remote, remoteClock, date, phase, sigma, baseWeight, local); // Initialize phase ambiguity driver - ambiguityDriver = new ParameterDriver(AMBIGUITY_NAME, 0.0, 1.0, - Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); + ambiguityDriver = cache.getAmbiguity(remoteName, local.getName(), wavelength); // The local satellite clock offset affects the measurement addParameterDriver(ambiguityDriver); addParameterDriver(local.getClockOffsetDriver()); // Initialise fields - this.dtRemote = dtRemote; - this.remote = remote; this.wavelength = wavelength; } @@ -117,7 +147,7 @@ public double getWavelength() { /** Get the driver for phase ambiguity. * @return the driver for phase ambiguity */ - public ParameterDriver getAmbiguityDriver() { + public AmbiguityDriver getAmbiguityDriver() { return ambiguityDriver; } @@ -127,37 +157,23 @@ protected EstimatedMeasurementBase theoreticalEvaluationWithout final int evaluation, final SpacecraftState[] states) { - // Coordinates of both satellites - final SpacecraftState localState = states[0]; - final TimeStampedPVCoordinates pvaLocal = localState.getPVCoordinates(); - final TimeStampedPVCoordinates pvaRemote = remote.getPVCoordinates(getDate(), localState.getFrame()); - - // Downlink delay - final double dtLocal = getSatellites().get(0).getClockOffsetDriver().getValue(localState.getDate()); - final AbsoluteDate arrivalDate = getDate().shiftedBy(-dtLocal); - - final TimeStampedPVCoordinates s1Downlink = - pvaLocal.shiftedBy(arrivalDate.durationFrom(pvaLocal.getDate())); - final double tauD = signalTimeOfFlight(pvaRemote, s1Downlink.getPosition(), arrivalDate); - - // Transit state - final double delta = getDate().durationFrom(pvaRemote.getDate()); - final double deltaMTauD = delta - tauD; + final OnBoardCommonParametersWithoutDerivatives common = computeCommonParametersWithout(states, false); // prepare the evaluation final EstimatedMeasurementBase estimatedPhase = new EstimatedMeasurementBase<>(this, iteration, evaluation, new SpacecraftState[] { - localState.shiftedBy(deltaMTauD) + common.getState() }, new TimeStampedPVCoordinates[] { - pvaRemote.shiftedBy(delta - tauD), - localState.shiftedBy(delta).getPVCoordinates() + common.getRemotePV(), + common.getTransitPV() }); // Phase value - final double cOverLambda = Constants.SPEED_OF_LIGHT / wavelength; - final double ambiguity = ambiguityDriver.getValue(localState.getDate()); - final double phase = (tauD + dtLocal - dtRemote) * cOverLambda + ambiguity; + final double cOverLambda = Constants.SPEED_OF_LIGHT / wavelength; + final double ambiguity = ambiguityDriver.getValue(common.getState().getDate()); + final double phase = (common.getTauD() + common.getLocalOffset() - common.getRemoteOffset()) * cOverLambda + + ambiguity; // Set value of the estimated measurement estimatedPhase.setEstimatedValue(phase); @@ -173,64 +189,36 @@ protected EstimatedMeasurement theoreticalEvaluation(final int final int evaluation, final SpacecraftState[] states) { - // Phase derivatives are computed with respect to spacecrafts states in inertial frame - // Parameters: - // - 0..2 - Position of the receiver satellite in inertial frame - // - 3..5 - Velocity of the receiver satellite in inertial frame - // - 6..n - Measurement parameters: ambiguity and clock offset - int nbEstimatedParamsPhase = 6; - final Map parameterIndicesPhase = new HashMap<>(); - for (ParameterDriver phaseMeasurementDriver : getParametersDrivers()) { - if (phaseMeasurementDriver.isSelected()) { - for (Span span = phaseMeasurementDriver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { - parameterIndicesPhase.put(span.getData(), nbEstimatedParamsPhase++); - } - } - } - - // Coordinates of both satellites - final SpacecraftState localState = states[0]; - final TimeStampedFieldPVCoordinates pvaLocal = getCoordinates(localState, 0, nbEstimatedParamsPhase); - final TimeStampedPVCoordinates pvaRemote = remote.getPVCoordinates(getDate(), localState.getFrame()); - - // Downlink delay - final Gradient dtLocal = getSatellites().get(0).getClockOffsetDriver().getValue(nbEstimatedParamsPhase, parameterIndicesPhase, localState.getDate()); - final FieldAbsoluteDate arrivalDate = new FieldAbsoluteDate<>(getDate(), dtLocal.negate()); - - final TimeStampedFieldPVCoordinates s1Downlink = - pvaLocal.shiftedBy(arrivalDate.durationFrom(pvaLocal.getDate())); - final Gradient tauD = signalTimeOfFlight(new TimeStampedFieldPVCoordinates<>(pvaRemote.getDate(), dtLocal.getField().getOne(), pvaRemote), - s1Downlink.getPosition(), arrivalDate); - - // Transit state - final double delta = getDate().durationFrom(pvaRemote.getDate()); - final Gradient deltaMTauD = tauD.negate().add(delta); + final OnBoardCommonParametersWithDerivatives common = computeCommonParametersWith(states, false); // prepare the evaluation final EstimatedMeasurement estimatedPhase = new EstimatedMeasurement<>(this, iteration, evaluation, new SpacecraftState[] { - localState.shiftedBy(deltaMTauD.getValue()) + common.getState() }, new TimeStampedPVCoordinates[] { - pvaRemote.shiftedBy(delta - tauD.getValue()), - localState.shiftedBy(delta).getPVCoordinates() + common.getRemotePV().toTimeStampedPVCoordinates(), + common.getTransitPV().toTimeStampedPVCoordinates() }); // Phase value final double cOverLambda = Constants.SPEED_OF_LIGHT / wavelength; - final Gradient ambiguity = ambiguityDriver.getValue(nbEstimatedParamsPhase, parameterIndicesPhase, localState.getDate()); - final Gradient phase = tauD.add(dtLocal).subtract(dtRemote).multiply(cOverLambda).add(ambiguity); + final Gradient ambiguity = ambiguityDriver.getValue(common.getTauD().getFreeParameters(), common.getIndices(), + common.getState().getDate()); + final Gradient phase = common.getTauD().add(common.getLocalOffset()).subtract(common.getRemoteOffset()). + multiply(cOverLambda). + add(ambiguity); final double[] phaseDerivatives = phase.getGradient(); - // Set value and state derivatives of the estimated measurement + // Set value and state first order derivatives of the estimated measurement estimatedPhase.setEstimatedValue(phase.getValue()); estimatedPhase.setStateDerivatives(0, Arrays.copyOfRange(phaseDerivatives, 0, 6)); - // Set partial derivatives with respect to parameters + // Set first order derivatives with respect to parameters for (final ParameterDriver phaseMeasurementDriver : getParametersDrivers()) { for (Span span = phaseMeasurementDriver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { - final Integer index = parameterIndicesPhase.get(span.getData()); + final Integer index = common.getIndices().get(span.getData()); if (index != null) { estimatedPhase.setParameterDerivatives(phaseMeasurementDriver, span.getStart(), phaseDerivatives[index]); } diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/OneWayGNSSRange.java b/src/main/java/org/orekit/estimation/measurements/gnss/OneWayGNSSRange.java index 6f6a141fb6..ce445e4b95 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/OneWayGNSSRange.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/OneWayGNSSRange.java @@ -17,24 +17,19 @@ package org.orekit.estimation.measurements.gnss; import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; import org.hipparchus.analysis.differentiation.Gradient; -import org.orekit.estimation.measurements.AbstractMeasurement; import org.orekit.estimation.measurements.EstimatedMeasurement; import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.InterSatellitesRange; import org.orekit.estimation.measurements.ObservableSatellite; +import org.orekit.estimation.measurements.QuadraticClockModel; import org.orekit.propagation.SpacecraftState; import org.orekit.time.AbsoluteDate; -import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.Constants; import org.orekit.utils.PVCoordinatesProvider; import org.orekit.utils.ParameterDriver; import org.orekit.utils.TimeSpanMap.Span; -import org.orekit.utils.TimeStampedFieldPVCoordinates; import org.orekit.utils.TimeStampedPVCoordinates; /** One-way GNSS range measurement. @@ -56,17 +51,11 @@ * @author Bryan Cazabonne * @since 10.3 */ -public class OneWayGNSSRange extends AbstractMeasurement { +public class OneWayGNSSRange extends AbstractOneWayGNSSMeasurement { /** Type of the measurement. */ public static final String MEASUREMENT_TYPE = "OneWayGNSSRange"; - /** Emitting satellite. */ - private final PVCoordinatesProvider remote; - - /** Clock offset of the emitting satellite. */ - private final double dtRemote; - /** Simple constructor. * @param remote provider for GNSS satellite which simply emits the signal * @param dtRemote clock offset of the GNSS satellite, in seconds @@ -81,13 +70,26 @@ public OneWayGNSSRange(final PVCoordinatesProvider remote, final AbsoluteDate date, final double range, final double sigma, final double baseWeight, final ObservableSatellite local) { + this(remote, new QuadraticClockModel(date, dtRemote, 0.0, 0.0), date, range, sigma, baseWeight, local); + } + + /** Simple constructor. + * @param remote provider for GNSS satellite which simply emits the signal + * @param remoteClock clock offset of the GNSS satellite + * @param date date of the measurement + * @param range observed value + * @param sigma theoretical standard deviation + * @param baseWeight base weight + * @param local satellite which receives the signal and perform the measurement + * @since 12.1 + */ + public OneWayGNSSRange(final PVCoordinatesProvider remote, + final QuadraticClockModel remoteClock, + final AbsoluteDate date, + final double range, final double sigma, + final double baseWeight, final ObservableSatellite local) { // Call super constructor - super(date, range, sigma, baseWeight, Collections.singletonList(local)); - // The local satellite clock offset affects the measurement - addParameterDriver(local.getClockOffsetDriver()); - // Initialise fields - this.dtRemote = dtRemote; - this.remote = remote; + super(remote, remoteClock, date, range, sigma, baseWeight, local); } /** {@inheritDoc} */ @@ -96,34 +98,22 @@ protected EstimatedMeasurementBase theoreticalEvaluationWithout final int evaluation, final SpacecraftState[] states) { - // Coordinates of both satellites in local satellite frame - final SpacecraftState localState = states[0]; - final TimeStampedPVCoordinates pvaLocal = localState.getPVCoordinates(); - final TimeStampedPVCoordinates pvaRemote = remote.getPVCoordinates(getDate(), localState.getFrame()); - // Downlink delay - final double dtLocal = getSatellites().get(0).getClockOffsetDriver().getValue(localState.getDate()); - final AbsoluteDate arrivalDate = getDate().shiftedBy(-dtLocal); - - final TimeStampedPVCoordinates s1Downlink = pvaLocal.shiftedBy(arrivalDate.durationFrom(pvaLocal.getDate())); - final double tauD = signalTimeOfFlight(pvaRemote, s1Downlink.getPosition(), arrivalDate); - - // Transit state - final double delta = getDate().durationFrom(pvaRemote.getDate()); - final double deltaMTauD = delta - tauD; + final OnBoardCommonParametersWithoutDerivatives common = computeCommonParametersWithout(states, false); // Estimated measurement final EstimatedMeasurementBase estimatedRange = new EstimatedMeasurementBase<>(this, iteration, evaluation, new SpacecraftState[] { - localState.shiftedBy(deltaMTauD) + common.getState() }, new TimeStampedPVCoordinates[] { - pvaRemote.shiftedBy(delta - tauD), - localState.shiftedBy(delta).getPVCoordinates() + common.getRemotePV(), + common.getTransitPV() }); // Range value - final double range = (tauD + dtLocal - dtRemote) * Constants.SPEED_OF_LIGHT; + final double range = (common.getTauD() + common.getLocalOffset() - common.getRemoteOffset()) * + Constants.SPEED_OF_LIGHT; // Set value of the estimated measurement estimatedRange.setEstimatedValue(range); @@ -139,61 +129,32 @@ protected EstimatedMeasurement theoreticalEvaluation(final int final int evaluation, final SpacecraftState[] states) { - // Range derivatives are computed with respect to spacecraft state in inertial frame - // Parameters: - // - 0..2 - Position of the spacecraft in inertial frame - // - 3..5 - Velocity of the spacecraft in inertial frame - // - 6..n - measurements parameters (clock offset, etc) - int nbEstimatedParams = 6; - final Map parameterIndices = new HashMap<>(); - for (ParameterDriver measurementDriver : getParametersDrivers()) { - if (measurementDriver.isSelected()) { - for (Span span = measurementDriver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { - parameterIndices.put(span.getData(), nbEstimatedParams++); - } - } - } - - // Coordinates of both satellites in local satellite frame - final SpacecraftState localState = states[0]; - final TimeStampedFieldPVCoordinates pvaLocal = getCoordinates(localState, 0, nbEstimatedParams); - final TimeStampedPVCoordinates pvaRemote = remote.getPVCoordinates(getDate(), localState.getFrame()); - - // Downlink delay - final Gradient dtLocal = getSatellites().get(0).getClockOffsetDriver().getValue(nbEstimatedParams, parameterIndices, localState.getDate()); - final FieldAbsoluteDate arrivalDate = new FieldAbsoluteDate<>(getDate(), dtLocal.negate()); - - final TimeStampedFieldPVCoordinates s1Downlink = pvaLocal.shiftedBy(arrivalDate.durationFrom(pvaLocal.getDate())); - final Gradient tauD = signalTimeOfFlight(new TimeStampedFieldPVCoordinates<>(pvaRemote.getDate(), dtLocal.getField().getOne(), pvaRemote), - s1Downlink.getPosition(), arrivalDate); - - // Transit state - final double delta = getDate().durationFrom(pvaRemote.getDate()); - final Gradient deltaMTauD = tauD.negate().add(delta); + final OnBoardCommonParametersWithDerivatives common = computeCommonParametersWith(states, false); // Estimated measurement final EstimatedMeasurement estimatedRange = new EstimatedMeasurement<>(this, iteration, evaluation, new SpacecraftState[] { - localState.shiftedBy(deltaMTauD.getValue()) + common.getState() }, new TimeStampedPVCoordinates[] { - pvaRemote.shiftedBy(delta - tauD.getValue()), - localState.shiftedBy(delta).getPVCoordinates() + common.getRemotePV().toTimeStampedPVCoordinates(), + common.getTransitPV().toTimeStampedPVCoordinates() }); // Range value - final Gradient range = tauD.add(dtLocal).subtract(dtRemote).multiply(Constants.SPEED_OF_LIGHT); + final Gradient range = common.getTauD().add(common.getLocalOffset()).subtract(common.getRemoteOffset()). + multiply(Constants.SPEED_OF_LIGHT); final double[] rangeDerivatives = range.getGradient(); - // Set value and state derivatives of the estimated measurement + // Set value and state first order derivatives of the estimated measurement estimatedRange.setEstimatedValue(range.getValue()); estimatedRange.setStateDerivatives(0, Arrays.copyOfRange(rangeDerivatives, 0, 6)); - // Set partial derivatives with respect to parameters + // Set first order derivatives with respect to parameters for (final ParameterDriver measurementDriver : getParametersDrivers()) { for (Span span = measurementDriver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { - final Integer index = parameterIndices.get(span.getData()); + final Integer index = common.getIndices().get(span.getData()); if (index != null) { estimatedRange.setParameterDerivatives(measurementDriver, span.getStart(), rangeDerivatives[index]); } diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/OneWayGNSSRangeRate.java b/src/main/java/org/orekit/estimation/measurements/gnss/OneWayGNSSRangeRate.java new file mode 100644 index 0000000000..3de3df4180 --- /dev/null +++ b/src/main/java/org/orekit/estimation/measurements/gnss/OneWayGNSSRangeRate.java @@ -0,0 +1,174 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.estimation.measurements.gnss; + +import java.util.Arrays; + +import org.hipparchus.analysis.differentiation.Gradient; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; +import org.orekit.estimation.measurements.ObservableSatellite; +import org.orekit.estimation.measurements.QuadraticClockModel; +import org.orekit.propagation.SpacecraftState; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.Constants; +import org.orekit.utils.FieldPVCoordinates; +import org.orekit.utils.PVCoordinates; +import org.orekit.utils.PVCoordinatesProvider; +import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TimeSpanMap.Span; +import org.orekit.utils.TimeStampedPVCoordinates; + +/** One-way GNSS range rate measurement. + *

    + * This class can be used in precise orbit determination applications + * for modeling a range rate measurement between a GNSS satellite (emitter) + * and a LEO satellite (receiver). + *

    + * The one-way GNSS range rate measurement assumes knowledge of the orbit and + * the clock offset of the emitting GNSS satellite. For instance, it is + * possible to use a SP3 file or a GNSS navigation message to recover + * the satellite's orbit and clock. + *

    + * This class is very similar to {@link InterSatellitesOneWayRangeRate} measurement + * class. However, using the one-way GNSS range measurement, the orbit and clock + * of the emitting GNSS satellite are NOT estimated simultaneously with + * LEO satellite coordinates. + * + * @author Luc Maisonobe + * @since 12.1 + */ +public class OneWayGNSSRangeRate extends AbstractOneWayGNSSMeasurement { + + /** Type of the measurement. */ + public static final String MEASUREMENT_TYPE = "OneWayGNSSRangeRate"; + + /** Simple constructor. + * @param remote provider for GNSS satellite which simply emits the signal + * @param dtRemote clock offset of the GNSS satellite, in seconds + * @param date date of the measurement + * @param rangeRate observed value + * @param sigma theoretical standard deviation + * @param baseWeight base weight + * @param local satellite which receives the signal and perform the measurement + */ + public OneWayGNSSRangeRate(final PVCoordinatesProvider remote, + final double dtRemote, + final AbsoluteDate date, + final double rangeRate, final double sigma, + final double baseWeight, final ObservableSatellite local) { + this(remote, new QuadraticClockModel(date, dtRemote, 0.0, 0.0), date, rangeRate, sigma, baseWeight, local); + } + + /** Simple constructor. + * @param remote provider for GNSS satellite which simply emits the signal + * @param remoteClock clock offset of the GNSS satellite + * @param date date of the measurement + * @param rangeRate observed value + * @param sigma theoretical standard deviation + * @param baseWeight base weight + * @param local satellite which receives the signal and perform the measurement + * @since 12.1 + */ + public OneWayGNSSRangeRate(final PVCoordinatesProvider remote, + final QuadraticClockModel remoteClock, + final AbsoluteDate date, + final double rangeRate, final double sigma, + final double baseWeight, final ObservableSatellite local) { + // Call super constructor + super(remote, remoteClock, date, rangeRate, sigma, baseWeight, local); + } + + /** {@inheritDoc} */ + @Override + protected EstimatedMeasurementBase theoreticalEvaluationWithoutDerivatives(final int iteration, + final int evaluation, + final SpacecraftState[] states) { + + + final OnBoardCommonParametersWithoutDerivatives common = computeCommonParametersWithout(states, false); + + // Estimated measurement + final EstimatedMeasurementBase estimatedRangeRate = + new EstimatedMeasurementBase<>(this, iteration, evaluation, + new SpacecraftState[] { + common.getState() + }, new TimeStampedPVCoordinates[] { + common.getRemotePV(), + common.getTransitPV() + }); + + // Range rate value + final PVCoordinates delta = new PVCoordinates(common.getRemotePV(), common.getTransitPV()); + final double rangeRate = Vector3D.dotProduct(delta.getVelocity(), delta.getPosition().normalize()) + + Constants.SPEED_OF_LIGHT * (common.getLocalRate() - common.getRemoteRate()); + + // Set value of the estimated measurement + estimatedRangeRate.setEstimatedValue(rangeRate); + + // Return the estimated measurement + return estimatedRangeRate; + + } + + /** {@inheritDoc} */ + @Override + protected EstimatedMeasurement theoreticalEvaluation(final int iteration, + final int evaluation, + final SpacecraftState[] states) { + + final OnBoardCommonParametersWithDerivatives common = computeCommonParametersWith(states, false); + + // Estimated measurement + final EstimatedMeasurement estimatedRangeRate = + new EstimatedMeasurement<>(this, iteration, evaluation, + new SpacecraftState[] { + common.getState() + }, new TimeStampedPVCoordinates[] { + common.getRemotePV().toTimeStampedPVCoordinates(), + common.getTransitPV().toTimeStampedPVCoordinates() + }); + + // Range rate value + final FieldPVCoordinates delta = new FieldPVCoordinates<>(common.getRemotePV(), common.getTransitPV()); + final Gradient rangeRate = FieldVector3D.dotProduct(delta.getVelocity(), delta.getPosition().normalize()). + add(common.getLocalRate().subtract(common.getRemoteRate()).multiply(Constants.SPEED_OF_LIGHT)); + final double[] rangeRateDerivatives = rangeRate.getGradient(); + + // Set value and state first order derivatives of the estimated measurement + estimatedRangeRate.setEstimatedValue(rangeRate.getValue()); + estimatedRangeRate.setStateDerivatives(0, Arrays.copyOfRange(rangeRateDerivatives, 0, 6)); + + // Set first order derivatives with respect to parameters + for (final ParameterDriver measurementDriver : getParametersDrivers()) { + for (Span span = measurementDriver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + + final Integer index = common.getIndices().get(span.getData()); + if (index != null) { + estimatedRangeRate.setParameterDerivatives(measurementDriver, span.getStart(), rangeRateDerivatives[index]); + } + } + } + + // Return the estimated measurement + return estimatedRangeRate; + + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/Phase.java b/src/main/java/org/orekit/estimation/measurements/gnss/Phase.java index 720e954c8b..88988285ec 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/Phase.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/Phase.java @@ -53,11 +53,14 @@ public class Phase extends GroundReceiverMeasurement { /** Type of the measurement. */ public static final String MEASUREMENT_TYPE = "Phase"; - /** Name for ambiguity driver. */ + /** Name for ambiguity driver. + * @deprecated as of 12.1 not used anymore + */ + @Deprecated public static final String AMBIGUITY_NAME = "ambiguity"; /** Driver for ambiguity. */ - private final ParameterDriver ambiguityDriver; + private final AmbiguityDriver ambiguityDriver; /** Wavelength of the phase observed value [m]. */ private final double wavelength; @@ -71,14 +74,37 @@ public class Phase extends GroundReceiverMeasurement { * @param baseWeight base weight * @param satellite satellite related to this measurement * @since 9.3 + * @deprecated as of 12.1, replaced by {@link #Phase(GroundStation, + * AbsoluteDate, double, double, double, double, ObservableSatellite, + * AmbiguityCache)} */ + @Deprecated public Phase(final GroundStation station, final AbsoluteDate date, final double phase, final double wavelength, final double sigma, final double baseWeight, final ObservableSatellite satellite) { + this(station, date, phase, wavelength, sigma, baseWeight, satellite, + AmbiguityCache.DEFAULT_CACHE); + } + + /** Simple constructor. + * @param station ground station from which measurement is performed + * @param date date of the measurement + * @param phase observed value (cycles) + * @param wavelength phase observed value wavelength (m) + * @param sigma theoretical standard deviation + * @param baseWeight base weight + * @param satellite satellite related to this measurement + * @param cache from which ambiguity drive should come + * @since 12.1 + */ + public Phase(final GroundStation station, final AbsoluteDate date, + final double phase, final double wavelength, final double sigma, + final double baseWeight, final ObservableSatellite satellite, + final AmbiguityCache cache) { super(station, false, date, phase, sigma, baseWeight, satellite); - ambiguityDriver = new ParameterDriver(AMBIGUITY_NAME, - 0.0, 1.0, - Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); + ambiguityDriver = cache.getAmbiguity(satellite.getName(), + station.getBaseFrame().getName(), + wavelength); addParameterDriver(ambiguityDriver); this.wavelength = wavelength; } @@ -94,7 +120,7 @@ public double getWavelength() { * @return the driver for phase ambiguity * @since 10.3 */ - public ParameterDriver getAmbiguityDriver() { + public AmbiguityDriver getAmbiguityDriver() { return ambiguityDriver; } @@ -153,13 +179,13 @@ protected EstimatedMeasurement theoreticalEvaluation(final int iteration, // prepare the evaluation final EstimatedMeasurement estimated = - new EstimatedMeasurement(this, iteration, evaluation, - new SpacecraftState[] { - common.getTransitState() - }, new TimeStampedPVCoordinates[] { - common.getTransitPV().toTimeStampedPVCoordinates(), - common.getStationDownlink().toTimeStampedPVCoordinates() - }); + new EstimatedMeasurement<>(this, iteration, evaluation, + new SpacecraftState[] { + common.getTransitState() + }, new TimeStampedPVCoordinates[] { + common.getTransitPV().toTimeStampedPVCoordinates(), + common.getStationDownlink().toTimeStampedPVCoordinates() + }); // Clock offsets final ObservableSatellite satellite = getSatellites().get(0); @@ -173,12 +199,11 @@ protected EstimatedMeasurement theoreticalEvaluation(final int iteration, estimated.setEstimatedValue(phase.getValue()); - // Phase partial derivatives with respect to state + // Phase first order derivatives with respect to state final double[] derivatives = phase.getGradient(); estimated.setStateDerivatives(0, Arrays.copyOfRange(derivatives, 0, 6)); - // set partial derivatives with respect to parameters - // (beware element at index 0 is the value, not a derivative) + // Set first order derivatives with respect to parameters for (final ParameterDriver driver : getParametersDrivers()) { for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/PhaseBuilder.java b/src/main/java/org/orekit/estimation/measurements/gnss/PhaseBuilder.java index b066d97267..806bf3cc1b 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/PhaseBuilder.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/PhaseBuilder.java @@ -35,6 +35,11 @@ */ public class PhaseBuilder extends AbstractMeasurementBuilder { + /** Cache for ambiguities. + * @since 12.1 + */ + private final AmbiguityCache cache; + /** Ground station from which measurement is performed. */ private final GroundStation station; @@ -53,15 +58,39 @@ public class PhaseBuilder extends AbstractMeasurementBuilder { * @param sigma theoretical standard deviation * @param baseWeight base weight * @param satellite satellite related to this builder + * @deprecated as of 12.1, replaced by {@link #PhaseBuilder(CorrelatedRandomVectorGenerator, + * GroundStation, double, double, double, ObservableSatellite, + * AmbiguityCache)} */ + @Deprecated public PhaseBuilder(final CorrelatedRandomVectorGenerator noiseSource, final GroundStation station, final double wavelength, final double sigma, final double baseWeight, final ObservableSatellite satellite) { + this(noiseSource, station, wavelength, sigma, baseWeight, satellite, + AmbiguityCache.DEFAULT_CACHE); + } + + /** Simple constructor. + * @param noiseSource noise source, may be null for generating perfect measurements + * @param station ground station from which measurement is performed + * @param wavelength phase observed value wavelength (m) + * @param sigma theoretical standard deviation + * @param baseWeight base weight + * @param satellite satellite related to this builder + * @param cache from which ambiguity drive should come + * @since 12.1 + */ + public PhaseBuilder(final CorrelatedRandomVectorGenerator noiseSource, + final GroundStation station, final double wavelength, + final double sigma, final double baseWeight, + final ObservableSatellite satellite, + final AmbiguityCache cache) { super(noiseSource, sigma, baseWeight, satellite); this.station = station; this.wavelength = wavelength; this.satellite = satellite; + this.cache = cache; } /** {@inheritDoc} */ @@ -73,7 +102,8 @@ public Phase build(final AbsoluteDate date, final Map modifier : getModifiers()) { dummy.addModifier(modifier); } @@ -88,7 +118,7 @@ public Phase build(final AbsoluteDate date, final Map modifier : getModifiers()) { measurement.addModifier(modifier); } diff --git a/src/main/java/org/orekit/estimation/measurements/gnss/PhaseMinusCodeCycleSlipDetector.java b/src/main/java/org/orekit/estimation/measurements/gnss/PhaseMinusCodeCycleSlipDetector.java index 92133c8654..4b7bbf1d8c 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/PhaseMinusCodeCycleSlipDetector.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/PhaseMinusCodeCycleSlipDetector.java @@ -29,7 +29,6 @@ import org.orekit.gnss.MeasurementType; import org.orekit.gnss.SatelliteSystem; import org.orekit.time.AbsoluteDate; -import org.orekit.utils.Constants; /** * Phase minus code cycle slip detectors. @@ -50,9 +49,6 @@ */ public class PhaseMinusCodeCycleSlipDetector extends AbstractCycleSlipDetector { - /** Mega Hertz to Hertz conversion factor. */ - private static final double MHZ_TO_HZ = 1.0e6; - /** Order of the polynomial used for fitting. */ private final int order; @@ -101,10 +97,9 @@ protected void manageData(final ObservationDataSet observation) { // Loop on range measurement for (final ObservationData pseudoRange : pseudoRanges) { // Change unit of phase measurement - final double frequency = phase.getObservationType().getFrequency(system).getMHzFrequency() * MHZ_TO_HZ; - final double cOverF = Constants.SPEED_OF_LIGHT / frequency; + final double wavelength = phase.getObservationType().getFrequency(system).getWavelength(); final ObservationData phaseInMeters = new ObservationData(phase.getObservationType(), - cOverF * phase.getValue(), + wavelength * phase.getValue(), phase.getLossOfLockIndicator(), phase.getSignalStrength()); 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 f8fa224a46..72a4dba4a7 100644 --- a/src/main/java/org/orekit/estimation/measurements/gnss/WindUpFactory.java +++ b/src/main/java/org/orekit/estimation/measurements/gnss/WindUpFactory.java @@ -51,30 +51,15 @@ public WindUpFactory() { public WindUp getWindUp(final SatelliteSystem system, final int prnNumber, final Dipole emitterDipole, final String receiverName) { // select satellite system - Map> systemModifiers = modifiers.get(system); - if (systemModifiers == null) { - // build a new map for this satellite system - systemModifiers = new HashMap<>(); - modifiers.put(system, systemModifiers); - } + final Map> systemModifiers = + modifiers.computeIfAbsent(system, s -> new HashMap<>()); // select satellite - Map satelliteModifiers = systemModifiers.get(prnNumber); - if (satelliteModifiers == null) { - // build a new map for this satellite - satelliteModifiers = new HashMap<>(); - systemModifiers.put(prnNumber, satelliteModifiers); - } + final Map satelliteModifiers = + systemModifiers.computeIfAbsent(prnNumber, n -> new HashMap<>()); // select receiver - WindUp receiverModifier = satelliteModifiers.get(receiverName); - if (receiverModifier == null) { - // build a new wind-up modifier - receiverModifier = new WindUp(emitterDipole); - satelliteModifiers.put(receiverName, receiverModifier); - } - - return receiverModifier; + return satelliteModifiers.computeIfAbsent(receiverName, r -> new WindUp(emitterDipole)); } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/AberrationModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/AberrationModifier.java index cf05eda927..488f12d8af 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/AberrationModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/AberrationModifier.java @@ -352,7 +352,7 @@ public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { final Gradient rightAscension = baseRightAscension.add(twoPiWrap); // New estimated values - estimated.setEstimatedValue(rightAscension.getValue(), naturalRaDec[1].getValue()); + estimated.modifyEstimatedValue(this, rightAscension.getValue(), naturalRaDec[1].getValue()); // Derivatives (only parameter, no state) final double[] raDerivatives = rightAscension.getGradient(); diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/AbstractAmbiguityModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/AbstractAmbiguityModifier.java index 3cec597178..2de8c958a7 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/AbstractAmbiguityModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/AbstractAmbiguityModifier.java @@ -22,6 +22,8 @@ import org.hipparchus.util.FastMath; import org.orekit.estimation.measurements.EstimatedMeasurement; import org.orekit.estimation.measurements.EstimatedMeasurementBase; +import org.orekit.estimation.measurements.EstimationModifier; +import org.orekit.estimation.measurements.ObservedMeasurement; import org.orekit.utils.ParameterDriver; import org.orekit.utils.TimeSpanMap.Span; @@ -30,7 +32,12 @@ * @author Bryan Cazabonne * @author Luc Maisonobe * @since 10.3 + * @deprecated as of 12.1 ambiguity is managed directly by raw measurements + * {@link org.orekit.estimation.measurements.gnss.Phase}, + * {@link org.orekit.estimation.measurements.gnss.OneWayGNSSPhase} + * and {@link org.orekit.estimation.measurements.gnss.InterSatellitesPhase} */ +@Deprecated public class AbstractAmbiguityModifier { /** Ambiguity scale factor. @@ -61,21 +68,29 @@ protected List getDrivers() { } /** Modify measurement. + * @param type of the measurements + * @param modifier applied modifier * @param estimated measurement to modify + * @since 12.1 */ - protected void doModifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { + protected > void doModifyWithoutDerivatives(final EstimationModifier modifier, + final EstimatedMeasurementBase estimated) { // Apply the ambiguity to the measurement value for (Span span = ambiguity.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { final double[] value = estimated.getEstimatedValue(); value[0] += ambiguity.getValue(span.getStart()); - estimated.setEstimatedValue(value); + estimated.modifyEstimatedValue(modifier, value); } } /** Modify measurement. + * @param type of the measurements + * @param modifier applied modifier * @param estimated measurement to modify + * @since 12.1 */ - protected void doModify(final EstimatedMeasurement estimated) { + protected > void doModify(final EstimationModifier modifier, + final EstimatedMeasurement estimated) { // apply the ambiguity to the measurement derivatives for (Span span = ambiguity.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { @@ -86,7 +101,7 @@ protected void doModify(final EstimatedMeasurement estimated) { } // apply the ambiguity to the measurement value - doModifyWithoutDerivatives(estimated); + doModifyWithoutDerivatives(modifier, estimated); } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/AbstractRelativisticClockOnBoardRangeRateModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/AbstractRelativisticClockOnBoardRangeRateModifier.java new file mode 100644 index 0000000000..5c2980c97e --- /dev/null +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/AbstractRelativisticClockOnBoardRangeRateModifier.java @@ -0,0 +1,90 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.estimation.measurements.modifiers; + +import org.orekit.estimation.measurements.EstimatedMeasurementBase; +import org.orekit.estimation.measurements.EstimationModifier; +import org.orekit.estimation.measurements.ObservedMeasurement; +import org.orekit.utils.Constants; +import org.orekit.utils.ParameterDriver; + +import java.util.Collections; +import java.util.List; + +/** Class modifying theoretical range-rate measurement with relativistic frequency deviation. + *

    + * Relativistic clock correction is caused by the motion of the satellite as well as + * the change in the gravitational potential + *

    + * @param type of the measurement + * @author Luc Maisonobe + * @since 12.1 + * + * @see "Teunissen, Peter, and Oliver Montenbruck, eds. Springer handbook of global navigation + * satellite systems. Chapter 19.2. Springer, 2017." + */ +public abstract class AbstractRelativisticClockOnBoardRangeRateModifier> + extends AbstractRelativisticClockModifier implements EstimationModifier { + + /** Gravitational constant. */ + private final double gm; + + /** Simple constructor. + * @param gm gravitational constant for main body in signal path vicinity. + */ + public AbstractRelativisticClockOnBoardRangeRateModifier(final double gm) { + super(); + this.gm = gm; + } + + /** Get gravitational constant for main body in signal path vicinity. + * @return gravitational constant for main body in signal path vicinity + */ + protected double getGm() { + return gm; + } + + /** {@inheritDoc} */ + @Override + public List getParametersDrivers() { + return Collections.emptyList(); + } + + /** Apply relativistic frequency deviation. + * @param estimated estimated measurement to modify + * @param aLocal semi major axis or local (receiver) satellite + * @param rLocal distance of local (receiver) satellite to central body center + * @param aRemote semi major axis or remote (transmitter) satellite + * @param rRemote distance of remote (transmitter) satellite to central body center + */ + protected void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated, + final double aLocal, final double rLocal, + final double aRemote, final double rRemote) { + + // compute relativistic frequency deviation + final double factor = -gm * getScaleFactor(); + final double dfLocal = factor * (1.0 / aLocal - 1.0 / rLocal); + final double dfRemote = factor * (1.0 / aRemote - 1.0 / rRemote); + + // Update estimated value taking into account the relativistic effect. + final double[] newValue = estimated.getEstimatedValue().clone(); + newValue[0] = newValue[0] + (dfLocal - dfRemote) * Constants.SPEED_OF_LIGHT; + estimated.modifyEstimatedValue(this, newValue); + + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/AbstractShapiroBaseModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/AbstractShapiroBaseModifier.java index 3e8eb0501a..07f6296850 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/AbstractShapiroBaseModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/AbstractShapiroBaseModifier.java @@ -19,6 +19,8 @@ import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; import org.orekit.estimation.measurements.EstimatedMeasurementBase; +import org.orekit.estimation.measurements.EstimationModifier; +import org.orekit.estimation.measurements.ObservedMeasurement; import org.orekit.utils.Constants; import org.orekit.utils.TimeStampedPVCoordinates; @@ -26,7 +28,6 @@ *

    * Shapiro time delay is a relativistic effect due to gravity. *

    - * * @author Luc Maisonobe * @since 10.0 */ @@ -43,9 +44,13 @@ public AbstractShapiroBaseModifier(final double gm) { } /** Modify measurement. + * @param type of the measurements + * @param modifier applied modifier * @param estimated measurement to modify + * @since 12.1 */ - protected void doModify(final EstimatedMeasurementBase estimated) { + protected > void doModify(final EstimationModifier modifier, + final EstimatedMeasurementBase estimated) { // compute correction, for one way or two way measurements final TimeStampedPVCoordinates[] pv = estimated.getParticipants(); @@ -56,7 +61,7 @@ protected void doModify(final EstimatedMeasurementBase estimated) { // update estimated value taking into account the Shapiro time delay. final double[] newValue = estimated.getEstimatedValue().clone(); newValue[0] = newValue[0] + correction; - estimated.setEstimatedValue(newValue); + estimated.modifyEstimatedValue(modifier, newValue); } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/AngularIonosphericDelayModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/AngularIonosphericDelayModifier.java index 5d22fd1463..685eddd0d1 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/AngularIonosphericDelayModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/AngularIonosphericDelayModifier.java @@ -34,13 +34,16 @@ import org.orekit.utils.TrackingCoordinates; /** Class modifying theoretical angular measurement with ionospheric delay. + *

    * The effect of ionospheric correction on the angular measurement is computed * through the computation of the ionospheric delay. The spacecraft state * is shifted by the computed delay time and elevation and azimuth are computed * again with the new spacecraft state. - * + *

    + *

    * The ionospheric delay depends on the frequency of the signal (GNSS, VLBI, ...). * For optical measurements (e.g. SLR), the ray is not affected by ionosphere charged particles. + *

    *

    * Since 10.0, state derivatives and ionospheric parameters derivates are computed * using automatic differentiation. @@ -77,8 +80,7 @@ private double angularErrorIonosphericModel(final GroundStation station, // Base frame associated with the station final TopocentricFrame baseFrame = station.getBaseFrame(); // delay in meters - final double delay = ionoModel.pathDelay(state, baseFrame, frequency, ionoModel.getParameters(state.getDate())); - return delay; + return ionoModel.pathDelay(state, baseFrame, frequency, ionoModel.getParameters(state.getDate())); } /** {@inheritDoc} */ @@ -112,7 +114,7 @@ public void modifyWithoutDerivatives(final EstimatedMeasurementBase // Update estimated value taking into account the ionospheric delay. // Azimuth - elevation values - estimated.setEstimatedValue(azimuth, tc.getElevation()); + estimated.modifyEstimatedValue(this, azimuth, tc.getElevation()); } } 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 88f71efad8..f574ad8ee3 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/AngularRadioRefractionModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/AngularRadioRefractionModifier.java @@ -92,7 +92,7 @@ public void modifyWithoutDerivatives(final EstimatedMeasurementBase // consider only effect on elevation newValue[1] = newValue[1] + correction; - estimated.setEstimatedValue(newValue[0], newValue[1]); + estimated.modifyEstimatedValue(this, newValue[0], newValue[1]); } } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/AngularTroposphericDelayModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/AngularTroposphericDelayModifier.java index a136964e5b..32310b267b 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/AngularTroposphericDelayModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/AngularTroposphericDelayModifier.java @@ -16,8 +16,6 @@ */ package org.orekit.estimation.measurements.modifiers; -import java.util.List; - import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.MathUtils; import org.orekit.estimation.measurements.AngularAzEl; @@ -25,35 +23,54 @@ import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.GroundStation; import org.orekit.frames.Frame; -import org.orekit.models.earth.troposphere.DiscreteTroposphericModel; +import org.orekit.models.earth.troposphere.TroposphericModel; import org.orekit.propagation.SpacecraftState; import org.orekit.time.AbsoluteDate; import org.orekit.utils.Constants; import org.orekit.utils.ParameterDriver; import org.orekit.utils.TrackingCoordinates; +import java.util.List; + /** Class modifying theoretical angular measurement with tropospheric delay. + *

    * The effect of tropospheric correction on the angular is computed * through the computation of the tropospheric delay.The spacecraft state * is shifted by the computed delay time and elevation and azimuth are computed * again with the new spacecraft state. - * + *

    + *

    * In general, for GNSS, VLBI, ... there is hardly any frequency dependence in the delay. * For SLR techniques however, the frequency dependence is sensitive. - * + *

    + * @deprecated as of 12.1, {@link AngularRadioRefractionModifier} shall be used to handle tropospheric effect on angular measurements * @author Thierry Ceolin * @since 8.0 */ +@Deprecated public class AngularTroposphericDelayModifier implements EstimationModifier { /** Tropospheric delay model. */ - private final DiscreteTroposphericModel tropoModel; + private final TroposphericModel tropoModel; + + /** Constructor. + * + * @param model Tropospheric delay model appropriate for the current angular measurement method. + * @deprecated as of 12.1, {@link AngularRadioRefractionModifier} shall be used to handle tropospheric effect on angular measurements + */ + @Deprecated + public AngularTroposphericDelayModifier(final org.orekit.models.earth.troposphere.DiscreteTroposphericModel model) { + this(new org.orekit.models.earth.troposphere.TroposphericModelAdapter(model)); + } /** Constructor. * * @param model Tropospheric delay model appropriate for the current angular measurement method. + * @since 12.1 + * @deprecated as of 12.1, {@link AngularRadioRefractionModifier} shall be used to handle tropospheric effect on angular measurements */ - public AngularTroposphericDelayModifier(final DiscreteTroposphericModel model) { + @Deprecated + public AngularTroposphericDelayModifier(final TroposphericModel model) { tropoModel = model; } @@ -67,18 +84,19 @@ private double angularErrorTroposphericModel(final GroundStation station, // final Vector3D position = state.getPosition(); - // elevation - final double elevation = - station.getBaseFrame().getTrackingCoordinates(position, state.getFrame(), state.getDate()). - getElevation(); + // tracking + final TrackingCoordinates trackingCoordinates = + station.getBaseFrame().getTrackingCoordinates(position, state.getFrame(), state.getDate()); // only consider measures above the horizon - if (elevation > 0.0) { + if (trackingCoordinates.getElevation() > 0.0) { // delay in meters - final double delay = tropoModel.pathDelay(elevation, station.getBaseFrame().getPoint(), tropoModel.getParameters(state.getDate()), state.getDate()); + return tropoModel.pathDelay(trackingCoordinates, + station.getOffsetGeodeticPoint(state.getDate()), + station.getPressureTemperatureHumidity(state.getDate()), + tropoModel.getParameters(state.getDate()), state.getDate()). + getDelay(); - // one-way measurement. - return delay; } return 0; @@ -115,7 +133,7 @@ public void modifyWithoutDerivatives(final EstimatedMeasurementBase // Update estimated value taking into account the tropospheric delay. // Azimuth - elevation values - estimated.setEstimatedValue(azimuth, tc.getElevation()); + estimated.modifyEstimatedValue(this, azimuth, tc.getElevation()); } } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/BaseRangeRateTroposphericDelayModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/BaseRangeRateTroposphericDelayModifier.java index 7781c17ba6..f6e7ffddd8 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/BaseRangeRateTroposphericDelayModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/BaseRangeRateTroposphericDelayModifier.java @@ -23,10 +23,12 @@ import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.orekit.estimation.measurements.GroundStation; -import org.orekit.models.earth.troposphere.DiscreteTroposphericModel; +import org.orekit.models.earth.troposphere.TroposphericModel; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; +import org.orekit.utils.FieldTrackingCoordinates; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TrackingCoordinates; /** Baselass modifying theoretical range-rate measurements with tropospheric delay. * The effect of tropospheric correction on the range-rate is directly computed @@ -42,20 +44,31 @@ public abstract class BaseRangeRateTroposphericDelayModifier { /** Tropospheric delay model. */ - private final DiscreteTroposphericModel tropoModel; + private final TroposphericModel tropoModel; /** Constructor. * * @param model Tropospheric delay model appropriate for the current range-rate measurement method. + * @deprecated as of 12.1, replaced by {@link #BaseRangeRateTroposphericDelayModifier(TroposphericModel)} */ - protected BaseRangeRateTroposphericDelayModifier(final DiscreteTroposphericModel model) { + @Deprecated + protected BaseRangeRateTroposphericDelayModifier(final org.orekit.models.earth.troposphere.DiscreteTroposphericModel model) { + this(new org.orekit.models.earth.troposphere.TroposphericModelAdapter(model)); + } + + /** Constructor. + * + * @param model Tropospheric delay model appropriate for the current range-rate measurement method. + * @since 12.1 + */ + protected BaseRangeRateTroposphericDelayModifier(final TroposphericModel model) { tropoModel = model; } /** Get the tropospheric delay model. * @return tropospheric delay model */ - protected DiscreteTroposphericModel getTropoModel() { + protected TroposphericModel getTropoModel() { return tropoModel; } @@ -74,15 +87,18 @@ public double rangeRateErrorTroposphericModel(final GroundStation station, // spacecraft position and elevation as seen from the ground station final Vector3D position = state.getPosition(); - // elevation - final double elevation1 = - station.getBaseFrame().getTrackingCoordinates(position, state.getFrame(), state.getDate()). - getElevation(); + // tracking + final TrackingCoordinates trackingCoordinates1 = + station.getBaseFrame().getTrackingCoordinates(position, state.getFrame(), state.getDate()); // only consider measures above the horizon - if (elevation1 > 0) { + if (trackingCoordinates1.getElevation() > 0) { // tropospheric delay in meters - final double d1 = tropoModel.pathDelay(elevation1, station.getBaseFrame().getPoint(), tropoModel.getParameters(state.getDate()), state.getDate()); + final double d1 = tropoModel.pathDelay(trackingCoordinates1, + station.getOffsetGeodeticPoint(state.getDate()), + station.getPressureTemperatureHumidity(state.getDate()), + tropoModel.getParameters(state.getDate()), state.getDate()). + getDelay(); // propagate spacecraft state forward by dt final SpacecraftState state2 = state.shiftedBy(dt); @@ -90,13 +106,16 @@ public double rangeRateErrorTroposphericModel(final GroundStation station, // spacecraft position and elevation as seen from the ground station final Vector3D position2 = state2.getPosition(); - // elevation - final double elevation2 = - station.getBaseFrame().getTrackingCoordinates(position2, state2.getFrame(), state2.getDate()). - getElevation(); + // tracking + final TrackingCoordinates trackingCoordinates2 = + station.getBaseFrame().getTrackingCoordinates(position2, state2.getFrame(), state2.getDate()); // tropospheric delay dt after - final double d2 = tropoModel.pathDelay(elevation2, station.getBaseFrame().getPoint(), tropoModel.getParameters(state2.getDate()), state2.getDate()); + final double d2 = tropoModel.pathDelay(trackingCoordinates2, + station.getOffsetGeodeticPoint(state.getDate()), + station.getPressureTemperatureHumidity(state.getDate()), + tropoModel.getParameters(state2.getDate()), state2.getDate()). + getDelay(); return (d2 - d1) / dt; } @@ -126,14 +145,17 @@ public > T rangeRateErrorTroposphericModel(fin // spacecraft position and elevation as seen from the ground station final FieldVector3D position = state.getPosition(); - final T elevation1 = - station.getBaseFrame().getTrackingCoordinates(position, state.getFrame(), state.getDate()). - getElevation(); + final FieldTrackingCoordinates trackingCoordinates1 = + station.getBaseFrame().getTrackingCoordinates(position, state.getFrame(), state.getDate()); // only consider measures above the horizon - if (elevation1.getReal() > 0) { + if (trackingCoordinates1.getElevation().getReal() > 0) { // tropospheric delay in meters - final T d1 = tropoModel.pathDelay(elevation1, station.getBaseFrame().getPoint(field), parameters, state.getDate()); + final T d1 = tropoModel.pathDelay(trackingCoordinates1, + station.getOffsetGeodeticPoint(state.getDate()), + station.getPressureTemperatureHumidity(state.getDate()), + parameters, state.getDate()). + getDelay(); // propagate spacecraft state forward by dt final FieldSpacecraftState state2 = state.shiftedBy(dt); @@ -142,13 +164,16 @@ public > T rangeRateErrorTroposphericModel(fin final FieldVector3D position2 = state2.getPosition(); // elevation - final T elevation2 = - station.getBaseFrame().getTrackingCoordinates(position2, state2.getFrame(), state2.getDate()). - getElevation(); + final FieldTrackingCoordinates trackingCoordinates2 = + station.getBaseFrame().getTrackingCoordinates(position2, state2.getFrame(), state2.getDate()); // tropospheric delay dt after - final T d2 = tropoModel.pathDelay(elevation2, station.getBaseFrame().getPoint(field), parameters, state2.getDate()); + final T d2 = tropoModel.pathDelay(trackingCoordinates2, + station.getOffsetGeodeticPoint(state.getDate()), + station.getPressureTemperatureHumidity(state.getDate()), + parameters, state2.getDate()). + getDelay(); return d2.subtract(d1).divide(dt); } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/BaseRangeTroposphericDelayModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/BaseRangeTroposphericDelayModifier.java index 6f01a4f9b9..2e89e37e87 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/BaseRangeTroposphericDelayModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/BaseRangeTroposphericDelayModifier.java @@ -23,10 +23,12 @@ import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.orekit.estimation.measurements.GroundStation; -import org.orekit.models.earth.troposphere.DiscreteTroposphericModel; +import org.orekit.models.earth.troposphere.TroposphericModel; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; +import org.orekit.utils.FieldTrackingCoordinates; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TrackingCoordinates; /** Base class modifying theoretical range measurements with tropospheric delay. * The effect of tropospheric correction on the range is directly computed @@ -41,20 +43,31 @@ public abstract class BaseRangeTroposphericDelayModifier { /** Tropospheric delay model. */ - private final DiscreteTroposphericModel tropoModel; + private final TroposphericModel tropoModel; + + /** Constructor. + * + * @param model Tropospheric delay model appropriate for the current range measurement method. + * @deprecated as of 12.1, replaced by {@link #BaseRangeTroposphericDelayModifier(TroposphericModel)} + */ + @Deprecated + protected BaseRangeTroposphericDelayModifier(final org.orekit.models.earth.troposphere.DiscreteTroposphericModel model) { + this(new org.orekit.models.earth.troposphere.TroposphericModelAdapter(model)); + } /** Constructor. * * @param model Tropospheric delay model appropriate for the current range measurement method. + * @since 12.1 */ - protected BaseRangeTroposphericDelayModifier(final DiscreteTroposphericModel model) { + protected BaseRangeTroposphericDelayModifier(final TroposphericModel model) { tropoModel = model; } /** Get the tropospheric delay model. * @return tropospheric delay model */ - protected DiscreteTroposphericModel getTropoModel() { + protected TroposphericModel getTropoModel() { return tropoModel; } @@ -68,15 +81,17 @@ public double rangeErrorTroposphericModel(final GroundStation station, // spacecraft position and elevation as seen from the ground station final Vector3D position = state.getPosition(); - final double elevation = - station.getBaseFrame().getTrackingCoordinates(position, state.getFrame(), state.getDate()). - getElevation(); + final TrackingCoordinates trackingCoordinates = + station.getBaseFrame().getTrackingCoordinates(position, state.getFrame(), state.getDate()); // only consider measures above the horizon - if (elevation > 0) { + if (trackingCoordinates.getElevation() > 0) { // tropospheric delay in meters - final double delay = tropoModel.pathDelay(elevation, station.getBaseFrame().getPoint(), - tropoModel.getParameters(), state.getDate()); + final double delay = tropoModel.pathDelay(trackingCoordinates, + station.getOffsetGeodeticPoint(state.getDate()), + station.getPressureTemperatureHumidity(state.getDate()), + tropoModel.getParameters(), state.getDate()). + getDelay(); return delay; } @@ -101,15 +116,17 @@ public > T rangeErrorTroposphericModel(final G // spacecraft position and elevation as seen from the ground station final FieldVector3D position = state.getPosition(); - final T elevation = - station.getBaseFrame().getTrackingCoordinates(position, state.getFrame(), state.getDate()). - getElevation(); + final FieldTrackingCoordinates trackingCoordinates = + station.getBaseFrame().getTrackingCoordinates(position, state.getFrame(), state.getDate()); // only consider measures above the horizon - if (elevation .getReal() > 0) { + if (trackingCoordinates.getElevation() .getReal() > 0) { // tropospheric delay in meters - final T delay = tropoModel.pathDelay(elevation, station.getBaseFrame().getPoint(field), - parameters, state.getDate()); + final T delay = tropoModel.pathDelay(trackingCoordinates, + station.getOffsetGeodeticPoint(state.getDate()), + station.getPressureTemperatureHumidity(state.getDate()), + parameters, state.getDate()). + getDelay(); return delay; } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/Bias.java b/src/main/java/org/orekit/estimation/measurements/modifiers/Bias.java index cdbd65a0c7..23add1f646 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/Bias.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/Bias.java @@ -83,13 +83,13 @@ public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated // apply the bias to the measurement value final double[] value = estimated.getEstimatedValue(); int nb = 0; - for (int i = 0; i < drivers.size(); ++i) { - final ParameterDriver driver = drivers.get(i); - for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + for (final ParameterDriver driver : drivers) { + for (Span span = driver.getNamesSpanMap().getFirstSpan(); + span != null; span = span.next()) { value[nb++] += driver.getValue(span.getStart()); } } - estimated.setEstimatedValue(value); + estimated.modifyEstimatedValue(this, value); } @@ -99,12 +99,13 @@ public void modify(final EstimatedMeasurement estimated) { // apply the bias to the measurement value int nb = 0; - for (int i = 0; i < drivers.size(); ++i) { - final ParameterDriver driver = drivers.get(i); - for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + for (final ParameterDriver driver : drivers) { + for (Span span = driver.getNamesSpanMap().getFirstSpan(); + span != null; span = span.next()) { if (driver.isSelected()) { // add the partial derivatives - estimated.setParameterDerivatives(driver, span.getStart(), derivatives[nb++]); + estimated.setParameterDerivatives(driver, span.getStart(), + derivatives[nb++]); } } } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticModifierUtil.java b/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticModifierUtil.java index f9c64d18bd..7f0f9d6318 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticModifierUtil.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticModifierUtil.java @@ -21,6 +21,7 @@ import org.hipparchus.analysis.differentiation.Gradient; import org.orekit.estimation.measurements.EstimatedMeasurement; import org.orekit.estimation.measurements.EstimatedMeasurementBase; +import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.GroundStation; import org.orekit.estimation.measurements.ObservedMeasurement; import org.orekit.propagation.FieldSpacecraftState; @@ -48,10 +49,29 @@ private BistaticModifierUtil() { * @param emitter emitter station * @param receiver receiver station * @param modelEffect model effect + * @deprecated as of 12.1, replaced by {@link #modify(EstimatedMeasurementBase, + * GroundStation, GroundStation, ParametricModelEffect, EstimationModifier)} */ + @Deprecated public static > void modify(final EstimatedMeasurementBase estimated, final GroundStation emitter, final GroundStation receiver, final ParametricModelEffect modelEffect) { + modify(estimated, emitter, receiver, modelEffect, null); + } + + /** Apply a modifier to an estimated measurement. + * @param type of the measurement + * @param estimated estimated measurement to modify + * @param emitter emitter station + * @param receiver receiver station + * @param modelEffect model effect + * @param modifier applied modifier + * @since 12.1 + */ + public static > void modify(final EstimatedMeasurementBase estimated, + final GroundStation emitter, final GroundStation receiver, + final ParametricModelEffect modelEffect, + final EstimationModifier modifier) { // update estimated value taking into account the model effect. // The model effect delay is directly added to the measurement. @@ -59,7 +79,7 @@ public static > void modify(final EstimatedMeas final double[] newValue = estimated.getEstimatedValue().clone(); newValue[0] += modelEffect.evaluate(emitter, state); newValue[0] += modelEffect.evaluate(receiver, state); - estimated.setEstimatedValue(newValue); + estimated.modifyEstimatedValue(modifier, newValue); } @@ -72,13 +92,39 @@ public static > void modify(final EstimatedMeas * @param parametricModel parametric modifier model * @param modelEffect model effect * @param modelEffectGradient model effect gradient + * @deprecated as of 12.1, replaced by {@link #modify(EstimatedMeasurement, + * ParameterDriversProvider, AbstractGradientConverter, GroundStation, GroundStation, + * ParametricModelEffect, ParametricModelEffectGradient, EstimationModifier)} */ + @Deprecated public static > void modify(final EstimatedMeasurement estimated, final ParameterDriversProvider parametricModel, final AbstractGradientConverter converter, final GroundStation emitter, final GroundStation receiver, final ParametricModelEffect modelEffect, final ParametricModelEffectGradient modelEffectGradient) { + modify(estimated, parametricModel, converter, emitter, receiver, modelEffect, modelEffectGradient, null); + } + + /** Apply a modifier to an estimated measurement. + * @param type of the measurement + * @param estimated estimated measurement to modify + * @param emitter emitter station + * @param receiver receiver station + * @param converter gradient converter + * @param parametricModel parametric modifier model + * @param modelEffect model effect + * @param modelEffectGradient model effect gradient + * @param modifier applied modifier + * @since 12.1 + */ + public static > void modify(final EstimatedMeasurement estimated, + final ParameterDriversProvider parametricModel, + final AbstractGradientConverter converter, + final GroundStation emitter, final GroundStation receiver, + final ParametricModelEffect modelEffect, + final ParametricModelEffectGradient modelEffectGradient, + final EstimationModifier modifier) { final SpacecraftState state = estimated.getStates()[0]; @@ -148,7 +194,7 @@ public static > void modify(final EstimatedMeas } // modify the value - modify(estimated, emitter, receiver, modelEffect); + modify(estimated, emitter, receiver, modelEffect, modifier); } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticRangeIonosphericDelayModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticRangeIonosphericDelayModifier.java index b61d2f52e7..efdda8fd5b 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticRangeIonosphericDelayModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticRangeIonosphericDelayModifier.java @@ -63,7 +63,8 @@ public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { new ModifierGradientConverter(state, 6, new FrameAlignedProvider(state.getFrame())), emitter, receiver, this::rangeErrorIonosphericModel, - this::rangeErrorIonosphericModel); + this::rangeErrorIonosphericModel, + this); } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticRangeRateIonosphericDelayModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticRangeRateIonosphericDelayModifier.java index 8d25bb17a9..58a5e0567c 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticRangeRateIonosphericDelayModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticRangeRateIonosphericDelayModifier.java @@ -54,7 +54,9 @@ public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { new ModifierGradientConverter(state, 6, new FrameAlignedProvider(state.getFrame())), emitter, receiver, this::rangeRateErrorIonosphericModel, - this::rangeRateErrorIonosphericModel); + this::rangeRateErrorIonosphericModel, + this); } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticRangeRateTroposphericDelayModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticRangeRateTroposphericDelayModifier.java index 4d7ddbd9e3..5c70c08fd2 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticRangeRateTroposphericDelayModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticRangeRateTroposphericDelayModifier.java @@ -22,7 +22,7 @@ import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.GroundStation; -import org.orekit.models.earth.troposphere.DiscreteTroposphericModel; +import org.orekit.models.earth.troposphere.TroposphericModel; import org.orekit.propagation.SpacecraftState; /** Class modifying theoretical bistatic range-rate measurements with tropospheric delay. @@ -35,13 +35,25 @@ * @author Pascal Parraud * @since 11.2 */ -public class BistaticRangeRateTroposphericDelayModifier extends BaseRangeRateTroposphericDelayModifier implements EstimationModifier { +public class BistaticRangeRateTroposphericDelayModifier + extends BaseRangeRateTroposphericDelayModifier implements EstimationModifier { /** Constructor. * * @param model Tropospheric delay model appropriate for the current range-rate measurement method. + * @deprecated as of 12.1, replaced by {@link #BistaticRangeRateTroposphericDelayModifier(TroposphericModel)} */ - public BistaticRangeRateTroposphericDelayModifier(final DiscreteTroposphericModel model) { + @Deprecated + public BistaticRangeRateTroposphericDelayModifier(final org.orekit.models.earth.troposphere.DiscreteTroposphericModel model) { + this(new org.orekit.models.earth.troposphere.TroposphericModelAdapter(model)); + } + + /** Constructor. + * + * @param model Tropospheric delay model appropriate for the current range-rate measurement method. + * @since 12.1 + */ + public BistaticRangeRateTroposphericDelayModifier(final TroposphericModel model) { super(model); } @@ -53,7 +65,8 @@ public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { new ModifierGradientConverter(state, 6, new FrameAlignedProvider(state.getFrame())), emitter, receiver, this::rangeRateErrorTroposphericModel, - this::rangeRateErrorTroposphericModel); + this::rangeRateErrorTroposphericModel, + this); } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticRangeTroposphericDelayModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticRangeTroposphericDelayModifier.java index 657f80a2af..a41ab59d84 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticRangeTroposphericDelayModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/BistaticRangeTroposphericDelayModifier.java @@ -22,15 +22,18 @@ import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.GroundStation; -import org.orekit.models.earth.troposphere.DiscreteTroposphericModel; +import org.orekit.models.earth.troposphere.TroposphericModel; import org.orekit.propagation.SpacecraftState; /** Class modifying theoretical bistatic range measurement with tropospheric delay. + *

    * The effect of tropospheric correction on the range is directly computed * through the computation of the tropospheric delay. - * + *

    + *

    * In general, for GNSS, VLBI, ... there is hardly any frequency dependence in the delay. * For SLR techniques however, the frequency dependence is sensitive. + *

    * * @author Maxime Journot * @author Joris Olympio @@ -42,8 +45,19 @@ public class BistaticRangeTroposphericDelayModifier extends BaseRangeTropospheri /** Constructor. * * @param model Tropospheric delay model appropriate for the current range measurement method. + * @deprecated as of 12.1 replaced by {@link #BistaticRangeTroposphericDelayModifier(TroposphericModel)} + */ + @Deprecated + public BistaticRangeTroposphericDelayModifier(final org.orekit.models.earth.troposphere.DiscreteTroposphericModel model) { + this(new org.orekit.models.earth.troposphere.TroposphericModelAdapter(model)); + } + + /** Constructor. + * + * @param model Tropospheric delay model appropriate for the current range measurement method. + * @since 12.1 */ - public BistaticRangeTroposphericDelayModifier(final DiscreteTroposphericModel model) { + public BistaticRangeTroposphericDelayModifier(final TroposphericModel model) { super(model); } @@ -54,7 +68,8 @@ public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { new ModifierGradientConverter(state, 6, new FrameAlignedProvider(state.getFrame())), emitter, receiver, this::rangeErrorTroposphericModel, - this::rangeErrorTroposphericModel); + this::rangeErrorTroposphericModel, + this); } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/InterSatellitesPhaseAmbiguityModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/InterSatellitesPhaseAmbiguityModifier.java index 3cc4d90998..e1a4a8fc4e 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/InterSatellitesPhaseAmbiguityModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/InterSatellitesPhaseAmbiguityModifier.java @@ -27,7 +27,10 @@ /** Class modifying theoretical inter-satellites phase measurement with ambiguity. * @author Bryan Cazabonne * @since 10.3 + * @deprecated as of 12.1 ambiguity is managed directly by raw measurements + * {@link org.orekit.estimation.measurements.gnss.InterSatellitesPhase} */ +@Deprecated public class InterSatellitesPhaseAmbiguityModifier extends AbstractAmbiguityModifier implements EstimationModifier { /** Constructor. @@ -54,13 +57,13 @@ public List getParametersDrivers() { /** {@inheritDoc} */ @Override public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { - doModifyWithoutDerivatives(estimated); + doModifyWithoutDerivatives(this, estimated); } /** {@inheritDoc} */ @Override public void modify(final EstimatedMeasurement estimated) { - doModify(estimated); + doModify(this, estimated); } } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaInterSatellitesPhaseModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaInterSatellitesPhaseModifier.java index 600c9e0696..f409aebf89 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaInterSatellitesPhaseModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaInterSatellitesPhaseModifier.java @@ -23,35 +23,35 @@ import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.gnss.InterSatellitesPhase; -import org.orekit.frames.StaticTransform; -import org.orekit.propagation.SpacecraftState; -import org.orekit.time.AbsoluteDate; +import org.orekit.gnss.antenna.FrequencyPattern; import org.orekit.utils.ParameterDriver; -import org.orekit.utils.TimeStampedPVCoordinates; /** On-board antenna offset effect on inter-satellites phase measurements. * @author Bryan Cazabonne * @since 10.3 */ -public class OnBoardAntennaInterSatellitesPhaseModifier implements EstimationModifier { +public class OnBoardAntennaInterSatellitesPhaseModifier + extends PhaseCentersInterSatellitesBaseModifier + implements EstimationModifier { - /** Position of the Antenna Phase Center in satellite 1 frame. */ - private final Vector3D antennaPhaseCenter1; - - /** Position of the Antenna Phase Center in satellite 2 frame. */ - private final Vector3D antennaPhaseCenter2; + /** Simple constructor. + * @param receiverPhaseCenter position of the Antenna Phase Center in receiver satellite frame + * @param emitterPhaseCenter position of the Antenna Phase Center in emitter satellite frame + */ + public OnBoardAntennaInterSatellitesPhaseModifier(final Vector3D receiverPhaseCenter, + final Vector3D emitterPhaseCenter) { + this(new FrequencyPattern(receiverPhaseCenter, null), + new FrequencyPattern(emitterPhaseCenter, null)); + } /** Simple constructor. - * @param antennaPhaseCenter1 position of the Antenna Phase Center in satellite 1 frame - * (i.e. the satellite which receives the signal and performs the measurement) - * @param antennaPhaseCenter2 position of the Antenna Phase Center in satellite 2 frame - * (i.e. the satellite which simply emits the signal in the one-way - * case, or reflects the signal in the two-way case) + * @param receiverPattern pattern for receiver satellite + * @param emitterPattern pattern for emitter satellite + * @since 12.1 */ - public OnBoardAntennaInterSatellitesPhaseModifier(final Vector3D antennaPhaseCenter1, - final Vector3D antennaPhaseCenter2) { - this.antennaPhaseCenter1 = antennaPhaseCenter1; - this.antennaPhaseCenter2 = antennaPhaseCenter2; + public OnBoardAntennaInterSatellitesPhaseModifier(final FrequencyPattern receiverPattern, + final FrequencyPattern emitterPattern) { + super(receiverPattern, emitterPattern); } /** {@inheritDoc} */ @@ -62,39 +62,10 @@ public List getParametersDrivers() { @Override public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { - - // The participants are satellite 2 at emission, satellite 1 at reception - final TimeStampedPVCoordinates[] participants = estimated.getParticipants(); - final AbsoluteDate emissionDate = participants[0].getDate(); - final AbsoluteDate receptionDate = participants[1].getDate(); - - // transforms from spacecraft to inertial frame at emission/reception dates - final SpacecraftState localState = estimated.getStates()[0]; - final SpacecraftState receptionState = localState.shiftedBy(receptionDate.durationFrom(localState.getDate())); - final StaticTransform receptionSpacecraftToInert = receptionState.toStaticTransform().getInverse(); - final SpacecraftState remoteState = estimated.getStates()[1]; - final SpacecraftState emissionState = remoteState.shiftedBy(emissionDate.durationFrom(remoteState.getDate())); - final StaticTransform emissionSpacecraftToInert = emissionState.toStaticTransform().getInverse(); - - // Compute the geometrical value of the inter-satellites range directly from participants positions. - final Vector3D pSpacecraftReception = receptionSpacecraftToInert.transformPosition(Vector3D.ZERO); - final Vector3D pSpacecraftEmission = emissionSpacecraftToInert.transformPosition(Vector3D.ZERO); - final double interSatellitesRangeUsingSpacecraftCenter = Vector3D.distance(pSpacecraftEmission, pSpacecraftReception); - - // Compute the geometrical value of the range replacing - // The spacecraft positions with antenna phase center positions - final Vector3D pAPCReception = receptionSpacecraftToInert.transformPosition(antennaPhaseCenter1); - final Vector3D pAPCEmission = emissionSpacecraftToInert.transformPosition(antennaPhaseCenter2); - final double interSatellitesRangeUsingAntennaPhaseCenter = Vector3D.distance(pAPCEmission, pAPCReception); - - // Get the estimated value before this modifier is applied - final double[] value = estimated.getEstimatedValue(); - - // Modify the phase value by applying measurement wavelength - final double wavelength = estimated.getObservedMeasurement().getWavelength(); - value[0] += (interSatellitesRangeUsingAntennaPhaseCenter - interSatellitesRangeUsingSpacecraftCenter) / wavelength; - estimated.setEstimatedValue(value); - + estimated.modifyEstimatedValue(this, + estimated.getEstimatedValue()[0] + + oneWayDistanceModification(estimated) / + estimated.getObservedMeasurement().getWavelength()); } } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaInterSatellitesRangeModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaInterSatellitesRangeModifier.java index e39c8c9fae..8267be0307 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaInterSatellitesRangeModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaInterSatellitesRangeModifier.java @@ -23,35 +23,35 @@ import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.InterSatellitesRange; -import org.orekit.frames.StaticTransform; -import org.orekit.propagation.SpacecraftState; -import org.orekit.time.AbsoluteDate; +import org.orekit.gnss.antenna.FrequencyPattern; import org.orekit.utils.ParameterDriver; -import org.orekit.utils.TimeStampedPVCoordinates; /** On-board antenna offset effect on inter-satellites range measurements. * @author Luc Maisonobe * @since 9.0 */ -public class OnBoardAntennaInterSatellitesRangeModifier implements EstimationModifier { +public class OnBoardAntennaInterSatellitesRangeModifier + extends PhaseCentersInterSatellitesBaseModifier + implements EstimationModifier { - /** Position of the Antenna Phase Center in satellite 1 frame. */ - private final Vector3D antennaPhaseCenter1; - - /** Position of the Antenna Phase Center in satellite 2 frame. */ - private final Vector3D antennaPhaseCenter2; + /** Simple constructor. + * @param receiverPhaseCenter position of the Antenna Phase Center in emitter satellite frame + * @param emitterPhaseCenter position of the Antenna Phase Center in receiver satellite frame + */ + public OnBoardAntennaInterSatellitesRangeModifier(final Vector3D receiverPhaseCenter, + final Vector3D emitterPhaseCenter) { + this(new FrequencyPattern(receiverPhaseCenter, null), + new FrequencyPattern(emitterPhaseCenter, null)); + } /** Simple constructor. - * @param antennaPhaseCenter1 position of the Antenna Phase Center in satellite 1 frame - * (i.e. the satellite which receives the signal and performs the measurement) - * @param antennaPhaseCenter2 position of the Antenna Phase Center in satellite 2 frame - * (i.e. the satellite which simply emits the signal in the one-way - * case, or reflects the signal in the two-way case) + * @param receiverPattern pattern for receiver satellite + * @param emitterPattern pattern for emitter satellite + * @since 12.1 */ - public OnBoardAntennaInterSatellitesRangeModifier(final Vector3D antennaPhaseCenter1, - final Vector3D antennaPhaseCenter2) { - this.antennaPhaseCenter1 = antennaPhaseCenter1; - this.antennaPhaseCenter2 = antennaPhaseCenter2; + public OnBoardAntennaInterSatellitesRangeModifier(final FrequencyPattern receiverPattern, + final FrequencyPattern emitterPattern) { + super(receiverPattern, emitterPattern); } /** {@inheritDoc} */ @@ -63,103 +63,10 @@ public List getParametersDrivers() { /** {@inheritDoc} */ @Override public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { - if (estimated.getParticipants().length < 3) { - modifyOneWay(estimated); - } else { - modifyTwoWay(estimated); - } - } - - /** Apply a modifier to an estimated measurement in the one-way case. - * @param estimated estimated measurement to modify - */ - private void modifyOneWay(final EstimatedMeasurementBase estimated) { - - // the participants are satellite 2 at emission, satellite 1 at reception - final TimeStampedPVCoordinates[] participants = estimated.getParticipants(); - final AbsoluteDate emissionDate = participants[0].getDate(); - final AbsoluteDate receptionDate = participants[1].getDate(); - - // transforms from spacecraft to inertial frame at emission/reception dates - final SpacecraftState refState1 = estimated.getStates()[0]; - final SpacecraftState receptionState = refState1.shiftedBy(receptionDate.durationFrom(refState1.getDate())); - final StaticTransform receptionSpacecraftToInert = receptionState.toStaticTransform().getInverse(); - final SpacecraftState refState2 = estimated.getStates()[1]; - final SpacecraftState emissionState = refState2.shiftedBy(emissionDate.durationFrom(refState2.getDate())); - final StaticTransform emissionSpacecraftToInert = emissionState.toStaticTransform().getInverse(); - - // compute the geometrical value of the inter-satellites range directly from participants positions. - // Note that this may be different from the value returned by estimated.getEstimatedValue(), - // because other modifiers may already have been taken into account - final Vector3D pSpacecraftReception = receptionSpacecraftToInert.transformPosition(Vector3D.ZERO); - final Vector3D pSpacecraftEmission = emissionSpacecraftToInert.transformPosition(Vector3D.ZERO); - final double interSatellitesRangeUsingSpacecraftCenter = - Vector3D.distance(pSpacecraftEmission, pSpacecraftReception); - - // compute the geometrical value of the range replacing - // the spacecraft positions with antenna phase center positions - final Vector3D pAPCReception = receptionSpacecraftToInert.transformPosition(antennaPhaseCenter1); - final Vector3D pAPCEmission = emissionSpacecraftToInert.transformPosition(antennaPhaseCenter2); - final double interSatellitesRangeUsingAntennaPhaseCenter = - Vector3D.distance(pAPCEmission, pAPCReception); - - // get the estimated value before this modifier is applied - final double[] value = estimated.getEstimatedValue(); - - // modify the value - value[0] += interSatellitesRangeUsingAntennaPhaseCenter - interSatellitesRangeUsingSpacecraftCenter; - estimated.setEstimatedValue(value); - - } - - /** Apply a modifier to an estimated measurement in the two-way case. - * @param estimated estimated measurement to modify - */ - private void modifyTwoWay(final EstimatedMeasurementBase estimated) { - - // the participants are satellite 1 at emission, satellite 2 at transit, satellite 1 at reception - final TimeStampedPVCoordinates[] participants = estimated.getParticipants(); - final AbsoluteDate emissionDate = participants[0].getDate(); - final AbsoluteDate transitDate = participants[1].getDate(); - final AbsoluteDate receptionDate = participants[2].getDate(); - - // transforms from spacecraft to inertial frame at emission/reception dates - final SpacecraftState refState1 = estimated.getStates()[0]; - final SpacecraftState receptionState = refState1.shiftedBy(receptionDate.durationFrom(refState1.getDate())); - final StaticTransform receptionSpacecraftToInert = receptionState.toStaticTransform().getInverse(); - final SpacecraftState refState2 = estimated.getStates()[1]; - final SpacecraftState transitState = refState2.shiftedBy(transitDate.durationFrom(refState2.getDate())); - final StaticTransform transitSpacecraftToInert = transitState.toStaticTransform().getInverse(); - final SpacecraftState emissionState = refState1.shiftedBy(emissionDate.durationFrom(refState1.getDate())); - final StaticTransform emissionSpacecraftToInert = emissionState.toStaticTransform().getInverse(); - - // compute the geometrical value of the inter-satellites range directly from participants positions. - // Note that this may be different from the value returned by estimated.getEstimatedValue(), - // because other modifiers may already have been taken into account - final Vector3D pSpacecraftReception = receptionSpacecraftToInert.transformPosition(Vector3D.ZERO); - final Vector3D pSpacecraftTransit = transitSpacecraftToInert.transformPosition(Vector3D.ZERO); - final Vector3D pSpacecraftEmission = emissionSpacecraftToInert.transformPosition(Vector3D.ZERO); - final double interSatellitesRangeUsingSpacecraftCenter = - 0.5 * (Vector3D.distance(pSpacecraftEmission, pSpacecraftTransit) + - Vector3D.distance(pSpacecraftTransit, pSpacecraftReception)); - - // compute the geometrical value of the range replacing - // the spacecraft positions with antenna phase center positions - final Vector3D pAPCReception = receptionSpacecraftToInert.transformPosition(antennaPhaseCenter1); - final Vector3D pAPCTransit = transitSpacecraftToInert.transformPosition(antennaPhaseCenter2); - final Vector3D pAPCEmission = emissionSpacecraftToInert.transformPosition(antennaPhaseCenter1); - final double interSatellitesRangeUsingAntennaPhaseCenter = - 0.5 * (Vector3D.distance(pAPCEmission, pAPCTransit) + - Vector3D.distance(pAPCTransit, pAPCReception)); - - - // get the estimated value before this modifier is applied - final double[] value = estimated.getEstimatedValue(); - - // modify the value - value[0] += interSatellitesRangeUsingAntennaPhaseCenter - interSatellitesRangeUsingSpacecraftCenter; - estimated.setEstimatedValue(value); - + final double delta = estimated.getParticipants().length < 3 ? + oneWayDistanceModification(estimated) : + twoWayDistanceModification(estimated); + estimated.modifyEstimatedValue(this, estimated.getEstimatedValue()[0] + delta); } } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaOneWayGNSSPhaseModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaOneWayGNSSPhaseModifier.java index b77c2973fd..91d4fe8be2 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaOneWayGNSSPhaseModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaOneWayGNSSPhaseModifier.java @@ -24,42 +24,42 @@ import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.gnss.OneWayGNSSPhase; -import org.orekit.frames.StaticTransform; -import org.orekit.orbits.CartesianOrbit; -import org.orekit.orbits.Orbit; -import org.orekit.propagation.SpacecraftState; -import org.orekit.time.AbsoluteDate; +import org.orekit.gnss.antenna.FrequencyPattern; import org.orekit.utils.ParameterDriver; -import org.orekit.utils.TimeStampedPVCoordinates; /** On-board antenna offset effect on one-way GNSS phase measurements. * @author Bryan Cazabonne * @since 10.3 */ -public class OnBoardAntennaOneWayGNSSPhaseModifier implements EstimationModifier { - - /** Position of the Antenna Phase Center in satellite 1 frame. */ - private final Vector3D antennaPhaseCenter1; - - /** Position of the Antenna Phase Center in satellite 2 frame. */ - private final Vector3D antennaPhaseCenter2; - - /** Attitude provider of the emitting satellite. */ - private final AttitudeProvider attitude; +public class OnBoardAntennaOneWayGNSSPhaseModifier + extends PhaseCentersOneWayGNSSBaseModifier + implements EstimationModifier { /** Simple constructor. - * @param antennaPhaseCenter1 position of the Antenna Phase Center in satellite 1 frame + * @param receiverPhaseCenter position of the Antenna Phase Center in satellite 1 frame * (i.e. the satellite which receives the signal and performs the measurement) - * @param antennaPhaseCenter2 position of the Antenna Phase Center in satellite 2 frame + * @param emitterPhaseCenter position of the Antenna Phase Center in satellite 2 frame * (i.e. the satellite which simply emits the signal) - * @param attitude attitude provider of the emitting satellite + * @param attitudeProvider attitude provider of the emitting satellite + */ + public OnBoardAntennaOneWayGNSSPhaseModifier(final Vector3D receiverPhaseCenter, + final Vector3D emitterPhaseCenter, + final AttitudeProvider attitudeProvider) { + this(new FrequencyPattern(receiverPhaseCenter, null), + new FrequencyPattern(emitterPhaseCenter, null), + attitudeProvider); + } + + /** Simple constructor. + * @param receiverPattern pattern for receiver satellite + * @param emitterPattern pattern for emitter satellite + * @param attitudeProvider attitude provider of the emitting satellite + * @since 12.1 */ - public OnBoardAntennaOneWayGNSSPhaseModifier(final Vector3D antennaPhaseCenter1, - final Vector3D antennaPhaseCenter2, - final AttitudeProvider attitude) { - this.antennaPhaseCenter1 = antennaPhaseCenter1; - this.antennaPhaseCenter2 = antennaPhaseCenter2; - this.attitude = attitude; + public OnBoardAntennaOneWayGNSSPhaseModifier(final FrequencyPattern receiverPattern, + final FrequencyPattern emitterPattern, + final AttitudeProvider attitudeProvider) { + super(receiverPattern, emitterPattern, attitudeProvider); } /** {@inheritDoc} */ @@ -71,49 +71,10 @@ public List getParametersDrivers() { /** {@inheritDoc} */ @Override public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { - - // The participants are remote satellite at emission, local satellite at reception - final TimeStampedPVCoordinates[] phaseParticipants = estimated.getParticipants(); - final AbsoluteDate phaseEmissionDate = phaseParticipants[0].getDate(); - final AbsoluteDate phaseReceptionDate = phaseParticipants[1].getDate(); - - // Transforms from spacecraft to inertial frame at reception date - final SpacecraftState refStateLocal = estimated.getStates()[0]; - final SpacecraftState receptionState = refStateLocal.shiftedBy(phaseReceptionDate.durationFrom(refStateLocal.getDate())); - final StaticTransform receptionSpacecraftToInert = receptionState.toStaticTransform().getInverse(); - - // Orbit of the remote satellite - final Orbit orbitRemote = new CartesianOrbit(phaseParticipants[0], refStateLocal.getFrame(), receptionState.getMu()); - - // Transforms from spacecraft to inertial frame at emission date - final SpacecraftState refStateRemote = new SpacecraftState(orbitRemote, - attitude.getAttitude(orbitRemote, - orbitRemote.getDate(), - orbitRemote.getFrame())); - final SpacecraftState emissionState = refStateRemote.shiftedBy(phaseEmissionDate.durationFrom(refStateRemote.getDate())); - final StaticTransform emissionSpacecraftToInert = emissionState.toStaticTransform().getInverse(); - - // Compute the geometrical value of the one-way GNSS phase directly from participants positions. - // Note that this may be different from the value returned by estimated.getEstimatedValue(), - // because other modifiers may already have been taken into account - final Vector3D pSpacecraftReception = receptionSpacecraftToInert.transformPosition(Vector3D.ZERO); - final Vector3D pSpacecraftEmission = emissionSpacecraftToInert.transformPosition(Vector3D.ZERO); - final double oneWayGNSSPhaseUsingSpacecraftCenter = Vector3D.distance(pSpacecraftEmission, pSpacecraftReception); - - // Compute the geometrical value of the phase replacing - // the spacecraft positions with antenna phase center positions - final Vector3D pAPCReception = receptionSpacecraftToInert.transformPosition(antennaPhaseCenter1); - final Vector3D pAPCEmission = emissionSpacecraftToInert.transformPosition(antennaPhaseCenter2); - final double oneWayGNSSPhaseUsingAntennaPhaseCenter = Vector3D.distance(pAPCEmission, pAPCReception); - - // Get the estimated value before this modifier is applied - final double[] value = estimated.getEstimatedValue(); - - // Modify the value - final double wavelength = estimated.getObservedMeasurement().getWavelength(); - value[0] += (oneWayGNSSPhaseUsingAntennaPhaseCenter - oneWayGNSSPhaseUsingSpacecraftCenter) / wavelength; - estimated.setEstimatedValue(value); - + estimated.modifyEstimatedValue(this, + estimated.getEstimatedValue()[0] + + oneWayDistanceModification(estimated) / + estimated.getObservedMeasurement().getWavelength()); } } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaOneWayGNSSRangeModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaOneWayGNSSRangeModifier.java index 8c388af8ea..243524dbc2 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaOneWayGNSSRangeModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaOneWayGNSSRangeModifier.java @@ -24,42 +24,42 @@ import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.gnss.OneWayGNSSRange; -import org.orekit.frames.StaticTransform; -import org.orekit.orbits.CartesianOrbit; -import org.orekit.orbits.Orbit; -import org.orekit.propagation.SpacecraftState; -import org.orekit.time.AbsoluteDate; +import org.orekit.gnss.antenna.FrequencyPattern; import org.orekit.utils.ParameterDriver; -import org.orekit.utils.TimeStampedPVCoordinates; /** On-board antenna offset effect on one-way GNSS range measurements. * @author Bryan Cazabonne * @since 10.3 */ -public class OnBoardAntennaOneWayGNSSRangeModifier implements EstimationModifier { - - /** Position of the Antenna Phase Center in satellite 1 frame. */ - private final Vector3D antennaPhaseCenter1; - - /** Position of the Antenna Phase Center in satellite 2 frame. */ - private final Vector3D antennaPhaseCenter2; - - /** Attitude provider of the emitting satellite. */ - private final AttitudeProvider attitude; +public class OnBoardAntennaOneWayGNSSRangeModifier + extends PhaseCentersOneWayGNSSBaseModifier + implements EstimationModifier { /** Simple constructor. - * @param antennaPhaseCenter1 position of the Antenna Phase Center in satellite 1 frame + * @param receiverPhaseCenter position of the Antenna Phase Center in satellite 1 frame * (i.e. the satellite which receives the signal and performs the measurement) - * @param antennaPhaseCenter2 position of the Antenna Phase Center in satellite 2 frame + * @param emitterPhaseCenter position of the Antenna Phase Center in satellite 2 frame * (i.e. the satellite which simply emits the signal) - * @param attitude attitude provider of the emitting satellite + * @param attitudeProvider attitude provider of the emitting satellite + */ + public OnBoardAntennaOneWayGNSSRangeModifier(final Vector3D receiverPhaseCenter, + final Vector3D emitterPhaseCenter, + final AttitudeProvider attitudeProvider) { + this(new FrequencyPattern(receiverPhaseCenter, null), + new FrequencyPattern(emitterPhaseCenter, null), + attitudeProvider); + } + + /** Simple constructor. + * @param receiverPattern pattern for receiver satellite + * @param emitterPattern pattern for emitter satellite + * @param attitudeProvider attitude provider of the emitting satellite + * @since 12.1 */ - public OnBoardAntennaOneWayGNSSRangeModifier(final Vector3D antennaPhaseCenter1, - final Vector3D antennaPhaseCenter2, - final AttitudeProvider attitude) { - this.antennaPhaseCenter1 = antennaPhaseCenter1; - this.antennaPhaseCenter2 = antennaPhaseCenter2; - this.attitude = attitude; + public OnBoardAntennaOneWayGNSSRangeModifier(final FrequencyPattern receiverPattern, + final FrequencyPattern emitterPattern, + final AttitudeProvider attitudeProvider) { + super(receiverPattern, emitterPattern, attitudeProvider); } /** {@inheritDoc} */ @@ -71,48 +71,9 @@ public List getParametersDrivers() { /** {@inheritDoc} */ @Override public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { - - // The participants are remote satellite at emission, local satellite at reception - final TimeStampedPVCoordinates[] participants = estimated.getParticipants(); - final AbsoluteDate emissionDate = participants[0].getDate(); - final AbsoluteDate receptionDate = participants[1].getDate(); - - // Transforms from spacecraft to inertial frame at reception date - final SpacecraftState refStateLocal = estimated.getStates()[0]; - final SpacecraftState receptionState = refStateLocal.shiftedBy(receptionDate.durationFrom(refStateLocal.getDate())); - final StaticTransform receptionSpacecraftToInert = receptionState.toStaticTransform().getInverse(); - - // Orbit of the remote satellite - final Orbit orbitRemote = new CartesianOrbit(participants[0], refStateLocal.getFrame(), receptionState.getMu()); - - // Transforms from spacecraft to inertial frame at emission date - final SpacecraftState refStateRemote = new SpacecraftState(orbitRemote, - attitude.getAttitude(orbitRemote, - orbitRemote.getDate(), - orbitRemote.getFrame())); - final SpacecraftState emissionState = refStateRemote.shiftedBy(emissionDate.durationFrom(refStateRemote.getDate())); - final StaticTransform emissionSpacecraftToInert = emissionState.toStaticTransform().getInverse(); - - // Compute the geometrical value of the one-way GNSS range directly from participants positions. - // Note that this may be different from the value returned by estimated.getEstimatedValue(), - // because other modifiers may already have been taken into account - final Vector3D pSpacecraftReception = receptionSpacecraftToInert.transformPosition(Vector3D.ZERO); - final Vector3D pSpacecraftEmission = emissionSpacecraftToInert.transformPosition(Vector3D.ZERO); - final double oneWayGNSSRangeUsingSpacecraftCenter = Vector3D.distance(pSpacecraftEmission, pSpacecraftReception); - - // Compute the geometrical value of the range replacing - // the spacecraft positions with antenna phase center positions - final Vector3D pAPCReception = receptionSpacecraftToInert.transformPosition(antennaPhaseCenter1); - final Vector3D pAPCEmission = emissionSpacecraftToInert.transformPosition(antennaPhaseCenter2); - final double oneWayGNSSRangeUsingAntennaPhaseCenter = Vector3D.distance(pAPCEmission, pAPCReception); - - // Get the estimated value before this modifier is applied - final double[] value = estimated.getEstimatedValue(); - - // Modify the value - value[0] += oneWayGNSSRangeUsingAntennaPhaseCenter - oneWayGNSSRangeUsingSpacecraftCenter; - estimated.setEstimatedValue(value); - + estimated.modifyEstimatedValue(this, + estimated.getEstimatedValue()[0] + + oneWayDistanceModification(estimated)); } } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaTurnAroundRangeModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaTurnAroundRangeModifier.java index 4bdded6a42..ccfb0b0e44 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaTurnAroundRangeModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaTurnAroundRangeModifier.java @@ -97,7 +97,7 @@ public void modifyWithoutDerivatives(final EstimatedMeasurementBase { /** Constructor. @@ -54,13 +57,13 @@ public List getParametersDrivers() { /** {@inheritDoc} */ @Override public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { - doModifyWithoutDerivatives(estimated); + doModifyWithoutDerivatives(this, estimated); } /** {@inheritDoc} */ @Override public void modify(final EstimatedMeasurement estimated) { - doModify(estimated); + doModify(this, estimated); } } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/ParametricModelEffect.java b/src/main/java/org/orekit/estimation/measurements/modifiers/ParametricModelEffect.java index 92d4315f1d..2a98c8de7c 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/ParametricModelEffect.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/ParametricModelEffect.java @@ -19,12 +19,12 @@ import org.orekit.estimation.measurements.GroundStation; import org.orekit.propagation.SpacecraftState; -/** Functional interface for parameteric models. +/** Functional interface for parametric models. * @author Luc Maisonobe * @since 11.2 */ @FunctionalInterface -interface ParametricModelEffect { +public interface ParametricModelEffect { /** Evaluate the parametric model effect. * @param station station diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/ParametricModelEffectGradient.java b/src/main/java/org/orekit/estimation/measurements/modifiers/ParametricModelEffectGradient.java index 3cee3ca169..9439a0c29d 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/ParametricModelEffectGradient.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/ParametricModelEffectGradient.java @@ -20,12 +20,12 @@ import org.orekit.estimation.measurements.GroundStation; import org.orekit.propagation.FieldSpacecraftState; -/** Functional interface for parameteric models. +/** Functional interface for parametric models. * @author Luc Maisonobe * @since 11.2 */ @FunctionalInterface -interface ParametricModelEffectGradient { +public interface ParametricModelEffectGradient { /** Evaluate the parametric model effect. * @param station station diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseAmbiguityModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseAmbiguityModifier.java index 0ca3cce25e..6cc841b5e2 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseAmbiguityModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseAmbiguityModifier.java @@ -28,7 +28,10 @@ * * @author Luc Maisonobe * @since 9.2 + * @deprecated as of 12.1 ambiguity is managed directly by raw measurements + * {@link org.orekit.estimation.measurements.gnss.Phase} */ +@Deprecated public class PhaseAmbiguityModifier extends AbstractAmbiguityModifier implements EstimationModifier { /** Constructor. @@ -54,12 +57,12 @@ public List getParametersDrivers() { @Override public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { - doModifyWithoutDerivatives(estimated); + doModifyWithoutDerivatives(this, estimated); } @Override public void modify(final EstimatedMeasurement estimated) { - doModify(estimated); + doModify(this, estimated); } } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseCentersInterSatellitesBaseModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseCentersInterSatellitesBaseModifier.java new file mode 100644 index 0000000000..a2bf47aaef --- /dev/null +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseCentersInterSatellitesBaseModifier.java @@ -0,0 +1,108 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.estimation.measurements.modifiers; + +import org.orekit.estimation.measurements.AbstractMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; +import org.orekit.estimation.measurements.InterSatellitesRange; +import org.orekit.frames.StaticTransform; +import org.orekit.gnss.antenna.FrequencyPattern; +import org.orekit.propagation.SpacecraftState; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.TimeStampedPVCoordinates; + +/** On-board antenna offset effect on inter-satellites phase measurements. + * @param type of the measurement + * @author Luc Maisonobe + * @since 12.1 + */ +public class PhaseCentersInterSatellitesBaseModifier> { + + /** Uplink offset model. */ + private final PhaseCentersOffsetComputer uplink; + + /** Downlink offset model. */ + private final PhaseCentersOffsetComputer downlink; + + /** Simple constructor. + * @param pattern1 pattern for satellite 1 + * (i.e. the satellite which receives the signal and performs the measurement) + * @param pattern2 pattern for satellite 2 + * (i.e. the satellite which simply emits the signal in the one-way + * case, or reflects the signal in the two-way case) + */ + public PhaseCentersInterSatellitesBaseModifier(final FrequencyPattern pattern1, + final FrequencyPattern pattern2) { + this.uplink = new PhaseCentersOffsetComputer(pattern1, pattern2); + this.downlink = new PhaseCentersOffsetComputer(pattern2, pattern1); + } + + /** Compute distance modification for one way measurement. + * @param estimated estimated measurement to modify + * @return distance modification to add to raw measurement + */ + public double oneWayDistanceModification(final EstimatedMeasurementBase estimated) { + + // The participants are satellite 2 at emission, satellite 1 at reception + final TimeStampedPVCoordinates[] participants = estimated.getParticipants(); + final AbsoluteDate emissionDate = participants[0].getDate(); + final AbsoluteDate receptionDate = participants[1].getDate(); + + // transforms from spacecraft to inertial frame at emission/reception dates + final SpacecraftState localState = estimated.getStates()[0]; + final SpacecraftState receptionState = localState.shiftedBy(receptionDate.durationFrom(localState.getDate())); + final StaticTransform receptionSpacecraftToInert = receptionState.toStaticTransform().getInverse(); + final SpacecraftState remoteState = estimated.getStates()[1]; + final SpacecraftState emissionState = remoteState.shiftedBy(emissionDate.durationFrom(remoteState.getDate())); + final StaticTransform emissionSpacecraftToInert = emissionState.toStaticTransform().getInverse(); + + // compute offset due to phase centers + return downlink.offset(emissionSpacecraftToInert, receptionSpacecraftToInert); + + } + + /** Compute distance modification for two way measurement. + * @param estimated estimated measurement to modify + * @return distance modification to add to raw measurement + */ + public double twoWayDistanceModification(final EstimatedMeasurementBase estimated) { + + // the participants are satellite 1 at emission, satellite 2 at transit, satellite 1 at reception + final TimeStampedPVCoordinates[] participants = estimated.getParticipants(); + final AbsoluteDate emissionDate = participants[0].getDate(); + final AbsoluteDate transitDate = participants[1].getDate(); + final AbsoluteDate receptionDate = participants[2].getDate(); + + // transforms from spacecraft to inertial frame at emission/reception dates + final SpacecraftState refState1 = estimated.getStates()[0]; + final SpacecraftState receptionState = refState1.shiftedBy(receptionDate.durationFrom(refState1.getDate())); + final StaticTransform receptionSpacecraftToInert = receptionState.toStaticTransform().getInverse(); + final SpacecraftState refState2 = estimated.getStates()[1]; + final SpacecraftState transitState = refState2.shiftedBy(transitDate.durationFrom(refState2.getDate())); + final StaticTransform transitSpacecraftToInert = transitState.toStaticTransform().getInverse(); + final SpacecraftState emissionState = refState1.shiftedBy(emissionDate.durationFrom(refState1.getDate())); + final StaticTransform emissionSpacecraftToInert = emissionState.toStaticTransform().getInverse(); + + // compute offsets due to phase centers + final double uplinkOffset = uplink.offset(emissionSpacecraftToInert, transitSpacecraftToInert); + final double downlinkOffset = downlink.offset(transitSpacecraftToInert, receptionSpacecraftToInert); + + return 0.5 * (uplinkOffset + downlinkOffset); + + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseCentersOneWayGNSSBaseModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseCentersOneWayGNSSBaseModifier.java new file mode 100644 index 0000000000..4fac240068 --- /dev/null +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseCentersOneWayGNSSBaseModifier.java @@ -0,0 +1,86 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.estimation.measurements.modifiers; + +import org.orekit.attitudes.AttitudeProvider; +import org.orekit.estimation.measurements.AbstractMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; +import org.orekit.frames.StaticTransform; +import org.orekit.gnss.antenna.FrequencyPattern; +import org.orekit.orbits.CartesianOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.propagation.SpacecraftState; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.TimeStampedPVCoordinates; + +/** On-board antenna offset effect on inter-satellites phase measurements. + * @param type of the measurement + * @author Luc Maisonobe + * @since 12.1 + */ +public class PhaseCentersOneWayGNSSBaseModifier> { + + /** Link offset model. */ + private final PhaseCentersOffsetComputer link; + + /** Attitude provider of the emitting satellite. */ + private final AttitudeProvider attitudeProvider; + + /** Simple constructor. + * @param receiverPattern pattern for receiver satellite + * @param emitterPattern pattern for emitter satellite + * @param attitudeProvider attitude provider of the emitting satellite + */ + public PhaseCentersOneWayGNSSBaseModifier(final FrequencyPattern receiverPattern, + final FrequencyPattern emitterPattern, + final AttitudeProvider attitudeProvider) { + this.link = new PhaseCentersOffsetComputer(emitterPattern, receiverPattern); + this.attitudeProvider = attitudeProvider; + } + + /** Compute distance modification for one way measurement. + * @param estimated estimated measurement to modify + * @return distance modification to add to raw measurement + */ + public double oneWayDistanceModification(final EstimatedMeasurementBase estimated) { + + // The participants are remote satellite at emission, local satellite at reception + final TimeStampedPVCoordinates[] phaseParticipants = estimated.getParticipants(); + final AbsoluteDate phaseEmissionDate = phaseParticipants[0].getDate(); + final AbsoluteDate phaseReceptionDate = phaseParticipants[1].getDate(); + + // Transforms from spacecraft to inertial frame at reception date + final SpacecraftState refStateLocal = estimated.getStates()[0]; + final SpacecraftState receptionState = refStateLocal.shiftedBy(phaseReceptionDate.durationFrom(refStateLocal.getDate())); + final StaticTransform receptionSpacecraftToInert = receptionState.toStaticTransform().getInverse(); + + // Orbit of the remote satellite + final Orbit orbitRemote = new CartesianOrbit(phaseParticipants[0], refStateLocal.getFrame(), receptionState.getMu()); + + // Transforms from spacecraft to inertial frame at emission date + final SpacecraftState refStateRemote = new SpacecraftState(orbitRemote, + attitudeProvider.getAttitude(orbitRemote, + orbitRemote.getDate(), + orbitRemote.getFrame())); + final SpacecraftState emissionState = refStateRemote.shiftedBy(phaseEmissionDate.durationFrom(refStateRemote.getDate())); + final StaticTransform emissionSpacecraftToInert = emissionState.toStaticTransform().getInverse(); + + // compute offset due to phase centers + return link.offset(emissionSpacecraftToInert, receptionSpacecraftToInert); + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseCentersPhaseModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseCentersPhaseModifier.java index b43577fa3c..0c1060f9b5 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseCentersPhaseModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseCentersPhaseModifier.java @@ -29,10 +29,9 @@ * @author Luc Maisonobe * @since 12.0 */ -public class PhaseCentersPhaseModifier implements EstimationModifier { - - /** Raw modifier. */ - private final PhaseCentersGroundReceiverBaseModifier modifier; +public class PhaseCentersPhaseModifier + extends PhaseCentersGroundReceiverBaseModifier + implements EstimationModifier { /** Simple constructor. * @param stationPattern station pattern @@ -40,7 +39,7 @@ public class PhaseCentersPhaseModifier implements EstimationModifier { */ public PhaseCentersPhaseModifier(final FrequencyPattern stationPattern, final FrequencyPattern satellitePattern) { - this.modifier = new PhaseCentersGroundReceiverBaseModifier<>(stationPattern, satellitePattern); + super(stationPattern, satellitePattern); } /** {@inheritDoc} */ @@ -52,9 +51,10 @@ public List getParametersDrivers() { /** {@inheritDoc} */ @Override public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { - estimated.setEstimatedValue(estimated.getEstimatedValue()[0] + - modifier.oneWayDistanceModification(estimated) / - estimated.getObservedMeasurement().getWavelength()); + estimated.modifyEstimatedValue(this, + estimated.getEstimatedValue()[0] + + oneWayDistanceModification(estimated) / + estimated.getObservedMeasurement().getWavelength()); } } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseCentersRangeModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseCentersRangeModifier.java index 29b5d71ddd..95eb51c1cd 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseCentersRangeModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseCentersRangeModifier.java @@ -29,10 +29,9 @@ * @author Luc Maisonobe * @since 12.0 */ -public class PhaseCentersRangeModifier implements EstimationModifier { - - /** Raw modifier. */ - private final PhaseCentersGroundReceiverBaseModifier modifier; +public class PhaseCentersRangeModifier + extends PhaseCentersGroundReceiverBaseModifier + implements EstimationModifier { /** Simple constructor. * @param stationPattern station pattern @@ -40,7 +39,7 @@ public class PhaseCentersRangeModifier implements EstimationModifier { */ public PhaseCentersRangeModifier(final FrequencyPattern stationPattern, final FrequencyPattern satellitePattern) { - this.modifier = new PhaseCentersGroundReceiverBaseModifier<>(stationPattern, satellitePattern); + super(stationPattern, satellitePattern); } /** {@inheritDoc} */ @@ -52,27 +51,11 @@ public List getParametersDrivers() { /** {@inheritDoc} */ @Override public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { - if (estimated.getObservedMeasurement().isTwoWay()) { - modifyTwoWay(estimated); - } else { - modifyOneWay(estimated); - } - } - - /** Apply a modifier to a one-way range measurement. - * @param estimated estimated measurement to modify - */ - private void modifyOneWay(final EstimatedMeasurementBase estimated) { - estimated.setEstimatedValue(estimated.getEstimatedValue()[0] + - modifier.oneWayDistanceModification(estimated)); - } - - /** Apply a modifier to a two-way range measurement. - * @param estimated estimated measurement to modify - */ - private void modifyTwoWay(final EstimatedMeasurementBase estimated) { - estimated.setEstimatedValue(estimated.getEstimatedValue()[0] + - modifier.twoWayDistanceModification(estimated)); + final double delta = estimated.getObservedMeasurement().isTwoWay() ? + twoWayDistanceModification(estimated) : + oneWayDistanceModification(estimated); + estimated.modifyEstimatedValue(this, + estimated.getEstimatedValue()[0] + delta); } } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseIonosphericDelayModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseIonosphericDelayModifier.java index 18b46bb4e6..0428347bab 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseIonosphericDelayModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseIonosphericDelayModifier.java @@ -109,9 +109,7 @@ private > T phaseErrorIonosphericModel(final G */ private double[][] phaseErrorJacobianState(final double[] derivatives, final int freeStateParameters) { final double[][] finiteDifferencesJacobian = new double[1][6]; - for (int i = 0; i < freeStateParameters; i++) { - finiteDifferencesJacobian[0][i] = derivatives[i]; - } + System.arraycopy(derivatives, 0, finiteDifferencesJacobian[0], 0, freeStateParameters); return finiteDifferencesJacobian; } @@ -143,14 +141,7 @@ private double phaseErrorParameterDerivative(final GroundStation station, private double[] phaseErrorParameterDerivative(final double[] derivatives, final int freeStateParameters) { // 0 ... freeStateParameters - 1 -> derivatives of the delay wrt state // freeStateParameters ... n -> derivatives of the delay wrt ionospheric parameters - final int dim = derivatives.length - freeStateParameters; - final double[] phaseError = new double[dim]; - - for (int i = 0; i < dim; i++) { - phaseError[i] = derivatives[freeStateParameters + i]; - } - - return phaseError; + return Arrays.copyOfRange(derivatives, freeStateParameters, derivatives.length); } /** {@inheritDoc} */ @@ -171,7 +162,7 @@ public void modifyWithoutDerivatives(final EstimatedMeasurementBase estim final double[] newValue = estimated.getEstimatedValue(); final double delay = phaseErrorIonosphericModel(station, state); newValue[0] = newValue[0] - delay; - estimated.setEstimatedValue(newValue); + estimated.modifyEstimatedValue(this, newValue); } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseTroposphericDelayModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseTroposphericDelayModifier.java index b19d238e57..0fbef7a047 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseTroposphericDelayModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/PhaseTroposphericDelayModifier.java @@ -28,13 +28,15 @@ import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.GroundStation; import org.orekit.estimation.measurements.gnss.Phase; -import org.orekit.models.earth.troposphere.DiscreteTroposphericModel; +import org.orekit.models.earth.troposphere.TroposphericModel; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; import org.orekit.utils.Differentiation; +import org.orekit.utils.FieldTrackingCoordinates; import org.orekit.utils.ParameterDriver; import org.orekit.utils.ParameterFunction; import org.orekit.utils.TimeSpanMap.Span; +import org.orekit.utils.TrackingCoordinates; /** * Class modifying theoretical phase measurement with tropospheric delay. @@ -47,13 +49,24 @@ public class PhaseTroposphericDelayModifier implements EstimationModifier { /** Tropospheric delay model. */ - private final DiscreteTroposphericModel tropoModel; + private final TroposphericModel tropoModel; /** Constructor. * * @param model Tropospheric delay model appropriate for the current range measurement method. + * @deprecated as of 12.1, replaced by {@link #PhaseTroposphericDelayModifier(TroposphericModel)} */ - public PhaseTroposphericDelayModifier(final DiscreteTroposphericModel model) { + @Deprecated + public PhaseTroposphericDelayModifier(final org.orekit.models.earth.troposphere.DiscreteTroposphericModel model) { + this(new org.orekit.models.earth.troposphere.TroposphericModelAdapter(model)); + } + + /** Constructor. + * + * @param model Tropospheric delay model appropriate for the current range measurement method. + * @since 12.1 + */ + public PhaseTroposphericDelayModifier(final TroposphericModel model) { tropoModel = model; } @@ -65,15 +78,18 @@ public PhaseTroposphericDelayModifier(final DiscreteTroposphericModel model) { */ private double phaseErrorTroposphericModel(final GroundStation station, final SpacecraftState state, final double wavelength) { - // elevation - final double elevation = - station.getBaseFrame().getTrackingCoordinates(state.getPosition(), state.getFrame(), state.getDate()). - getElevation(); + // tracking + final TrackingCoordinates trackingCoordinates = + station.getBaseFrame().getTrackingCoordinates(state.getPosition(), state.getFrame(), state.getDate()); // only consider measures above the horizon - if (elevation > 0) { + if (trackingCoordinates.getElevation() > 0) { // delay in meters - final double delay = tropoModel.pathDelay(elevation, station.getBaseFrame().getPoint(), tropoModel.getParameters(state.getDate()), state.getDate()); + final double delay = tropoModel.pathDelay(trackingCoordinates, + station.getOffsetGeodeticPoint(state.getDate()), + station.getPressureTemperatureHumidity(state.getDate()), + tropoModel.getParameters(state.getDate()), state.getDate()). + getDelay(); return delay / wavelength; } @@ -97,16 +113,19 @@ private > T phaseErrorTroposphericModel(final final Field field = state.getDate().getField(); final T zero = field.getZero(); - // satellite elevation - final T elevation = - station.getBaseFrame().getTrackingCoordinates(state.getPosition(), state.getFrame(), state.getDate()). - getElevation(); + // tracking + final FieldTrackingCoordinates trackingCoordinates = + station.getBaseFrame().getTrackingCoordinates(state.getPosition(), state.getFrame(), state.getDate()); // only consider measures above the horizon - if (elevation.getReal() > 0) { + if (trackingCoordinates.getElevation().getReal() > 0) { // delay in meters - final T delay = tropoModel.pathDelay(elevation, station.getBaseFrame().getPoint(field), parameters, state.getDate()); + final T delay = tropoModel.pathDelay(trackingCoordinates, + station.getOffsetGeodeticPoint(state.getDate()), + station.getPressureTemperatureHumidity(state.getDate()), + parameters, state.getDate()). + getDelay(); return delay.divide(wavelength); } @@ -156,14 +175,7 @@ private double phaseErrorParameterDerivative(final GroundStation station, private double[] phaseErrorParameterDerivative(final double[] derivatives, final int freeStateParameters) { // 0 ... freeStateParameters - 1 -> derivatives of the delay wrt state // freeStateParameters ... n -> derivatives of the delay wrt tropospheric parameters - final int dim = derivatives.length - freeStateParameters; - final double[] rangeError = new double[dim]; - - for (int i = 0; i < dim; i++) { - rangeError[i] = derivatives[freeStateParameters + i]; - } - - return rangeError; + return Arrays.copyOfRange(derivatives, freeStateParameters, derivatives.length); } /** {@inheritDoc} */ @@ -185,7 +197,7 @@ public void modifyWithoutDerivatives(final EstimatedMeasurementBase estim final double[] newValue = estimated.getEstimatedValue(); final double delay = phaseErrorTroposphericModel(station, state, measurement.getWavelength()); newValue[0] = newValue[0] + delay; - estimated.setEstimatedValue(newValue); + estimated.modifyEstimatedValue(this, newValue); } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/RangeIonosphericDelayModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/RangeIonosphericDelayModifier.java index 9a8af72a45..436b133e04 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/RangeIonosphericDelayModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/RangeIonosphericDelayModifier.java @@ -26,11 +26,14 @@ import org.orekit.propagation.SpacecraftState; /** Class modifying theoretical range measurement with ionospheric delay. + *

    * The effect of ionospheric correction on the range is directly computed * through the computation of the ionospheric delay. - * + *

    + *

    * The ionospheric delay depends on the frequency of the signal (GNSS, VLBI, ...). * For optical measurements (e.g. SLR), the ray is not affected by ionosphere charged particles. + *

    *

    * Since 10.0, state derivatives and ionospheric parameters derivates are computed * using automatic differentiation. @@ -58,7 +61,9 @@ public void modifyWithoutDerivatives(final EstimatedMeasurementBase estim final Range measurement = estimated.getObservedMeasurement(); final GroundStation station = measurement.getStation(); - RangeModifierUtil.modifyWithoutDerivatives(estimated, station, this::rangeErrorIonosphericModel); + RangeModifierUtil.modifyWithoutDerivatives(estimated, station, + this::rangeErrorIonosphericModel, + this); } @@ -74,7 +79,8 @@ public void modify(final EstimatedMeasurement estimated) { new ModifierGradientConverter(state, 6, new FrameAlignedProvider(state.getFrame())), station, this::rangeErrorIonosphericModel, - this::rangeErrorIonosphericModel); + this::rangeErrorIonosphericModel, + this); } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/RangeModifierUtil.java b/src/main/java/org/orekit/estimation/measurements/modifiers/RangeModifierUtil.java index ce3804c15f..a264325892 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/RangeModifierUtil.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/RangeModifierUtil.java @@ -21,6 +21,7 @@ import org.hipparchus.analysis.differentiation.Gradient; import org.orekit.estimation.measurements.EstimatedMeasurement; import org.orekit.estimation.measurements.EstimatedMeasurementBase; +import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.GroundStation; import org.orekit.estimation.measurements.ObservedMeasurement; import org.orekit.propagation.FieldSpacecraftState; @@ -49,10 +50,28 @@ private RangeModifierUtil() { * @param station ground station * @param modelEffect model effect * @since 12.0 + * @deprecated as of 12.1, replaced by {@link #modifyWithoutDerivatives(EstimatedMeasurementBase, + * GroundStation, ParametricModelEffect, EstimationModifier)} */ + @Deprecated public static > void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated, final GroundStation station, final ParametricModelEffect modelEffect) { + modifyWithoutDerivatives(estimated, station, modelEffect, null); + } + + /** Apply a modifier to an estimated measurement. + * @param type of the measurement + * @param estimated estimated measurement to modify + * @param station ground station + * @param modelEffect model effect + * @param modifier applied modifier + * @since 12.1 + */ + public static > void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated, + final GroundStation station, + final ParametricModelEffect modelEffect, + final EstimationModifier modifier) { final SpacecraftState state = estimated.getStates()[0]; final double[] oldValue = estimated.getEstimatedValue(); @@ -62,7 +81,7 @@ public static > void modifyWithoutDerivatives(f final double[] newValue = oldValue.clone(); final double delay = modelEffect.evaluate(station, state); newValue[0] = newValue[0] + delay; - estimated.setEstimatedValue(newValue); + estimated.modifyEstimatedValue(modifier, newValue); } @@ -74,13 +93,37 @@ public static > void modifyWithoutDerivatives(f * @param parametricModel parametric modifier model * @param modelEffect model effect * @param modelEffectGradient model effect gradient + * @deprecated as of 12.1, replaced by {@link #modify(EstimatedMeasurement, + * ParameterDriversProvider, AbstractGradientConverter, GroundStation, + * ParametricModelEffect, ParametricModelEffectGradient, EstimationModifier)} */ + @Deprecated public static > void modify(final EstimatedMeasurement estimated, final ParameterDriversProvider parametricModel, final AbstractGradientConverter converter, final GroundStation station, final ParametricModelEffect modelEffect, final ParametricModelEffectGradient modelEffectGradient) { + modify(estimated, parametricModel, converter, station, modelEffect, modelEffectGradient, null); + } + + /** Apply a modifier to an estimated measurement. + * @param type of the measurement + * @param estimated estimated measurement to modify + * @param station ground station + * @param converter gradient converter + * @param parametricModel parametric modifier model + * @param modelEffect model effect + * @param modelEffectGradient model effect gradient + * @param modifier applied modifier + */ + public static > void modify(final EstimatedMeasurement estimated, + final ParameterDriversProvider parametricModel, + final AbstractGradientConverter converter, + final GroundStation station, + final ParametricModelEffect modelEffect, + final ParametricModelEffectGradient modelEffectGradient, + final EstimationModifier modifier) { final SpacecraftState state = estimated.getStates()[0]; @@ -126,7 +169,7 @@ public static > void modify(final EstimatedMeas } // update estimated value taking into account the ionospheric delay. - modifyWithoutDerivatives(estimated, station, modelEffect); + modifyWithoutDerivatives(estimated, station, modelEffect, modifier); } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/RangeRateIonosphericDelayModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/RangeRateIonosphericDelayModifier.java index 36df51ef16..af035a5852 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/RangeRateIonosphericDelayModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/RangeRateIonosphericDelayModifier.java @@ -28,12 +28,15 @@ import org.orekit.propagation.SpacecraftState; /** Class modifying theoretical range-rate measurement with ionospheric delay. + *

    * The effect of ionospheric correction on the range-rate is directly computed * through the computation of the ionospheric delay difference with respect to * time. - * + *

    + *

    * The ionospheric delay depends on the frequency of the signal (GNSS, VLBI, ...). * For optical measurements (e.g. SLR), the ray is not affected by ionosphere charged particles. + *

    *

    * Since 10.0, state derivatives and ionospheric parameters derivates are computed * using automatic differentiation. @@ -84,7 +87,9 @@ public void modifyWithoutDerivatives(final EstimatedMeasurementBase e final RangeRate measurement = estimated.getObservedMeasurement(); final GroundStation station = measurement.getStation(); - RangeModifierUtil.modifyWithoutDerivatives(estimated, station, this::rangeRateErrorIonosphericModel); + RangeModifierUtil.modifyWithoutDerivatives(estimated, station, + this::rangeRateErrorIonosphericModel, + this); } @@ -100,7 +105,8 @@ public void modify(final EstimatedMeasurement estimated) { new ModifierGradientConverter(state, 6, new FrameAlignedProvider(state.getFrame())), station, this::rangeRateErrorIonosphericModel, - this::rangeRateErrorIonosphericModel); + this::rangeRateErrorIonosphericModel, + this); } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/RangeRateModifierUtil.java b/src/main/java/org/orekit/estimation/measurements/modifiers/RangeRateModifierUtil.java index c42dd5749a..8b4c03524f 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/RangeRateModifierUtil.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/RangeRateModifierUtil.java @@ -21,6 +21,7 @@ import org.hipparchus.analysis.differentiation.Gradient; import org.orekit.estimation.measurements.EstimatedMeasurement; import org.orekit.estimation.measurements.EstimatedMeasurementBase; +import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.GroundStation; import org.orekit.estimation.measurements.ObservedMeasurement; import org.orekit.propagation.FieldSpacecraftState; @@ -47,10 +48,28 @@ private RangeRateModifierUtil() { * @param estimated estimated measurement to modify * @param station ground station * @param modelEffect model effect + * @deprecated as of 12.1, replaced by {@link #modifyWithoutDerivatives(EstimatedMeasurementBase, + * GroundStation, ParametricModelEffect, EstimationModifier)} */ + @Deprecated public static > void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated, final GroundStation station, final ParametricModelEffect modelEffect) { + modifyWithoutDerivatives(estimated, station, modelEffect, null); + } + + /** Apply a modifier to an estimated measurement. + * @param type of the measurement + * @param estimated estimated measurement to modify + * @param station ground station + * @param modelEffect model effect + * @param modifier applied modifier + * @since 12.1 + */ + public static > void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated, + final GroundStation station, + final ParametricModelEffect modelEffect, + final EstimationModifier modifier) { final SpacecraftState state = estimated.getStates()[0]; @@ -59,7 +78,7 @@ public static > void modifyWithoutDerivatives(f final double[] newValue = estimated.getEstimatedValue(); final double delay = modelEffect.evaluate(station, state); newValue[0] = newValue[0] + delay; - estimated.setEstimatedValue(newValue); + estimated.modifyEstimatedValue(modifier, newValue); } @@ -71,13 +90,40 @@ public static > void modifyWithoutDerivatives(f * @param parametricModel parametric modifier model * @param modelEffect model effect * @param modelEffectGradient model effect gradient + * @deprecated as of 12.1, replaced by {@link #modify(EstimatedMeasurement, + * ParameterDriversProvider, AbstractGradientConverter, GroundStation, + * ParametricModelEffect, ParametricModelEffectGradient, EstimationModifier)} */ + @Deprecated public static > void modify(final EstimatedMeasurement estimated, final ParameterDriversProvider parametricModel, final AbstractGradientConverter converter, final GroundStation station, final ParametricModelEffect modelEffect, final ParametricModelEffectGradient modelEffectGradient) { + modify(estimated, parametricModel, converter, station, + modelEffect, modelEffectGradient, null); + + } + + /** Apply a modifier to an estimated measurement. + * @param type of the measurement + * @param estimated estimated measurement to modify + * @param station ground station + * @param converter gradient converter + * @param parametricModel parametric modifier model + * @param modelEffect model effect + * @param modelEffectGradient model effect gradient + * @param modifier applied modifier + * @since 12.1 + */ + public static > void modify(final EstimatedMeasurement estimated, + final ParameterDriversProvider parametricModel, + final AbstractGradientConverter converter, + final GroundStation station, + final ParametricModelEffect modelEffect, + final ParametricModelEffectGradient modelEffectGradient, + final EstimationModifier modifier) { final SpacecraftState state = estimated.getStates()[0]; @@ -126,7 +172,7 @@ public static > void modify(final EstimatedMeas } // update estimated value taking into account the ionospheric delay. - modifyWithoutDerivatives(estimated, station, modelEffect); + modifyWithoutDerivatives(estimated, station, modelEffect, modifier); } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/RangeRateTroposphericDelayModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/RangeRateTroposphericDelayModifier.java index aeca65fd11..dd83258ee9 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/RangeRateTroposphericDelayModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/RangeRateTroposphericDelayModifier.java @@ -23,17 +23,20 @@ import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.GroundStation; import org.orekit.estimation.measurements.RangeRate; -import org.orekit.models.earth.troposphere.DiscreteTroposphericModel; +import org.orekit.models.earth.troposphere.TroposphericModel; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; /** Class modifying theoretical range-rate measurements with tropospheric delay. + *

    * The effect of tropospheric correction on the range-rate is directly computed * through the computation of the tropospheric delay difference with respect to * time. - * + *

    + *

    * In general, for GNSS, VLBI, ... there is hardly any frequency dependence in the delay. * For SLR techniques however, the frequency dependence is sensitive. + *

    * * @author Joris Olympio * @since 8.0 @@ -43,12 +46,30 @@ public class RangeRateTroposphericDelayModifier extends BaseRangeRateTropospheri /** Two-way measurement factor. */ private final double fTwoWay; + /** Constructor. + * + * @param model Tropospheric delay model appropriate for the current range-rate measurement method. + * @param tw Flag indicating whether the measurement is two-way. + * @deprecated as of 12.1, replaced byb {@link #RangeRateTroposphericDelayModifier(TroposphericModel, boolean)} + */ + @Deprecated + public RangeRateTroposphericDelayModifier(final org.orekit.models.earth.troposphere.DiscreteTroposphericModel model, + final boolean tw) { + super(model); + if (tw) { + fTwoWay = 2.; + } else { + fTwoWay = 1.; + } + } + /** Constructor. * * @param model Tropospheric delay model appropriate for the current range-rate measurement method. * @param tw Flag indicating whether the measurement is two-way. + * @since 12.1 */ - public RangeRateTroposphericDelayModifier(final DiscreteTroposphericModel model, final boolean tw) { + public RangeRateTroposphericDelayModifier(final TroposphericModel model, final boolean tw) { super(model); if (tw) { fTwoWay = 2.; @@ -90,7 +111,7 @@ public void modifyWithoutDerivatives(final EstimatedMeasurementBase e final RangeRate measurement = estimated.getObservedMeasurement(); final GroundStation station = measurement.getStation(); - RangeRateModifierUtil.modifyWithoutDerivatives(estimated, station, this::rangeRateErrorTroposphericModel); + RangeRateModifierUtil.modifyWithoutDerivatives(estimated, station, this::rangeRateErrorTroposphericModel, this); } @@ -107,7 +128,8 @@ public void modify(final EstimatedMeasurement estimated) { new ModifierGradientConverter(state, 6, new FrameAlignedProvider(state.getFrame())), station, this::rangeRateErrorTroposphericModel, - this::rangeRateErrorTroposphericModel); + this::rangeRateErrorTroposphericModel, + this); } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/RangeTroposphericDelayModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/RangeTroposphericDelayModifier.java index edd84a1262..1d0c156782 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/RangeTroposphericDelayModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/RangeTroposphericDelayModifier.java @@ -22,15 +22,18 @@ import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.GroundStation; import org.orekit.estimation.measurements.Range; -import org.orekit.models.earth.troposphere.DiscreteTroposphericModel; +import org.orekit.models.earth.troposphere.TroposphericModel; import org.orekit.propagation.SpacecraftState; /** Class modifying theoretical range measurement with tropospheric delay. + *

    * The effect of tropospheric correction on the range is directly computed * through the computation of the tropospheric delay. - * + *

    + *

    * In general, for GNSS, VLBI, ... there is hardly any frequency dependence in the delay. * For SLR techniques however, the frequency dependence is sensitive. + *

    * * @author Maxime Journot * @author Joris Olympio @@ -41,8 +44,19 @@ public class RangeTroposphericDelayModifier extends BaseRangeTroposphericDelayMo /** Constructor. * * @param model Tropospheric delay model appropriate for the current range measurement method. + * @deprecated as of 12.1, replaced by {@link #RangeTroposphericDelayModifier(TroposphericModel)} + */ + @Deprecated + public RangeTroposphericDelayModifier(final org.orekit.models.earth.troposphere.DiscreteTroposphericModel model) { + this(new org.orekit.models.earth.troposphere.TroposphericModelAdapter(model)); + } + + /** Constructor. + * + * @param model Tropospheric delay model appropriate for the current range measurement method. + * @since 12.1 */ - public RangeTroposphericDelayModifier(final DiscreteTroposphericModel model) { + public RangeTroposphericDelayModifier(final TroposphericModel model) { super(model); } @@ -53,7 +67,9 @@ public void modifyWithoutDerivatives(final EstimatedMeasurementBase estim final Range measurement = estimated.getObservedMeasurement(); final GroundStation station = measurement.getStation(); - RangeModifierUtil.modifyWithoutDerivatives(estimated, station, this::rangeErrorTroposphericModel); + RangeModifierUtil.modifyWithoutDerivatives(estimated, station, + this::rangeErrorTroposphericModel, + this); } @@ -70,7 +86,8 @@ public void modify(final EstimatedMeasurement estimated) { new ModifierGradientConverter(state, 6, new FrameAlignedProvider(state.getFrame())), station, this::rangeErrorTroposphericModel, - this::rangeErrorTroposphericModel); + this::rangeErrorTroposphericModel, + this); } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockInterSatellitesOneWayRangeRateModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockInterSatellitesOneWayRangeRateModifier.java new file mode 100644 index 0000000000..c71a7fe44c --- /dev/null +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockInterSatellitesOneWayRangeRateModifier.java @@ -0,0 +1,54 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.estimation.measurements.modifiers; + +import org.orekit.estimation.measurements.EstimatedMeasurementBase; +import org.orekit.estimation.measurements.gnss.InterSatellitesOneWayRangeRate; +import org.orekit.propagation.SpacecraftState; + +/** Class modifying theoretical range-rate measurement with relativistic frequency deviation. + *

    + * Relativistic clock correction is caused by the motion of the satellite as well as + * the change in the gravitational potential + *

    + * @author Luc Maisonobe + * @since 12.1 + * + * @see "Teunissen, Peter, and Oliver Montenbruck, eds. Springer handbook of global navigation + * satellite systems. Chapter 19.2. Springer, 2017." + */ +public class RelativisticClockInterSatellitesOneWayRangeRateModifier + extends AbstractRelativisticClockOnBoardRangeRateModifier { + + /** Simple constructor. + * @param gm gravitational constant for main body in signal path vicinity. + */ + public RelativisticClockInterSatellitesOneWayRangeRateModifier(final double gm) { + super(gm); + } + + /** {@inheritDoc} */ + @Override + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { + final SpacecraftState local = estimated.getStates()[0]; + final SpacecraftState remote = estimated.getStates()[1]; + modifyWithoutDerivatives(estimated, + local.getA(), local.getPosition().getNorm(), + remote.getA(), remote.getPosition().getNorm()); + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockInterSatellitesPhaseModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockInterSatellitesPhaseModifier.java index bc45a32449..e69d589269 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockInterSatellitesPhaseModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockInterSatellitesPhaseModifier.java @@ -62,7 +62,7 @@ public void modifyWithoutDerivatives(final EstimatedMeasurementBase + * Relativistic clock correction is caused by the motion of the satellite as well as + * the change in the gravitational potential + *

    + * @author Luc Maisonobe + * @since 12.1 + * + * @see "Teunissen, Peter, and Oliver Montenbruck, eds. Springer handbook of global navigation + * satellite systems. Chapter 19.2. Springer, 2017." + */ +public class RelativisticClockOneWayGNSSRangeRateModifier + extends AbstractRelativisticClockOnBoardRangeRateModifier { + + /** Simple constructor. + * @param gm gravitational constant for main body in signal path vicinity. + */ + public RelativisticClockOneWayGNSSRangeRateModifier(final double gm) { + super(gm); + } + + /** {@inheritDoc} */ + @Override + public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { + + // local satellite + final SpacecraftState local = estimated.getStates()[0]; + + // compute semi major axis of remote (transmitter) satellite + final PVCoordinates remote = estimated.getParticipants()[0]; + final double rRemote = remote.getPosition().getNorm(); + final double vRemote = remote.getVelocity().getNorm(); + final double aRemote = 1.0 / (2 / rRemote - vRemote * vRemote / getGm()); + + modifyWithoutDerivatives(estimated, + local.getA(), local.getPosition().getNorm(), + aRemote, rRemote); + + } + +} diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockPhaseModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockPhaseModifier.java index 70b91a4b47..97b31d71b9 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockPhaseModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockPhaseModifier.java @@ -62,7 +62,7 @@ public void modifyWithoutDerivatives(final EstimatedMeasurementBase estim final double cOverLambda = Constants.SPEED_OF_LIGHT / wavelength; final double[] newValue = estimated.getEstimatedValue().clone(); newValue[0] = newValue[0] - dtRel * cOverLambda; - estimated.setEstimatedValue(newValue); + estimated.modifyEstimatedValue(this, newValue); } } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockRangeModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockRangeModifier.java index 824760b186..eed1913a94 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockRangeModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockRangeModifier.java @@ -58,7 +58,7 @@ public void modifyWithoutDerivatives(final EstimatedMeasurementBase estim // Update estimated value taking into account the relativistic effect. final double[] newValue = estimated.getEstimatedValue().clone(); newValue[0] = newValue[0] - dtRel * Constants.SPEED_OF_LIGHT; - estimated.setEstimatedValue(newValue); + estimated.modifyEstimatedValue(this, newValue); } } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockRangeRateModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockRangeRateModifier.java index 69d3486439..ddf5cd37ff 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockRangeRateModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticClockRangeRateModifier.java @@ -70,7 +70,7 @@ public void modifyWithoutDerivatives(final EstimatedMeasurementBase e // Update estimated value taking into account the relativistic effect. final double[] newValue = estimated.getEstimatedValue().clone(); newValue[0] = newValue[0] + dfRel * Constants.SPEED_OF_LIGHT; - estimated.setEstimatedValue(newValue); + estimated.modifyEstimatedValue(this, newValue); } /** Returns the inverse of the given value. diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockInterSatellitesPhaseModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockInterSatellitesPhaseModifier.java index c51c2c83df..0a351268bb 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockInterSatellitesPhaseModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockInterSatellitesPhaseModifier.java @@ -77,7 +77,7 @@ public void modifyWithoutDerivatives(final EstimatedMeasurementBase estim final double cOverLambda = Constants.SPEED_OF_LIGHT / wavelength; final double[] newValue = estimated.getEstimatedValue().clone(); newValue[0] = newValue[0] - dtJ2 * cOverLambda; - estimated.setEstimatedValue(newValue); + estimated.modifyEstimatedValue(this, newValue); } } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockRangeModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockRangeModifier.java index fb645a893e..68560a84e1 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockRangeModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockRangeModifier.java @@ -72,7 +72,7 @@ public void modifyWithoutDerivatives(final EstimatedMeasurementBase estim // Update estimated value taking into account the relativistic effect. final double[] newValue = estimated.getEstimatedValue().clone(); newValue[0] = newValue[0] - dtJ2 * Constants.SPEED_OF_LIGHT; - estimated.setEstimatedValue(newValue); + estimated.modifyEstimatedValue(this, newValue); } } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroInterSatellitePhaseModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroInterSatellitePhaseModifier.java index 5aa8741a0b..bb0e8f5ca3 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroInterSatellitePhaseModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroInterSatellitePhaseModifier.java @@ -57,7 +57,7 @@ public void modifyWithoutDerivatives(final EstimatedMeasurementBase getParametersDrivers() { /** {@inheritDoc} */ @Override public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { - doModify(estimated); + doModify(this, estimated); } } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroOneWayGNSSPhaseModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroOneWayGNSSPhaseModifier.java index e03df8a565..7295ea0953 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroOneWayGNSSPhaseModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroOneWayGNSSPhaseModifier.java @@ -57,7 +57,7 @@ public void modifyWithoutDerivatives(final EstimatedMeasurementBase getParametersDrivers() { /** {@inheritDoc} */ @Override public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { - doModify(estimated); + doModify(this, estimated); } } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroPhaseModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroPhaseModifier.java index dbce3fbc51..3095226d60 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroPhaseModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroPhaseModifier.java @@ -62,7 +62,7 @@ public void modifyWithoutDerivatives(final EstimatedMeasurementBase estim // update estimated value taking into account the Shapiro time delay. final double[] newValue = estimated.getEstimatedValue().clone(); newValue[0] = newValue[0] + correction; - estimated.setEstimatedValue(newValue); + estimated.modifyEstimatedValue(this, newValue); } } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroRangeModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroRangeModifier.java index 66576f028d..5d989f50ef 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroRangeModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/ShapiroRangeModifier.java @@ -50,7 +50,7 @@ public List getParametersDrivers() { /** {@inheritDoc} */ @Override public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { - doModify(estimated); + doModify(this, estimated); } } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/TDOAIonosphericDelayModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/TDOAIonosphericDelayModifier.java index 7d8d4f5766..9080d89458 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/TDOAIonosphericDelayModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/TDOAIonosphericDelayModifier.java @@ -107,7 +107,9 @@ public void modifyWithoutDerivatives(final EstimatedMeasurementBase estima final GroundStation primeStation = measurement.getPrimeStation(); final GroundStation secondStation = measurement.getSecondStation(); - TDOAModifierUtil.modifyWithoutDerivatives(estimated, primeStation, secondStation, this::timeErrorIonosphericModel); + TDOAModifierUtil.modifyWithoutDerivatives(estimated, primeStation, secondStation, + this::timeErrorIonosphericModel, + this); } @@ -123,7 +125,8 @@ public void modify(final EstimatedMeasurement estimated) { new ModifierGradientConverter(state, 6, new FrameAlignedProvider(state.getFrame())), primeStation, secondStation, this::timeErrorIonosphericModel, - this::timeErrorIonosphericModel); + this::timeErrorIonosphericModel, + this); } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/TDOAModifierUtil.java b/src/main/java/org/orekit/estimation/measurements/modifiers/TDOAModifierUtil.java index 8c141782f6..b05a7a7a88 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/TDOAModifierUtil.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/TDOAModifierUtil.java @@ -21,6 +21,7 @@ import org.hipparchus.analysis.differentiation.Gradient; import org.orekit.estimation.measurements.EstimatedMeasurement; import org.orekit.estimation.measurements.EstimatedMeasurementBase; +import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.GroundStation; import org.orekit.estimation.measurements.ObservedMeasurement; import org.orekit.propagation.FieldSpacecraftState; @@ -48,11 +49,31 @@ private TDOAModifierUtil() { * @param primeStation prime station * @param secondStation second station * @param modelEffect model effect + * @deprecated as of 12.1, replaced by {@link #modifyWithoutDerivatives(EstimatedMeasurementBase, + * GroundStation, GroundStation, ParametricModelEffect, EstimationModifier)} */ + @Deprecated public static > void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated, final GroundStation primeStation, final GroundStation secondStation, final ParametricModelEffect modelEffect) { + modifyWithoutDerivatives(estimated, primeStation, secondStation, modelEffect, null); + } + + /** Apply a modifier to an estimated measurement. + * @param type of the measurement + * @param estimated estimated measurement to modify + * @param primeStation prime station + * @param secondStation second station + * @param modelEffect model effect + * @param modifier applied modifier + * @since 12.1 + */ + public static > void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated, + final GroundStation primeStation, + final GroundStation secondStation, + final ParametricModelEffect modelEffect, + final EstimationModifier modifier) { final SpacecraftState state = estimated.getStates()[0]; final double[] oldValue = estimated.getEstimatedValue(); @@ -64,7 +85,7 @@ public static > void modifyWithoutDerivatives(f final double[] newValue = oldValue.clone(); newValue[0] += primeDelay; newValue[0] -= secondDelay; - estimated.setEstimatedValue(newValue); + estimated.modifyEstimatedValue(modifier, newValue); } /** Apply a modifier to an estimated measurement. @@ -76,13 +97,40 @@ public static > void modifyWithoutDerivatives(f * @param parametricModel parametric modifier model * @param modelEffect model effect * @param modelEffectGradient model effect gradient + * @deprecated as of 12.1, replaced by {@link #modify(EstimatedMeasurement, + * ParameterDriversProvider, AbstractGradientConverter, GroundStation, GroundStation, + * ParametricModelEffect, ParametricModelEffectGradient, EstimationModifier)} */ + @Deprecated public static > void modify(final EstimatedMeasurement estimated, final ParameterDriversProvider parametricModel, final AbstractGradientConverter converter, final GroundStation primeStation, final GroundStation secondStation, final ParametricModelEffect modelEffect, final ParametricModelEffectGradient modelEffectGradient) { + modify(estimated, parametricModel, converter, primeStation, secondStation, + modelEffect, modelEffectGradient, null); + } + + /** Apply a modifier to an estimated measurement. + * @param type of the measurement + * @param estimated estimated measurement to modify + * @param primeStation prime station + * @param secondStation second station + * @param converter gradient converter + * @param parametricModel parametric modifier model + * @param modelEffect model effect + * @param modelEffectGradient model effect gradient + * @param modifier applied modifier + * @since 12.1 + */ + public static > void modify(final EstimatedMeasurement estimated, + final ParameterDriversProvider parametricModel, + final AbstractGradientConverter converter, + final GroundStation primeStation, final GroundStation secondStation, + final ParametricModelEffect modelEffect, + final ParametricModelEffectGradient modelEffectGradient, + final EstimationModifier modifier) { final SpacecraftState state = estimated.getStates()[0]; final double[] oldValue = estimated.getEstimatedValue(); @@ -155,7 +203,7 @@ public static > void modify(final EstimatedMeas final double[] newValue = oldValue.clone(); newValue[0] += primeGDelay.getReal(); newValue[0] -= secondGDelay.getReal(); - estimated.setEstimatedValue(newValue); + estimated.modifyEstimatedValue(modifier, newValue); } } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/TDOATroposphericDelayModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/TDOATroposphericDelayModifier.java index c78d197fff..4dc0fb0542 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/TDOATroposphericDelayModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/TDOATroposphericDelayModifier.java @@ -28,11 +28,13 @@ import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.GroundStation; import org.orekit.estimation.measurements.TDOA; -import org.orekit.models.earth.troposphere.DiscreteTroposphericModel; +import org.orekit.models.earth.troposphere.TroposphericModel; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; import org.orekit.utils.Constants; +import org.orekit.utils.FieldTrackingCoordinates; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TrackingCoordinates; /** Class modifying theoretical TDOA measurements with tropospheric delay. *

    @@ -47,13 +49,24 @@ public class TDOATroposphericDelayModifier implements EstimationModifier { /** Tropospheric delay model. */ - private final DiscreteTroposphericModel tropoModel; + private final TroposphericModel tropoModel; /** Constructor. * * @param model tropospheric model appropriate for the current TDOA measurement method. + * @deprecated as of 12.1, replaced by {@link #TDOATroposphericDelayModifier(TroposphericModel)} */ - public TDOATroposphericDelayModifier(final DiscreteTroposphericModel model) { + @Deprecated + public TDOATroposphericDelayModifier(final org.orekit.models.earth.troposphere.DiscreteTroposphericModel model) { + this(new org.orekit.models.earth.troposphere.TroposphericModelAdapter(model)); + } + + /** Constructor. + * + * @param model tropospheric model appropriate for the current TDOA measurement method. + * @since 12.1 + */ + public TDOATroposphericDelayModifier(final TroposphericModel model) { tropoModel = model; } @@ -65,16 +78,18 @@ public TDOATroposphericDelayModifier(final DiscreteTroposphericModel model) { private double timeErrorTroposphericModel(final GroundStation station, final SpacecraftState state) { final Vector3D position = state.getPosition(); - // elevation - final double elevation = - station.getBaseFrame().getTrackingCoordinates(position, state.getFrame(), state.getDate()). - getElevation(); + // tracking + final TrackingCoordinates trackingCoordinates = + station.getBaseFrame().getTrackingCoordinates(position, state.getFrame(), state.getDate()); // only consider measurements above the horizon - if (elevation > 0) { + if (trackingCoordinates.getElevation() > 0) { // Delay in meters - final double delay = tropoModel.pathDelay(elevation, station.getBaseFrame().getPoint(), - tropoModel.getParameters(state.getDate()), state.getDate()); + final double delay = tropoModel.pathDelay(trackingCoordinates, + station.getOffsetGeodeticPoint(state.getDate()), + station.getPressureTemperatureHumidity(state.getDate()), + tropoModel.getParameters(state.getDate()), state.getDate()). + getDelay(); // return delay in seconds return delay / Constants.SPEED_OF_LIGHT; } @@ -96,17 +111,19 @@ private > T timeErrorTroposphericModel(final G final Field field = state.getDate().getField(); final T zero = field.getZero(); - // elevation + // tracking final FieldVector3D pos = state.getPosition(); - final T elevation = - station.getBaseFrame().getTrackingCoordinates(pos, state.getFrame(), state.getDate()). - getElevation(); + final FieldTrackingCoordinates trackingCoordinates = + station.getBaseFrame().getTrackingCoordinates(pos, state.getFrame(), state.getDate()); // only consider measurements above the horizon - if (elevation.getReal() > 0) { + if (trackingCoordinates.getElevation().getReal() > 0) { // delay in meters - final T delay = tropoModel.pathDelay(elevation, station.getBaseFrame().getPoint(field), - parameters, state.getDate()); + final T delay = tropoModel.pathDelay(trackingCoordinates, + station.getOffsetGeodeticPoint(state.getDate()), + station.getPressureTemperatureHumidity(state.getDate()), + parameters, state.getDate()). + getDelay(); // return delay in seconds return delay.divide(Constants.SPEED_OF_LIGHT); } @@ -128,7 +145,8 @@ public void modifyWithoutDerivatives(final EstimatedMeasurementBase estima final GroundStation primeStation = measurement.getPrimeStation(); final GroundStation secondStation = measurement.getSecondStation(); - TDOAModifierUtil.modifyWithoutDerivatives(estimated, primeStation, secondStation, this::timeErrorTroposphericModel); + TDOAModifierUtil.modifyWithoutDerivatives(estimated, primeStation, secondStation, + this::timeErrorTroposphericModel, this); } @@ -145,7 +163,8 @@ public void modify(final EstimatedMeasurement estimated) { new ModifierGradientConverter(state, 6, new FrameAlignedProvider(state.getFrame())), primeStation, secondStation, this::timeErrorTroposphericModel, - this::timeErrorTroposphericModel); + this::timeErrorTroposphericModel, + this); } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/TurnAroundRangeIonosphericDelayModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/TurnAroundRangeIonosphericDelayModifier.java index c599cd858b..a0a3f7607b 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/TurnAroundRangeIonosphericDelayModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/TurnAroundRangeIonosphericDelayModifier.java @@ -38,11 +38,14 @@ import org.orekit.utils.TimeSpanMap.Span; /** Class modifying theoretical TurnAroundRange measurement with ionospheric delay. + *

    * The effect of ionospheric correction on the TurnAroundRange is directly computed * through the computation of the ionospheric delay. - * + *

    + *

    * The ionospheric delay depends on the frequency of the signal (GNSS, VLBI, ...). * For optical measurements (e.g. SLR), the ray is not affected by ionosphere charged particles. + *

    *

    * Since 10.0, state derivatives and ionospheric parameters derivates are computed * using automatic differentiation. @@ -79,8 +82,7 @@ private double rangeErrorIonosphericModel(final GroundStation station, // Base frame associated with the station final TopocentricFrame baseFrame = station.getBaseFrame(); // Delay in meters - final double delay = ionoModel.pathDelay(state, baseFrame, frequency, ionoModel.getParameters(state.getDate())); - return delay; + return ionoModel.pathDelay(state, baseFrame, frequency, ionoModel.getParameters(state.getDate())); } /** Compute the measurement error due to ionosphere. @@ -96,8 +98,7 @@ private > T rangeErrorIonosphericModel(final G // Base frame associated with the station final TopocentricFrame baseFrame = station.getBaseFrame(); // Delay in meters - final T delay = ionoModel.pathDelay(state, baseFrame, frequency, parameters); - return delay; + return ionoModel.pathDelay(state, baseFrame, frequency, parameters); } /** Compute the Jacobian of the delay term wrt state using @@ -150,14 +151,7 @@ public double value(final ParameterDriver parameterDriver, final AbsoluteDate da private double[] rangeErrorParameterDerivative(final double[] derivatives, final int freeStateParameters) { // 0 ... freeStateParameters - 1 -> derivatives of the delay wrt state // freeStateParameters ... n -> derivatives of the delay wrt ionospheric parameters - final int dim = derivatives.length - freeStateParameters; - final double[] rangeError = new double[dim]; - - for (int i = 0; i < dim; i++) { - rangeError[i] = derivatives[freeStateParameters + i]; - } - - return rangeError; + return Arrays.copyOfRange(derivatives, freeStateParameters, derivatives.length); } /** {@inheritDoc} */ @@ -180,7 +174,7 @@ public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { // The ionospheric delay is directly added to the TurnAroundRange. final double[] newValue = estimated.getEstimatedValue(); newValue[0] = newValue[0] + primaryGDelay.getReal() + secondaryGDelay.getReal(); - estimated.setEstimatedValue(newValue); + estimated.modifyEstimatedValue(this, newValue); } } diff --git a/src/main/java/org/orekit/estimation/measurements/modifiers/TurnAroundRangeTroposphericDelayModifier.java b/src/main/java/org/orekit/estimation/measurements/modifiers/TurnAroundRangeTroposphericDelayModifier.java index 4ccbcc905b..74c542b40d 100644 --- a/src/main/java/org/orekit/estimation/measurements/modifiers/TurnAroundRangeTroposphericDelayModifier.java +++ b/src/main/java/org/orekit/estimation/measurements/modifiers/TurnAroundRangeTroposphericDelayModifier.java @@ -30,21 +30,26 @@ import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.GroundStation; import org.orekit.estimation.measurements.TurnAroundRange; -import org.orekit.models.earth.troposphere.DiscreteTroposphericModel; +import org.orekit.models.earth.troposphere.TroposphericModel; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; import org.orekit.time.AbsoluteDate; import org.orekit.utils.Differentiation; +import org.orekit.utils.FieldTrackingCoordinates; import org.orekit.utils.ParameterDriver; import org.orekit.utils.ParameterFunction; import org.orekit.utils.TimeSpanMap.Span; +import org.orekit.utils.TrackingCoordinates; /** Class modifying theoretical turn-around TurnAroundRange measurement with tropospheric delay. + *

    * The effect of tropospheric correction on the TurnAroundRange is directly computed * through the computation of the tropospheric delay. - * + *

    + *

    * In general, for GNSS, VLBI, ... there is hardly any frequency dependence in the delay. * For SLR techniques however, the frequency dependence is sensitive. + *

    * * @author Maxime Journot * @since 9.0 @@ -52,13 +57,24 @@ public class TurnAroundRangeTroposphericDelayModifier implements EstimationModifier { /** Tropospheric delay model. */ - private final DiscreteTroposphericModel tropoModel; + private final TroposphericModel tropoModel; + + /** Constructor. + * + * @param model Tropospheric delay model appropriate for the current TurnAroundRange measurement method. + * @deprecated as of 12.1, replaced by {@link #TurnAroundRangeTroposphericDelayModifier(TroposphericModel)} + */ + @Deprecated + public TurnAroundRangeTroposphericDelayModifier(final org.orekit.models.earth.troposphere.DiscreteTroposphericModel model) { + this(new org.orekit.models.earth.troposphere.TroposphericModelAdapter(model)); + } /** Constructor. * * @param model Tropospheric delay model appropriate for the current TurnAroundRange measurement method. + * @since 12.1 */ - public TurnAroundRangeTroposphericDelayModifier(final DiscreteTroposphericModel model) { + public TurnAroundRangeTroposphericDelayModifier(final TroposphericModel model) { tropoModel = model; } @@ -71,17 +87,18 @@ private double rangeErrorTroposphericModel(final GroundStation station, final Sp // final Vector3D position = state.getPosition(); - // elevation - final double elevation = - station.getBaseFrame().getTrackingCoordinates(position, state.getFrame(), state.getDate()). - getElevation(); + // tracking + final TrackingCoordinates trackingCoordinates = + station.getBaseFrame().getTrackingCoordinates(position, state.getFrame(), state.getDate()); // only consider measures above the horizon - if (elevation > 0) { + if (trackingCoordinates.getElevation() > 0) { // Delay in meters - final double delay = tropoModel.pathDelay(elevation, station.getBaseFrame().getPoint(), tropoModel.getParameters(state.getDate()), state.getDate()); - - return delay; + return tropoModel.pathDelay(trackingCoordinates, + station.getOffsetGeodeticPoint(state.getDate()), + station.getPressureTemperatureHumidity(state.getDate()), + tropoModel.getParameters(state.getDate()), state.getDate()). + getDelay(); } return 0; @@ -103,16 +120,17 @@ private > T rangeErrorTroposphericModel(final // final FieldVector3D position = state.getPosition(); - final T dsElevation = - station.getBaseFrame().getTrackingCoordinates(position, state.getFrame(), state.getDate()). - getElevation(); + final FieldTrackingCoordinates trackingCoordinates = + station.getBaseFrame().getTrackingCoordinates(position, state.getFrame(), state.getDate()); // only consider measures above the horizon - if (dsElevation.getReal() > 0) { + if (trackingCoordinates.getElevation().getReal() > 0) { // Delay in meters - final T delay = tropoModel.pathDelay(dsElevation, station.getBaseFrame().getPoint(field), parameters, state.getDate()); - - return delay; + return tropoModel.pathDelay(trackingCoordinates, + station.getOffsetGeodeticPoint(state.getDate()), + station.getPressureTemperatureHumidity(state.getDate()), + parameters, state.getDate()). + getDelay(); } return zero; @@ -167,14 +185,7 @@ public double value(final ParameterDriver parameterDriver, final AbsoluteDate da private double[] rangeErrorParameterDerivative(final double[] derivatives, final int freeStateParameters) { // 0 ... freeStateParameters - 1 -> derivatives of the delay wrt state // freeStateParameters ... n -> derivatives of the delay wrt tropospheric parameters - final int dim = derivatives.length - freeStateParameters; - final double[] rangeError = new double[dim]; - - for (int i = 0; i < dim; i++) { - rangeError[i] = derivatives[freeStateParameters + i]; - } - - return rangeError; + return Arrays.copyOfRange(derivatives, freeStateParameters, derivatives.length); } /** {@inheritDoc} */ @@ -198,7 +209,7 @@ public void modifyWithoutDerivatives(final EstimatedMeasurementBase estimated) { // The tropospheric delay is directly added to the TurnAroundRange. final double[] newValue = oldValue.clone(); newValue[0] = newValue[0] + primaryGDelay.getReal() + secondaryGDelay.getReal(); - estimated.setEstimatedValue(newValue); + estimated.modifyEstimatedValue(this, newValue); } diff --git a/src/main/java/org/orekit/estimation/sequential/KalmanEstimationCommon.java b/src/main/java/org/orekit/estimation/sequential/KalmanEstimationCommon.java new file mode 100644 index 0000000000..a0ee0909b0 --- /dev/null +++ b/src/main/java/org/orekit/estimation/sequential/KalmanEstimationCommon.java @@ -0,0 +1,586 @@ +/* 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.estimation.sequential; + +import org.hipparchus.filtering.kalman.ProcessEstimate; +import org.hipparchus.linear.ArrayRealVector; +import org.hipparchus.linear.MatrixUtils; +import org.hipparchus.linear.RealMatrix; +import org.hipparchus.linear.RealVector; +import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.propagation.Propagator; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.conversion.PropagatorBuilder; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.ParameterDriver; +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 + * @since 9.2 + */ +class KalmanEstimationCommon implements KalmanEstimation { + + /** Builders for propagators. */ + private final List builders; + + /** Estimated orbital parameters. */ + private final ParameterDriversList allEstimatedOrbitalParameters; + + /** Estimated propagation drivers. */ + private final ParameterDriversList allEstimatedPropagationParameters; + + /** Per-builder estimated orbita parameters drivers. + * @since 11.1 + */ + private final ParameterDriversList[] estimatedOrbitalParameters; + + /** Per-builder estimated propagation drivers. */ + private final ParameterDriversList[] estimatedPropagationParameters; + + /** Estimated measurements parameters. */ + private final ParameterDriversList estimatedMeasurementsParameters; + + /** Start columns for each estimated orbit. */ + private final int[] orbitsStartColumns; + + /** End columns for each estimated orbit. */ + private final int[] orbitsEndColumns; + + /** Map for propagation parameters columns. */ + private final Map propagationParameterColumns; + + /** Map for measurements parameters columns. */ + private final Map measurementParameterColumns; + + /** Providers for covariance matrices. */ + private final List covarianceMatricesProviders; + + /** Process noise matrix provider for measurement parameters. */ + private final CovarianceMatrixProvider measurementProcessNoiseMatrix; + + /** Indirection arrays to extract the noise components for estimated parameters. */ + private final int[][] covarianceIndirection; + + /** Scaling factors. */ + private final double[] scale; + + /** Current corrected estimate. */ + private ProcessEstimate correctedEstimate; + + /** Current number of measurement. */ + private int currentMeasurementNumber; + + /** Reference date. */ + private final AbsoluteDate referenceDate; + + /** Current date. */ + private AbsoluteDate currentDate; + + /** Predicted spacecraft states. */ + private final SpacecraftState[] predictedSpacecraftStates; + + /** Corrected spacecraft states. */ + private final SpacecraftState[] correctedSpacecraftStates; + + /** Predicted measurement. */ + private EstimatedMeasurement predictedMeasurement; + + /** Corrected measurement. */ + private EstimatedMeasurement correctedMeasurement; + + /** Kalman process model constructor. + * @param propagatorBuilders propagators builders used to evaluate the orbits. + * @param covarianceMatricesProviders providers for covariance matrices + * @param estimatedMeasurementParameters measurement parameters to estimate + * @param measurementProcessNoiseMatrix provider for measurement process noise matrix + */ + protected KalmanEstimationCommon(final List propagatorBuilders, + final List covarianceMatricesProviders, + final ParameterDriversList estimatedMeasurementParameters, + final CovarianceMatrixProvider measurementProcessNoiseMatrix) { + + this.builders = propagatorBuilders; + this.estimatedMeasurementsParameters = estimatedMeasurementParameters; + this.measurementParameterColumns = new HashMap<>(estimatedMeasurementsParameters.getDrivers().size()); + this.currentMeasurementNumber = 0; + this.referenceDate = propagatorBuilders.get(0).getInitialOrbitDate(); + this.currentDate = referenceDate; + + final Map orbitalParameterColumns = new HashMap<>(6 * builders.size()); + orbitsStartColumns = new int[builders.size()]; + orbitsEndColumns = new int[builders.size()]; + int columns = 0; + allEstimatedOrbitalParameters = new ParameterDriversList(); + estimatedOrbitalParameters = new ParameterDriversList[builders.size()]; + for (int k = 0; k < builders.size(); ++k) { + estimatedOrbitalParameters[k] = new ParameterDriversList(); + orbitsStartColumns[k] = columns; + final String suffix = propagatorBuilders.size() > 1 ? "[" + k + "]" : null; + for (final ParameterDriver driver : builders.get(k).getOrbitalParametersDrivers().getDrivers()) { + if (driver.getReferenceDate() == null) { + driver.setReferenceDate(currentDate); + } + if (suffix != null && !driver.getName().endsWith(suffix)) { + // we add suffix only conditionally because the method may already have been called + // and suffixes may have already been appended + driver.setName(driver.getName() + suffix); + } + if (driver.isSelected()) { + allEstimatedOrbitalParameters.add(driver); + estimatedOrbitalParameters[k].add(driver); + orbitalParameterColumns.put(driver.getName(), columns++); + } + } + orbitsEndColumns[k] = columns; + } + + // Gather all the propagation drivers names in a list + allEstimatedPropagationParameters = new ParameterDriversList(); + estimatedPropagationParameters = new ParameterDriversList[builders.size()]; + final List estimatedPropagationParametersNames = new ArrayList<>(); + for (int k = 0; k < builders.size(); ++k) { + estimatedPropagationParameters[k] = new ParameterDriversList(); + for (final ParameterDriver driver : builders.get(k).getPropagationParametersDrivers().getDrivers()) { + if (driver.getReferenceDate() == null) { + driver.setReferenceDate(currentDate); + } + if (driver.isSelected()) { + allEstimatedPropagationParameters.add(driver); + estimatedPropagationParameters[k].add(driver); + final String driverName = driver.getName(); + // Add the driver name if it has not been added yet + if (!estimatedPropagationParametersNames.contains(driverName)) { + estimatedPropagationParametersNames.add(driverName); + } + } + } + } + estimatedPropagationParametersNames.sort(Comparator.naturalOrder()); + + // Populate the map of propagation drivers' columns and update the total number of columns + propagationParameterColumns = new HashMap<>(estimatedPropagationParametersNames.size()); + for (final String driverName : estimatedPropagationParametersNames) { + propagationParameterColumns.put(driverName, columns); + ++columns; + } + + // Populate the map of measurement drivers' columns and update the total number of columns + for (final ParameterDriver parameter : estimatedMeasurementsParameters.getDrivers()) { + if (parameter.getReferenceDate() == null) { + parameter.setReferenceDate(currentDate); + } + measurementParameterColumns.put(parameter.getName(), columns); + ++columns; + } + + // Store providers for process noise matrices + this.covarianceMatricesProviders = covarianceMatricesProviders; + this.measurementProcessNoiseMatrix = measurementProcessNoiseMatrix; + this.covarianceIndirection = new int[builders.size()][columns]; + for (int k = 0; k < covarianceIndirection.length; ++k) { + final ParameterDriversList orbitDrivers = builders.get(k).getOrbitalParametersDrivers(); + final ParameterDriversList parametersDrivers = builders.get(k).getPropagationParametersDrivers(); + Arrays.fill(covarianceIndirection[k], -1); + int i = 0; + for (final ParameterDriver driver : orbitDrivers.getDrivers()) { + final Integer c = orbitalParameterColumns.get(driver.getName()); + if (c != null) { + covarianceIndirection[k][i++] = c; + } + } + for (final ParameterDriver driver : parametersDrivers.getDrivers()) { + final Integer c = propagationParameterColumns.get(driver.getName()); + if (c != null) { + covarianceIndirection[k][i++] = c; + } + } + for (final ParameterDriver driver : estimatedMeasurementParameters.getDrivers()) { + final Integer c = measurementParameterColumns.get(driver.getName()); + if (c != null) { + covarianceIndirection[k][i++] = c; + } + } + } + + // Compute the scale factors + this.scale = new double[columns]; + int index = 0; + for (final ParameterDriver driver : allEstimatedOrbitalParameters.getDrivers()) { + scale[index++] = driver.getScale(); + } + for (final ParameterDriver driver : allEstimatedPropagationParameters.getDrivers()) { + scale[index++] = driver.getScale(); + } + for (final ParameterDriver driver : estimatedMeasurementsParameters.getDrivers()) { + scale[index++] = driver.getScale(); + } + + // Populate predicted and corrected states + this.predictedSpacecraftStates = new SpacecraftState[builders.size()]; + for (int i = 0; i < builders.size(); ++i) { + predictedSpacecraftStates[i] = builders.get(i).buildPropagator().getInitialState(); + } + this.correctedSpacecraftStates = predictedSpacecraftStates.clone(); + + // Initialize the estimated normalized state and fill its values + final RealVector correctedState = MatrixUtils.createRealVector(columns); + + int p = 0; + for (final ParameterDriver driver : allEstimatedOrbitalParameters.getDrivers()) { + correctedState.setEntry(p++, driver.getNormalizedValue()); + } + for (final ParameterDriver driver : allEstimatedPropagationParameters.getDrivers()) { + correctedState.setEntry(p++, driver.getNormalizedValue()); + } + for (final ParameterDriver driver : estimatedMeasurementsParameters.getDrivers()) { + correctedState.setEntry(p++, driver.getNormalizedValue()); + } + + // Set up initial covariance + final RealMatrix physicalProcessNoise = MatrixUtils.createRealMatrix(columns, columns); + for (int k = 0; k < covarianceMatricesProviders.size(); ++k) { + + // Number of estimated measurement parameters + final int nbMeas = estimatedMeasurementParameters.getNbParams(); + + // Number of estimated dynamic parameters (orbital + propagation) + final int nbDyn = orbitsEndColumns[k] - orbitsStartColumns[k] + + estimatedPropagationParameters[k].getNbParams(); + + // Covariance matrix + final RealMatrix noiseK = MatrixUtils.createRealMatrix(nbDyn + nbMeas, nbDyn + nbMeas); + if (nbDyn > 0) { + final RealMatrix noiseP = covarianceMatricesProviders.get(k). + getInitialCovarianceMatrix(correctedSpacecraftStates[k]); + noiseK.setSubMatrix(noiseP.getData(), 0, 0); + } + if (measurementProcessNoiseMatrix != null) { + final RealMatrix noiseM = measurementProcessNoiseMatrix. + getInitialCovarianceMatrix(correctedSpacecraftStates[k]); + noiseK.setSubMatrix(noiseM.getData(), nbDyn, nbDyn); + } + + KalmanEstimatorUtil.checkDimension(noiseK.getRowDimension(), + builders.get(k).getOrbitalParametersDrivers(), + builders.get(k).getPropagationParametersDrivers(), + estimatedMeasurementsParameters); + + final int[] indK = covarianceIndirection[k]; + for (int i = 0; i < indK.length; ++i) { + if (indK[i] >= 0) { + for (int j = 0; j < indK.length; ++j) { + if (indK[j] >= 0) { + physicalProcessNoise.setEntry(indK[i], indK[j], noiseK.getEntry(i, j)); + } + } + } + } + + } + final RealMatrix correctedCovariance = KalmanEstimatorUtil.normalizeCovarianceMatrix(physicalProcessNoise, scale); + + correctedEstimate = new ProcessEstimate(0.0, correctedState, correctedCovariance); + + } + + + /** {@inheritDoc} */ + @Override + public RealMatrix getPhysicalStateTransitionMatrix() { + // Un-normalize the state transition matrix (φ) from Hipparchus and return it. + // φ is an mxm matrix where m = nbOrb + nbPropag + nbMeas + // For each element [i,j] of normalized φ (φn), the corresponding physical value is: + // φ[i,j] = φn[i,j] * scale[i] / scale[j] + return correctedEstimate.getStateTransitionMatrix() == null ? + null : KalmanEstimatorUtil.unnormalizeStateTransitionMatrix(correctedEstimate.getStateTransitionMatrix(), scale); + } + + /** {@inheritDoc} */ + @Override + public RealMatrix getPhysicalMeasurementJacobian() { + // Un-normalize the measurement matrix (H) from Hipparchus and return it. + // H is an nxm matrix where: + // - m = nbOrb + nbPropag + nbMeas is the number of estimated parameters + // - n is the size of the measurement being processed by the filter + // For each element [i,j] of normalized H (Hn) the corresponding physical value is: + // H[i,j] = Hn[i,j] * σ[i] / scale[j] + return correctedEstimate.getMeasurementJacobian() == null ? + null : KalmanEstimatorUtil.unnormalizeMeasurementJacobian(correctedEstimate.getMeasurementJacobian(), + scale, + correctedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation()); + } + + /** {@inheritDoc} */ + @Override + public RealMatrix getPhysicalInnovationCovarianceMatrix() { + // Un-normalize the innovation covariance matrix (S) from Hipparchus and return it. + // S is an nxn matrix where n is the size of the measurement being processed by the filter + // For each element [i,j] of normalized S (Sn) the corresponding physical value is: + // S[i,j] = Sn[i,j] * σ[i] * σ[j] + return correctedEstimate.getInnovationCovariance() == null ? + null : KalmanEstimatorUtil.unnormalizeInnovationCovarianceMatrix(correctedEstimate.getInnovationCovariance(), + predictedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation()); + } + + /** {@inheritDoc} */ + @Override + public RealMatrix getPhysicalKalmanGain() { + // Un-normalize the Kalman gain (K) from Hipparchus and return it. + // K is an mxn matrix where: + // - m = nbOrb + nbPropag + nbMeas is the number of estimated parameters + // - n is the size of the measurement being processed by the filter + // For each element [i,j] of normalized K (Kn) the corresponding physical value is: + // K[i,j] = Kn[i,j] * scale[i] / σ[j] + return correctedEstimate.getKalmanGain() == null ? + null : KalmanEstimatorUtil.unnormalizeKalmanGainMatrix(correctedEstimate.getKalmanGain(), + scale, + correctedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation()); + } + + /** {@inheritDoc} */ + @Override + public SpacecraftState[] getPredictedSpacecraftStates() { + return predictedSpacecraftStates.clone(); + } + + /** {@inheritDoc} */ + @Override + public SpacecraftState[] getCorrectedSpacecraftStates() { + return correctedSpacecraftStates.clone(); + } + + /** {@inheritDoc} */ + @Override + public int getCurrentMeasurementNumber() { + return currentMeasurementNumber; + } + + /** {@inheritDoc} */ + @Override + public AbsoluteDate getCurrentDate() { + return currentDate; + } + + /** {@inheritDoc} */ + @Override + public EstimatedMeasurement getPredictedMeasurement() { + return predictedMeasurement; + } + + /** {@inheritDoc} */ + @Override + public EstimatedMeasurement getCorrectedMeasurement() { + return correctedMeasurement; + } + + /** {@inheritDoc} */ + @Override + public RealVector getPhysicalEstimatedState() { + // Method {@link ParameterDriver#getValue()} is used to get + // the physical values of the state. + // The scales'array is used to get the size of the state vector + final RealVector physicalEstimatedState = new ArrayRealVector(scale.length); + int i = 0; + for (final DelegatingDriver driver : getEstimatedOrbitalParameters().getDrivers()) { + physicalEstimatedState.setEntry(i++, driver.getValue()); + } + for (final DelegatingDriver driver : getEstimatedPropagationParameters().getDrivers()) { + physicalEstimatedState.setEntry(i++, driver.getValue()); + } + for (final DelegatingDriver driver : getEstimatedMeasurementsParameters().getDrivers()) { + physicalEstimatedState.setEntry(i++, driver.getValue()); + } + + return physicalEstimatedState; + } + + /** {@inheritDoc} */ + @Override + public RealMatrix getPhysicalEstimatedCovarianceMatrix() { + // Un-normalize the estimated covariance matrix (P) from Hipparchus and return it. + // The covariance P is an mxm matrix where m = nbOrb + nbPropag + nbMeas + // For each element [i,j] of P the corresponding normalized value is: + // Pn[i,j] = P[i,j] / (scale[i]*scale[j]) + // Consequently: P[i,j] = Pn[i,j] * scale[i] * scale[j] + return KalmanEstimatorUtil.unnormalizeCovarianceMatrix(correctedEstimate.getCovariance(), scale); + } + + /** {@inheritDoc} */ + @Override + public ParameterDriversList getEstimatedOrbitalParameters() { + return allEstimatedOrbitalParameters; + } + + /** {@inheritDoc} */ + @Override + public ParameterDriversList getEstimatedPropagationParameters() { + return allEstimatedPropagationParameters; + } + + /** {@inheritDoc} */ + @Override + public ParameterDriversList getEstimatedMeasurementsParameters() { + return estimatedMeasurementsParameters; + } + + /** Get the current corrected estimate. + * @return current corrected estimate + */ + public ProcessEstimate getEstimate() { + return correctedEstimate; + } + + /** Getter for the propagators. + * @return the propagators + */ + public List getBuilders() { + return builders; + } + + /** Get the propagators estimated with the values set in the propagators builders. + * @return propagators based on the current values in the builder + */ + public Propagator[] getEstimatedPropagators() { + // Return propagators built with current instantiation of the propagator builders + final Propagator[] propagators = new Propagator[getBuilders().size()]; + for (int k = 0; k < getBuilders().size(); ++k) { + propagators[k] = getBuilders().get(k).buildPropagator(); + } + return propagators; + } + + protected RealMatrix getNormalizedProcessNoise(final int stateDimension) { + final RealMatrix physicalProcessNoise = MatrixUtils.createRealMatrix(stateDimension, stateDimension); + for (int k = 0; k < covarianceMatricesProviders.size(); ++k) { + + // Number of estimated measurement parameters + final int nbMeas = estimatedMeasurementsParameters.getNbParams(); + + // Number of estimated dynamic parameters (orbital + propagation) + final int nbDyn = orbitsEndColumns[k] - orbitsStartColumns[k] + + estimatedPropagationParameters[k].getNbParams(); + + // Covariance matrix + final RealMatrix noiseK = MatrixUtils.createRealMatrix(nbDyn + nbMeas, nbDyn + nbMeas); + if (nbDyn > 0) { + final RealMatrix noiseP = covarianceMatricesProviders.get(k). + getProcessNoiseMatrix(correctedSpacecraftStates[k], + predictedSpacecraftStates[k]); + noiseK.setSubMatrix(noiseP.getData(), 0, 0); + } + if (measurementProcessNoiseMatrix != null) { + final RealMatrix noiseM = measurementProcessNoiseMatrix. + getProcessNoiseMatrix(correctedSpacecraftStates[k], + predictedSpacecraftStates[k]); + noiseK.setSubMatrix(noiseM.getData(), nbDyn, nbDyn); + } + + KalmanEstimatorUtil.checkDimension(noiseK.getRowDimension(), + builders.get(k).getOrbitalParametersDrivers(), + builders.get(k).getPropagationParametersDrivers(), + estimatedMeasurementsParameters); + + final int[] indK = covarianceIndirection[k]; + for (int i = 0; i < indK.length; ++i) { + if (indK[i] >= 0) { + for (int j = 0; j < indK.length; ++j) { + if (indK[j] >= 0) { + physicalProcessNoise.setEntry(indK[i], indK[j], noiseK.getEntry(i, j)); + } + } + } + } + + } + return KalmanEstimatorUtil.normalizeCovarianceMatrix(physicalProcessNoise, scale); + } + + + protected int[] getOrbitsStartColumns() { + return orbitsStartColumns; + } + + protected Map getPropagationParameterColumns() { + return propagationParameterColumns; + } + + protected Map getMeasurementParameterColumns() { + return measurementParameterColumns; + } + + protected ParameterDriversList[] getEstimatedPropagationParametersArray() { + return estimatedPropagationParameters; + } + + protected ParameterDriversList[] getEstimatedOrbitalParametersArray() { + return estimatedOrbitalParameters; + } + + protected int[][] getCovarianceIndirection() { + return covarianceIndirection; + } + + protected double[] getScale() { + return scale; + } + + protected ProcessEstimate getCorrectedEstimate() { + return correctedEstimate; + } + + protected void setCorrectedEstimate(final ProcessEstimate correctedEstimate) { + this.correctedEstimate = correctedEstimate; + } + + protected AbsoluteDate getReferenceDate() { + return referenceDate; + } + + protected void incrementCurrentMeasurementNumber() { + currentMeasurementNumber += 1; + } + + public void setCurrentDate(final AbsoluteDate currentDate) { + this.currentDate = currentDate; + } + + protected void setCorrectedSpacecraftState(final SpacecraftState correctedSpacecraftState, final int index) { + this.correctedSpacecraftStates[index] = correctedSpacecraftState; + } + + protected void setPredictedSpacecraftState(final SpacecraftState predictedSpacecraftState, final int index) { + this.predictedSpacecraftStates[index] = predictedSpacecraftState; + } + + protected void setPredictedMeasurement(final EstimatedMeasurement predictedMeasurement) { + this.predictedMeasurement = predictedMeasurement; + } + + 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 23a65ab21f..b1702e0789 100644 --- a/src/main/java/org/orekit/estimation/sequential/KalmanModel.java +++ b/src/main/java/org/orekit/estimation/sequential/KalmanModel.java @@ -16,18 +16,10 @@ */ 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.filtering.kalman.extended.NonLinearEvolution; import org.hipparchus.filtering.kalman.extended.NonLinearProcess; import org.hipparchus.linear.Array2DRowRealMatrix; -import org.hipparchus.linear.ArrayRealVector; import org.hipparchus.linear.MatrixUtils; import org.hipparchus.linear.RealMatrix; import org.hipparchus.linear.RealVector; @@ -43,56 +35,16 @@ import org.orekit.utils.ParameterDriversList; import org.orekit.utils.ParameterDriversList.DelegatingDriver; +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 * @since 9.2 */ -public class KalmanModel implements KalmanEstimation, NonLinearProcess { - - /** Builders for propagators. */ - private final List builders; - - /** Estimated orbital parameters. */ - private final ParameterDriversList allEstimatedOrbitalParameters; - - /** Estimated propagation drivers. */ - private final ParameterDriversList allEstimatedPropagationParameters; - - /** Per-builder estimated orbita parameters drivers. - * @since 11.1 - */ - private final ParameterDriversList[] estimatedOrbitalParameters; +public class KalmanModel extends KalmanEstimationCommon implements NonLinearProcess { - /** Per-builder estimated propagation drivers. */ - private final ParameterDriversList[] estimatedPropagationParameters; - - /** Estimated measurements parameters. */ - private final ParameterDriversList estimatedMeasurementsParameters; - - /** Start columns for each estimated orbit. */ - private final int[] orbitsStartColumns; - - /** End columns for each estimated orbit. */ - private final int[] orbitsEndColumns; - - /** Map for propagation parameters columns. */ - private final Map propagationParameterColumns; - - /** Map for measurements parameters columns. */ - private final Map measurementParameterColumns; - - /** Providers for covariance matrices. */ - private final List covarianceMatricesProviders; - - /** Process noise matrix provider for measurement parameters. */ - private final CovarianceMatrixProvider measurementProcessNoiseMatrix; - - /** Indirection arrays to extract the noise components for estimated parameters. */ - private final int[][] covarianceIndirection; - - /** Scaling factors. */ - private final double[] scale; /** Harvesters for extracting Jacobians from integrated states. */ private MatricesHarvester[] harvesters; @@ -100,30 +52,6 @@ public class KalmanModel implements KalmanEstimation, NonLinearProcess predictedMeasurement; - - /** Corrected measurement. */ - private EstimatedMeasurement correctedMeasurement; - /** Kalman process model constructor. * @param propagatorBuilders propagators builders used to evaluate the orbits. * @param covarianceMatricesProviders providers for covariance matrices @@ -134,190 +62,9 @@ public KalmanModel(final List propagatorBuilders, final List covarianceMatricesProviders, final ParameterDriversList estimatedMeasurementParameters, final CovarianceMatrixProvider measurementProcessNoiseMatrix) { - - this.builders = propagatorBuilders; - this.estimatedMeasurementsParameters = estimatedMeasurementParameters; - this.measurementParameterColumns = new HashMap<>(estimatedMeasurementsParameters.getDrivers().size()); - this.currentMeasurementNumber = 0; - this.referenceDate = propagatorBuilders.get(0).getInitialOrbitDate(); - this.currentDate = referenceDate; - - final Map orbitalParameterColumns = new HashMap<>(6 * builders.size()); - orbitsStartColumns = new int[builders.size()]; - orbitsEndColumns = new int[builders.size()]; - int columns = 0; - allEstimatedOrbitalParameters = new ParameterDriversList(); - estimatedOrbitalParameters = new ParameterDriversList[builders.size()]; - for (int k = 0; k < builders.size(); ++k) { - estimatedOrbitalParameters[k] = new ParameterDriversList(); - orbitsStartColumns[k] = columns; - final String suffix = propagatorBuilders.size() > 1 ? "[" + k + "]" : null; - for (final ParameterDriver driver : builders.get(k).getOrbitalParametersDrivers().getDrivers()) { - if (driver.getReferenceDate() == null) { - driver.setReferenceDate(currentDate); - } - if (suffix != null && !driver.getName().endsWith(suffix)) { - // we add suffix only conditionally because the method may already have been called - // and suffixes may have already been appended - driver.setName(driver.getName() + suffix); - } - if (driver.isSelected()) { - allEstimatedOrbitalParameters.add(driver); - estimatedOrbitalParameters[k].add(driver); - orbitalParameterColumns.put(driver.getName(), columns++); - } - } - orbitsEndColumns[k] = columns; - } - - // Gather all the propagation drivers names in a list - allEstimatedPropagationParameters = new ParameterDriversList(); - estimatedPropagationParameters = new ParameterDriversList[builders.size()]; - final List estimatedPropagationParametersNames = new ArrayList<>(); - for (int k = 0; k < builders.size(); ++k) { - estimatedPropagationParameters[k] = new ParameterDriversList(); - for (final ParameterDriver driver : builders.get(k).getPropagationParametersDrivers().getDrivers()) { - if (driver.getReferenceDate() == null) { - driver.setReferenceDate(currentDate); - } - if (driver.isSelected()) { - allEstimatedPropagationParameters.add(driver); - estimatedPropagationParameters[k].add(driver); - final String driverName = driver.getName(); - // Add the driver name if it has not been added yet - if (!estimatedPropagationParametersNames.contains(driverName)) { - estimatedPropagationParametersNames.add(driverName); - } - } - } - } - estimatedPropagationParametersNames.sort(Comparator.naturalOrder()); - - // Populate the map of propagation drivers' columns and update the total number of columns - propagationParameterColumns = new HashMap<>(estimatedPropagationParametersNames.size()); - for (final String driverName : estimatedPropagationParametersNames) { - propagationParameterColumns.put(driverName, columns); - ++columns; - } - - // Populate the map of measurement drivers' columns and update the total number of columns - for (final ParameterDriver parameter : estimatedMeasurementsParameters.getDrivers()) { - if (parameter.getReferenceDate() == null) { - parameter.setReferenceDate(currentDate); - } - measurementParameterColumns.put(parameter.getName(), columns); - ++columns; - } - - // Store providers for process noise matrices - this.covarianceMatricesProviders = covarianceMatricesProviders; - this.measurementProcessNoiseMatrix = measurementProcessNoiseMatrix; - this.covarianceIndirection = new int[builders.size()][columns]; - for (int k = 0; k < covarianceIndirection.length; ++k) { - final ParameterDriversList orbitDrivers = builders.get(k).getOrbitalParametersDrivers(); - final ParameterDriversList parametersDrivers = builders.get(k).getPropagationParametersDrivers(); - Arrays.fill(covarianceIndirection[k], -1); - int i = 0; - for (final ParameterDriver driver : orbitDrivers.getDrivers()) { - final Integer c = orbitalParameterColumns.get(driver.getName()); - if (c != null) { - covarianceIndirection[k][i++] = c.intValue(); - } - } - for (final ParameterDriver driver : parametersDrivers.getDrivers()) { - final Integer c = propagationParameterColumns.get(driver.getName()); - if (c != null) { - covarianceIndirection[k][i++] = c.intValue(); - } - } - for (final ParameterDriver driver : estimatedMeasurementParameters.getDrivers()) { - final Integer c = measurementParameterColumns.get(driver.getName()); - if (c != null) { - covarianceIndirection[k][i++] = c.intValue(); - } - } - } - - // Compute the scale factors - this.scale = new double[columns]; - int index = 0; - for (final ParameterDriver driver : allEstimatedOrbitalParameters.getDrivers()) { - scale[index++] = driver.getScale(); - } - for (final ParameterDriver driver : allEstimatedPropagationParameters.getDrivers()) { - scale[index++] = driver.getScale(); - } - for (final ParameterDriver driver : estimatedMeasurementsParameters.getDrivers()) { - scale[index++] = driver.getScale(); - } - + super(propagatorBuilders, covarianceMatricesProviders, estimatedMeasurementParameters, measurementProcessNoiseMatrix); // Build the reference propagators and add their partial derivatives equations implementation updateReferenceTrajectories(getEstimatedPropagators()); - this.predictedSpacecraftStates = new SpacecraftState[referenceTrajectories.length]; - for (int i = 0; i < predictedSpacecraftStates.length; ++i) { - predictedSpacecraftStates[i] = referenceTrajectories[i].getInitialState(); - }; - this.correctedSpacecraftStates = predictedSpacecraftStates.clone(); - - // Initialize the estimated normalized state and fill its values - final RealVector correctedState = MatrixUtils.createRealVector(columns); - - int p = 0; - for (final ParameterDriver driver : allEstimatedOrbitalParameters.getDrivers()) { - correctedState.setEntry(p++, driver.getNormalizedValue()); - } - for (final ParameterDriver driver : allEstimatedPropagationParameters.getDrivers()) { - correctedState.setEntry(p++, driver.getNormalizedValue()); - } - for (final ParameterDriver driver : estimatedMeasurementsParameters.getDrivers()) { - correctedState.setEntry(p++, driver.getNormalizedValue()); - } - - // Set up initial covariance - final RealMatrix physicalProcessNoise = MatrixUtils.createRealMatrix(columns, columns); - for (int k = 0; k < covarianceMatricesProviders.size(); ++k) { - - // Number of estimated measurement parameters - final int nbMeas = estimatedMeasurementParameters.getNbParams(); - - // Number of estimated dynamic parameters (orbital + propagation) - final int nbDyn = orbitsEndColumns[k] - orbitsStartColumns[k] + - estimatedPropagationParameters[k].getNbParams(); - - // Covariance matrix - final RealMatrix noiseK = MatrixUtils.createRealMatrix(nbDyn + nbMeas, nbDyn + nbMeas); - if (nbDyn > 0) { - final RealMatrix noiseP = covarianceMatricesProviders.get(k). - getInitialCovarianceMatrix(correctedSpacecraftStates[k]); - noiseK.setSubMatrix(noiseP.getData(), 0, 0); - } - if (measurementProcessNoiseMatrix != null) { - final RealMatrix noiseM = measurementProcessNoiseMatrix. - getInitialCovarianceMatrix(correctedSpacecraftStates[k]); - noiseK.setSubMatrix(noiseM.getData(), nbDyn, nbDyn); - } - - KalmanEstimatorUtil.checkDimension(noiseK.getRowDimension(), - builders.get(k).getOrbitalParametersDrivers(), - builders.get(k).getPropagationParametersDrivers(), - estimatedMeasurementsParameters); - - final int[] indK = covarianceIndirection[k]; - for (int i = 0; i < indK.length; ++i) { - if (indK[i] >= 0) { - for (int j = 0; j < indK.length; ++j) { - if (indK[j] >= 0) { - physicalProcessNoise.setEntry(indK[i], indK[j], noiseK.getEntry(i, j)); - } - } - } - } - - } - final RealMatrix correctedCovariance = KalmanEstimatorUtil.normalizeCovarianceMatrix(physicalProcessNoise, scale); - - correctedEstimate = new ProcessEstimate(0.0, correctedState, correctedCovariance); - } /** Update the reference trajectories using the propagators as input. @@ -339,153 +86,6 @@ protected void updateReferenceTrajectories(final Propagator[] propagators) { } - - /** {@inheritDoc} */ - @Override - public RealMatrix getPhysicalStateTransitionMatrix() { - // Un-normalize the state transition matrix (φ) from Hipparchus and return it. - // φ is an mxm matrix where m = nbOrb + nbPropag + nbMeas - // For each element [i,j] of normalized φ (φn), the corresponding physical value is: - // φ[i,j] = φn[i,j] * scale[i] / scale[j] - return correctedEstimate.getStateTransitionMatrix() == null ? - null : KalmanEstimatorUtil.unnormalizeStateTransitionMatrix(correctedEstimate.getStateTransitionMatrix(), scale); - } - - /** {@inheritDoc} */ - @Override - public RealMatrix getPhysicalMeasurementJacobian() { - // Un-normalize the measurement matrix (H) from Hipparchus and return it. - // H is an nxm matrix where: - // - m = nbOrb + nbPropag + nbMeas is the number of estimated parameters - // - n is the size of the measurement being processed by the filter - // For each element [i,j] of normalized H (Hn) the corresponding physical value is: - // H[i,j] = Hn[i,j] * σ[i] / scale[j] - return correctedEstimate.getMeasurementJacobian() == null ? - null : KalmanEstimatorUtil.unnormalizeMeasurementJacobian(correctedEstimate.getMeasurementJacobian(), - scale, - correctedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation()); - } - - /** {@inheritDoc} */ - @Override - public RealMatrix getPhysicalInnovationCovarianceMatrix() { - // Un-normalize the innovation covariance matrix (S) from Hipparchus and return it. - // S is an nxn matrix where n is the size of the measurement being processed by the filter - // For each element [i,j] of normalized S (Sn) the corresponding physical value is: - // S[i,j] = Sn[i,j] * σ[i] * σ[j] - return correctedEstimate.getInnovationCovariance() == null ? - null : KalmanEstimatorUtil.unnormalizeInnovationCovarianceMatrix(correctedEstimate.getInnovationCovariance(), - predictedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation()); - } - - /** {@inheritDoc} */ - @Override - public RealMatrix getPhysicalKalmanGain() { - // Un-normalize the Kalman gain (K) from Hipparchus and return it. - // K is an mxn matrix where: - // - m = nbOrb + nbPropag + nbMeas is the number of estimated parameters - // - n is the size of the measurement being processed by the filter - // For each element [i,j] of normalized K (Kn) the corresponding physical value is: - // K[i,j] = Kn[i,j] * scale[i] / σ[j] - return correctedEstimate.getKalmanGain() == null ? - null : KalmanEstimatorUtil.unnormalizeKalmanGainMatrix(correctedEstimate.getKalmanGain(), - scale, - correctedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation()); - } - - /** {@inheritDoc} */ - @Override - public SpacecraftState[] getPredictedSpacecraftStates() { - return predictedSpacecraftStates.clone(); - } - - /** {@inheritDoc} */ - @Override - public SpacecraftState[] getCorrectedSpacecraftStates() { - return correctedSpacecraftStates.clone(); - } - - /** {@inheritDoc} */ - @Override - public int getCurrentMeasurementNumber() { - return currentMeasurementNumber; - } - - /** {@inheritDoc} */ - @Override - public AbsoluteDate getCurrentDate() { - return currentDate; - } - - /** {@inheritDoc} */ - @Override - public EstimatedMeasurement getPredictedMeasurement() { - return predictedMeasurement; - } - - /** {@inheritDoc} */ - @Override - public EstimatedMeasurement getCorrectedMeasurement() { - return correctedMeasurement; - } - - /** {@inheritDoc} */ - @Override - public RealVector getPhysicalEstimatedState() { - // Method {@link ParameterDriver#getValue()} is used to get - // the physical values of the state. - // The scales'array is used to get the size of the state vector - final RealVector physicalEstimatedState = new ArrayRealVector(scale.length); - int i = 0; - for (final DelegatingDriver driver : getEstimatedOrbitalParameters().getDrivers()) { - physicalEstimatedState.setEntry(i++, driver.getValue()); - } - for (final DelegatingDriver driver : getEstimatedPropagationParameters().getDrivers()) { - physicalEstimatedState.setEntry(i++, driver.getValue()); - } - for (final DelegatingDriver driver : getEstimatedMeasurementsParameters().getDrivers()) { - physicalEstimatedState.setEntry(i++, driver.getValue()); - } - - return physicalEstimatedState; - } - - /** {@inheritDoc} */ - @Override - public RealMatrix getPhysicalEstimatedCovarianceMatrix() { - // Un-normalize the estimated covariance matrix (P) from Hipparchus and return it. - // The covariance P is an mxm matrix where m = nbOrb + nbPropag + nbMeas - // For each element [i,j] of P the corresponding normalized value is: - // Pn[i,j] = P[i,j] / (scale[i]*scale[j]) - // Consequently: P[i,j] = Pn[i,j] * scale[i] * scale[j] - return KalmanEstimatorUtil.unnormalizeCovarianceMatrix(correctedEstimate.getCovariance(), scale); - } - - /** {@inheritDoc} */ - @Override - public ParameterDriversList getEstimatedOrbitalParameters() { - return allEstimatedOrbitalParameters; - } - - /** {@inheritDoc} */ - @Override - public ParameterDriversList getEstimatedPropagationParameters() { - return allEstimatedPropagationParameters; - } - - /** {@inheritDoc} */ - @Override - public ParameterDriversList getEstimatedMeasurementsParameters() { - return estimatedMeasurementsParameters; - } - - /** Get the current corrected estimate. - * @return current corrected estimate - */ - public ProcessEstimate getEstimate() { - return correctedEstimate; - } - /** Get the normalized error state transition matrix (STM) from previous point to current point. * The STM contains the partial derivatives of current state with respect to previous state. * The STM is an mxm matrix where m is the size of the state vector. @@ -516,9 +116,14 @@ private RealMatrix getErrorStateTransitionMatrix() { */ // Initialize to the proper size identity matrix - final RealMatrix stm = MatrixUtils.createRealIdentityMatrix(correctedEstimate.getState().getDimension()); + final RealMatrix stm = MatrixUtils.createRealIdentityMatrix(getCorrectedEstimate().getState().getDimension()); // loop over all orbits + final SpacecraftState[] predictedSpacecraftStates = getPredictedSpacecraftStates(); + final int[][] covarianceIndirection = getCovarianceIndirection(); + final ParameterDriversList[] estimatedOrbitalParameters = getEstimatedOrbitalParametersArray(); + final ParameterDriversList[] estimatedPropagationParameters = getEstimatedPropagationParametersArray(); + final double[] scale = getScale(); for (int k = 0; k < predictedSpacecraftStates.length; ++k) { // Indexes @@ -578,6 +183,7 @@ private RealMatrix getErrorStateTransitionMatrix() { private RealMatrix getMeasurementMatrix() { // Observed measurement characteristics + final EstimatedMeasurement predictedMeasurement = getPredictedMeasurement(); final SpacecraftState[] evaluationStates = predictedMeasurement.getStates(); final ObservedMeasurement observedMeasurement = predictedMeasurement.getObservedMeasurement(); final double[] sigma = observedMeasurement.getTheoreticalStandardDeviation(); @@ -587,9 +193,13 @@ private RealMatrix getMeasurementMatrix() { // m: State vector size final RealMatrix measurementMatrix = MatrixUtils. createRealMatrix(observedMeasurement.getDimension(), - correctedEstimate.getState().getDimension()); + getCorrectedEstimate().getState().getDimension()); // loop over all orbits involved in the measurement + final int[] orbitsStartColumns = getOrbitsStartColumns(); + final ParameterDriversList[] estimatedPropagationParameters = getEstimatedPropagationParametersArray(); + final Map propagationParameterColumns = getPropagationParameterColumns(); + final Map measurementParameterColumns = getMeasurementParameterColumns(); for (int k = 0; k < evaluationStates.length; ++k) { final int p = observedMeasurement.getSatellites().get(k).getPropagatorIndex(); @@ -601,7 +211,7 @@ private RealMatrix getMeasurementMatrix() { // Partial derivatives of the current Cartesian coordinates with respect to current orbital state final double[][] aCY = new double[6][6]; - predictedOrbit.getJacobianWrtParameters(builders.get(p).getPositionAngleType(), aCY); //dC/dY + predictedOrbit.getJacobianWrtParameters(getBuilders().get(p).getPositionAngleType(), aCY); //dC/dY final RealMatrix dCdY = new Array2DRowRealMatrix(aCY, false); // Jacobian of the measurement with respect to current Cartesian coordinates @@ -614,7 +224,7 @@ private RealMatrix getMeasurementMatrix() { for (int i = 0; i < dMdY.getRowDimension(); ++i) { int jOrb = orbitsStartColumns[p]; for (int j = 0; j < dMdY.getColumnDimension(); ++j) { - final ParameterDriver driver = builders.get(p).getOrbitalParametersDrivers().getDrivers().get(j); + final ParameterDriver driver = getBuilders().get(p).getOrbitalParametersDrivers().getDrivers().get(j); if (driver.isSelected()) { measurementMatrix.setEntry(i, jOrb++, dMdY.getEntry(i, j) / sigma[i] * driver.getScale()); @@ -678,12 +288,12 @@ public NonLinearEvolution getEvolution(final double previousTime, final RealVect final ObservedMeasurement observedMeasurement = measurement.getObservedMeasurement(); for (final ParameterDriver driver : observedMeasurement.getParametersDrivers()) { if (driver.getReferenceDate() == null) { - driver.setReferenceDate(builders.get(0).getInitialOrbitDate()); + driver.setReferenceDate(getBuilders().get(0).getInitialOrbitDate()); } } - ++currentMeasurementNumber; - currentDate = measurement.getObservedMeasurement().getDate(); + incrementCurrentMeasurementNumber(); + setCurrentDate(measurement.getObservedMeasurement().getDate()); // Note: // - n = size of the current measurement @@ -708,58 +318,15 @@ public NonLinearEvolution getEvolution(final double previousTime, final RealVect // is twisted to fit the need of the Kalman filter. // The number of "iterations" is actually the number of measurements processed by the filter // so far. We use this to be able to apply the OutlierFilter modifiers on the predicted measurement. - predictedMeasurement = observedMeasurement.estimate(currentMeasurementNumber, - currentMeasurementNumber, - KalmanEstimatorUtil.filterRelevant(observedMeasurement, predictedSpacecraftStates)); + setPredictedMeasurement(observedMeasurement.estimate(getCurrentMeasurementNumber(), + getCurrentMeasurementNumber(), + KalmanEstimatorUtil.filterRelevant(observedMeasurement, getPredictedSpacecraftStates()))); // Normalized measurement matrix (nxm) final RealMatrix measurementMatrix = getMeasurementMatrix(); // compute process noise matrix - final RealMatrix physicalProcessNoise = MatrixUtils.createRealMatrix(previousState.getDimension(), - previousState.getDimension()); - for (int k = 0; k < covarianceMatricesProviders.size(); ++k) { - - // Number of estimated measurement parameters - final int nbMeas = estimatedMeasurementsParameters.getNbParams(); - - // Number of estimated dynamic parameters (orbital + propagation) - final int nbDyn = orbitsEndColumns[k] - orbitsStartColumns[k] + - estimatedPropagationParameters[k].getNbParams(); - - // Covariance matrix - final RealMatrix noiseK = MatrixUtils.createRealMatrix(nbDyn + nbMeas, nbDyn + nbMeas); - if (nbDyn > 0) { - final RealMatrix noiseP = covarianceMatricesProviders.get(k). - getProcessNoiseMatrix(correctedSpacecraftStates[k], - predictedSpacecraftStates[k]); - noiseK.setSubMatrix(noiseP.getData(), 0, 0); - } - if (measurementProcessNoiseMatrix != null) { - final RealMatrix noiseM = measurementProcessNoiseMatrix. - getProcessNoiseMatrix(correctedSpacecraftStates[k], - predictedSpacecraftStates[k]); - noiseK.setSubMatrix(noiseM.getData(), nbDyn, nbDyn); - } - - KalmanEstimatorUtil.checkDimension(noiseK.getRowDimension(), - builders.get(k).getOrbitalParametersDrivers(), - builders.get(k).getPropagationParametersDrivers(), - estimatedMeasurementsParameters); - - final int[] indK = covarianceIndirection[k]; - for (int i = 0; i < indK.length; ++i) { - if (indK[i] >= 0) { - for (int j = 0; j < indK.length; ++j) { - if (indK[j] >= 0) { - physicalProcessNoise.setEntry(indK[i], indK[j], noiseK.getEntry(i, j)); - } - } - } - } - - } - final RealMatrix normalizedProcessNoise = KalmanEstimatorUtil.normalizeCovarianceMatrix(physicalProcessNoise, scale); + final RealMatrix normalizedProcessNoise = getNormalizedProcessNoise(previousState.getDimension()); return new NonLinearEvolution(measurement.getTime(), predictedState, stateTransitionMatrix, normalizedProcessNoise, measurementMatrix); @@ -773,6 +340,7 @@ public RealVector getInnovation(final MeasurementDecorator measurement, final No final RealMatrix innovationCovarianceMatrix) { // Apply the dynamic outlier filter, if it exists + final EstimatedMeasurement predictedMeasurement = getPredictedMeasurement(); KalmanEstimatorUtil.applyDynamicOutlierFilter(predictedMeasurement, innovationCovarianceMatrix); // Compute the innovation vector return KalmanEstimatorUtil.computeInnovationVector(predictedMeasurement, predictedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation()); @@ -786,20 +354,20 @@ public void finalizeEstimation(final ObservedMeasurement observedMeasurement, final ProcessEstimate estimate) { // Update the parameters with the estimated state // The min/max values of the parameters are handled by the ParameterDriver implementation - correctedEstimate = estimate; + setCorrectedEstimate(estimate); updateParameters(); // Get the estimated propagator (mirroring parameter update in the builder) // and the estimated spacecraft state final Propagator[] estimatedPropagators = getEstimatedPropagators(); for (int k = 0; k < estimatedPropagators.length; ++k) { - correctedSpacecraftStates[k] = estimatedPropagators[k].getInitialState(); + setCorrectedSpacecraftState(estimatedPropagators[k].getInitialState(), k); } // Compute the estimated measurement using estimated spacecraft state - correctedMeasurement = observedMeasurement.estimate(currentMeasurementNumber, - currentMeasurementNumber, - KalmanEstimatorUtil.filterRelevant(observedMeasurement, correctedSpacecraftStates)); + setCorrectedMeasurement(observedMeasurement.estimate(getCurrentMeasurementNumber(), + getCurrentMeasurementNumber(), + KalmanEstimatorUtil.filterRelevant(observedMeasurement, getCorrectedSpacecraftStates()))); // Update the trajectory // --------------------- updateReferenceTrajectories(estimatedPropagators); @@ -814,25 +382,26 @@ public void finalizeEstimation(final ObservedMeasurement observedMeasurement, private RealVector predictState(final AbsoluteDate date) { // Predicted state is initialized to previous estimated state - final RealVector predictedState = correctedEstimate.getState().copy(); + final RealVector predictedState = getCorrectedEstimate().getState().copy(); // Orbital parameters counter int jOrb = 0; - for (int k = 0; k < predictedSpacecraftStates.length; ++k) { + for (int k = 0; k < getPredictedSpacecraftStates().length; ++k) { // Propagate the reference trajectory to measurement date - predictedSpacecraftStates[k] = referenceTrajectories[k].propagate(date); + final SpacecraftState predictedSpacecraftState = referenceTrajectories[k].propagate(date); + setPredictedSpacecraftState(predictedSpacecraftState, k); // Update the builder with the predicted orbit // This updates the orbital drivers with the values of the predicted orbit - builders.get(k).resetOrbit(predictedSpacecraftStates[k].getOrbit()); + getBuilders().get(k).resetOrbit(predictedSpacecraftState.getOrbit()); // The orbital parameters in the state vector are replaced with their predicted values // The propagation & measurement parameters are not changed by the prediction (i.e. the propagation) // As the propagator builder was previously updated with the predicted orbit, // the selected orbital drivers are already up to date with the prediction - for (DelegatingDriver orbitalDriver : builders.get(k).getOrbitalParametersDrivers().getDrivers()) { + for (DelegatingDriver orbitalDriver : getBuilders().get(k).getOrbitalParametersDrivers().getDrivers()) { if (orbitalDriver.isSelected()) { predictedState.setEntry(jOrb++, orbitalDriver.getNormalizedValue()); } @@ -848,7 +417,7 @@ private RealVector predictState(final AbsoluteDate date) { * The min/max allowed values are handled by the parameter themselves. */ private void updateParameters() { - final RealVector correctedState = correctedEstimate.getState(); + final RealVector correctedState = getCorrectedEstimate().getState(); int i = 0; for (final DelegatingDriver driver : getEstimatedOrbitalParameters().getDrivers()) { // let the parameter handle min/max clipping @@ -867,13 +436,6 @@ private void updateParameters() { } } - /** Getter for the propagators. - * @return the propagators - */ - public List getBuilders() { - return builders; - } - /** Getter for the reference trajectories. * @return the referencetrajectories */ @@ -888,16 +450,4 @@ public void setReferenceTrajectories(final Propagator[] referenceTrajectories) { this.referenceTrajectories = referenceTrajectories.clone(); } - /** Get the propagators estimated with the values set in the propagators builders. - * @return propagators based on the current values in the builder - */ - public Propagator[] getEstimatedPropagators() { - // Return propagators built with current instantiation of the propagator builders - final Propagator[] propagators = new Propagator[getBuilders().size()]; - for (int k = 0; k < getBuilders().size(); ++k) { - propagators[k] = getBuilders().get(k).buildPropagator(getBuilders().get(k).getSelectedNormalizedParameters()); - } - return propagators; - } - } diff --git a/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalKalmanModel.java b/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalKalmanModel.java index 9ec962266d..f53f7e241b 100644 --- a/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalKalmanModel.java +++ b/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalKalmanModel.java @@ -16,12 +16,6 @@ */ package org.orekit.estimation.sequential; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - import org.hipparchus.exception.MathRuntimeException; import org.hipparchus.filtering.kalman.ProcessEstimate; import org.hipparchus.filtering.kalman.extended.ExtendedKalmanFilter; @@ -54,6 +48,12 @@ import org.orekit.utils.ParameterDriversList.DelegatingDriver; import org.orekit.utils.TimeSpanMap.Span; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + /** Process model to use with a {@link SemiAnalyticalKalmanEstimator}. * * @see "Folcik Z., Orbit Determination Using Modern Filters/Smoothers and Continuous Thrust Modeling, @@ -346,7 +346,7 @@ public DSSTPropagator processMeasurements(final List> obs */ public DSSTPropagator getEstimatedPropagator() { // Return propagator built with current instantiation of the propagator builder - return builder.buildPropagator(builder.getSelectedNormalizedParameters()); + return (DSSTPropagator) builder.buildPropagator(); } /** {@inheritDoc} */ @@ -645,7 +645,7 @@ public void updateReferenceTrajectory(final DSSTPropagator propagator) { // Update the jacobian harvester dsstPropagator.setInitialState(meanState, PropagationType.MEAN); - harvester = (DSSTHarvester) dsstPropagator.setupMatricesComputation(equationName, null, null); + harvester = dsstPropagator.setupMatricesComputation(equationName, null, null); } @@ -662,7 +662,7 @@ public void updateShortPeriods(final SpacecraftState state) { /** {@inheritDoc} */ @Override public void initializeShortPeriodicTerms(final SpacecraftState meanState) { - final List shortPeriodTerms = new ArrayList(); + final List shortPeriodTerms = new ArrayList<>(); // initialize ForceModels in OSCULATING mode even if propagation is MEAN final PropagationType type = PropagationType.OSCULATING; for (final DSSTForceModel force : builder.getAllForceModels()) { diff --git a/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalUnscentedKalmanModel.java b/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalUnscentedKalmanModel.java index bd6605b043..a8e3892f97 100644 --- a/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalUnscentedKalmanModel.java +++ b/src/main/java/org/orekit/estimation/sequential/SemiAnalyticalUnscentedKalmanModel.java @@ -16,10 +16,6 @@ */ package org.orekit.estimation.sequential; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; - import org.hipparchus.filtering.kalman.ProcessEstimate; import org.hipparchus.filtering.kalman.unscented.UnscentedEvolution; import org.hipparchus.filtering.kalman.unscented.UnscentedKalmanFilter; @@ -30,6 +26,7 @@ import org.hipparchus.linear.RealVector; import org.hipparchus.util.FastMath; import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.ObservedMeasurement; import org.orekit.orbits.Orbit; import org.orekit.orbits.OrbitType; @@ -47,6 +44,10 @@ import org.orekit.utils.ParameterDriversList; import org.orekit.utils.ParameterDriversList.DelegatingDriver; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + /** Class defining the process model dynamics to use with a {@link SemiAnalyticalUnscentedKalmanEstimator}. * @author Gaëtan Pierre * @author Bryan Cazabonne @@ -287,7 +288,7 @@ public DSSTPropagator processMeasurements(final List> obs */ public DSSTPropagator getEstimatedPropagator() { // Return propagator built with current instantiation of the propagator builder - return builder.buildPropagator(builder.getSelectedNormalizedParameters()); + return (DSSTPropagator) builder.buildPropagator(); } /** {@inheritDoc} */ @@ -365,11 +366,8 @@ public RealVector[] getPredictedMeasurements(final RealVector[] predictedSigmaPo currentDate, builder.getMu(), builder.getFrame()); // Then, estimate the measurement - final EstimatedMeasurement estimated = observedMeasurement.estimate(currentMeasurementNumber, - currentMeasurementNumber, - new SpacecraftState[] { - new SpacecraftState(osculatingOrbit) - }); + final EstimatedMeasurement estimated = estimateMeasurement(observedMeasurement, currentMeasurementNumber, + new SpacecraftState[] { new SpacecraftState(osculatingOrbit) }); predictedMeasurements[k] = new ArrayRealVector(estimated.getEstimatedValue()); } @@ -392,7 +390,8 @@ public RealVector getInnovation(final MeasurementDecorator measurement, final Re final Orbit osculatingOrbit = orbitType.mapArrayToOrbit(osculating.toArray(), null, angleType, currentDate, builder.getMu(), builder.getFrame()); predictedSpacecraftState = new SpacecraftState(osculatingOrbit); - predictedMeasurement = measurement.getObservedMeasurement().estimate(currentMeasurementNumber, currentMeasurementNumber, getPredictedSpacecraftStates()); + predictedMeasurement = estimateMeasurement(measurement.getObservedMeasurement(), currentMeasurementNumber, + getPredictedSpacecraftStates()); predictedMeasurement.setEstimatedValue(predictedMeas.toArray()); // Apply the dynamic outlier filter, if it exists @@ -424,15 +423,37 @@ public void finalizeEstimation(final ObservedMeasurement observedMeasurement, // Compute the corrected measurements correctedSpacecraftState = new SpacecraftState(osculatingOrbit); - correctedMeasurement = observedMeasurement.estimate(currentMeasurementNumber, - currentMeasurementNumber, - getCorrectedSpacecraftStates()); + correctedMeasurement = estimateMeasurement(observedMeasurement, currentMeasurementNumber, + getCorrectedSpacecraftStates()); + // Call the observer if the user add one if (observer != null) { observer.evaluationPerformed(this); } } + /** + * Estimate measurement (without derivatives). + * @param measurement type + * @param observedMeasurement observed measurement + * @param measurementNumber measurement number + * @param spacecraftStates states + * @return estimated measurements + * @since 12.1 + */ + private static > EstimatedMeasurement estimateMeasurement(final ObservedMeasurement observedMeasurement, + final int measurementNumber, + final SpacecraftState[] spacecraftStates) { + 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; + } + /** Get the state transition matrix used to predict the filter correction. *

    * The state transition matrix is not computed by the DSST propagator. @@ -601,7 +622,7 @@ public void updateShortPeriods(final SpacecraftState state) { /** {@inheritDoc} */ @Override public void initializeShortPeriodicTerms(final SpacecraftState meanState) { - final List shortPeriodTerms = new ArrayList(); + final List shortPeriodTerms = new ArrayList<>(); for (final DSSTForceModel force : builder.getAllForceModels()) { shortPeriodTerms.addAll(force.initializeShortPeriodTerms(new AuxiliaryElements(meanState.getOrbit(), 1), PropagationType.OSCULATING, force.getParameters())); } diff --git a/src/main/java/org/orekit/estimation/sequential/UnscentedKalmanEstimator.java b/src/main/java/org/orekit/estimation/sequential/UnscentedKalmanEstimator.java index b00cd66451..2d50c4ecb1 100644 --- a/src/main/java/org/orekit/estimation/sequential/UnscentedKalmanEstimator.java +++ b/src/main/java/org/orekit/estimation/sequential/UnscentedKalmanEstimator.java @@ -123,7 +123,7 @@ public void setObserver(final KalmanObserver observer) { * @return estimated propagator */ public Propagator[] estimationStep(final ObservedMeasurement observedMeasurement) { - final ProcessEstimate estimate = filter.estimationStep(KalmanEstimatorUtil.decorateUnscented(observedMeasurement, referenceDate)); + final ProcessEstimate estimate = filter.estimationStep(KalmanEstimatorUtil.decorate(observedMeasurement, referenceDate)); processModel.finalizeEstimation(observedMeasurement, estimate); if (observer != null) { observer.evaluationPerformed(processModel); diff --git a/src/main/java/org/orekit/estimation/sequential/UnscentedKalmanModel.java b/src/main/java/org/orekit/estimation/sequential/UnscentedKalmanModel.java index 3da92965ed..c446593b55 100644 --- a/src/main/java/org/orekit/estimation/sequential/UnscentedKalmanModel.java +++ b/src/main/java/org/orekit/estimation/sequential/UnscentedKalmanModel.java @@ -16,13 +16,6 @@ */ 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.filtering.kalman.unscented.UnscentedEvolution; import org.hipparchus.filtering.kalman.unscented.UnscentedProcess; @@ -31,12 +24,11 @@ import org.hipparchus.linear.RealMatrix; import org.hipparchus.linear.RealVector; import org.orekit.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.ObservedMeasurement; +import org.orekit.orbits.CartesianOrbit; import org.orekit.orbits.Orbit; -import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.Propagator; -import org.orekit.propagation.PropagatorsParallelizer; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.conversion.PropagatorBuilder; import org.orekit.time.AbsoluteDate; @@ -44,263 +36,54 @@ import org.orekit.utils.ParameterDriversList; import org.orekit.utils.ParameterDriversList.DelegatingDriver; +import java.util.List; + /** Class defining the process model dynamics to use with a {@link UnscentedKalmanEstimator}. * @author Gaëtan Pierre * @author Bryan Cazabonne * @since 11.3 */ -public class UnscentedKalmanModel implements KalmanEstimation, UnscentedProcess { - - /** Builders for propagators. */ - private final List builders; - - /** Estimated orbital parameters. */ - private final ParameterDriversList allEstimatedOrbitalParameters; - - /** Estimated propagation drivers. */ - private final ParameterDriversList allEstimatedPropagationParameters; - - /** Per-builder estimated orbital parameters. */ - private final ParameterDriversList[] estimatedOrbitalParameters; - - /** Per-builder estimated propagation parameters. */ - private final ParameterDriversList[] estimatedPropagationParameters; - - /** Estimated measurements parameters. */ - private final ParameterDriversList estimatedMeasurementsParameters; - - /** Start columns for each estimated orbit. */ - private final int[] orbitsStartColumns; - - /** End columns for each estimated orbit. */ - private final int[] orbitsEndColumns; - - /** Map for propagation parameters columns. */ - private final Map propagationParameterColumns; - - /** Map for measurements parameters columns. */ - private final Map measurementParameterColumns; - - /** Provider for covariance matrice. */ - private final List covarianceMatrixProviders; - - /** Process noise matrix provider for measurement parameters. */ - private final CovarianceMatrixProvider measurementProcessNoiseMatrix; - - /** Indirection arrays to extract the noise components for estimated parameters. */ - private final int[][] covarianceIndirection; - - /** Position angle types used during orbit determination. */ - private final PositionAngleType[] angleTypes; - - /** Orbit types used during orbit determination. */ - private final OrbitType[] orbitTypes; - - /** Current number of measurement. */ - private int currentMeasurementNumber; - - /** Current corrected estimate. */ - private ProcessEstimate correctedEstimate; +public class UnscentedKalmanModel extends KalmanEstimationCommon implements UnscentedProcess { - /** Current date. */ - private AbsoluteDate currentDate; - - /** Previous date. */ - private AbsoluteDate previousDate; - - /** Predicted spacecraft states. */ - private SpacecraftState[] predictedSpacecraftStates; - - /** Corrected spacecraft states. */ - private SpacecraftState[] correctedSpacecraftStates; - - /** Predicted measurement. */ - private EstimatedMeasurement predictedMeasurement; - - /** Corrected measurement. */ - private EstimatedMeasurement correctedMeasurement; + /** Reference values. */ + private final double[] referenceValues; /** Unscented Kalman process model constructor (package private). * @param propagatorBuilders propagators builders used to evaluate the orbits. - * @param covarianceMatrixProviders provider for covariance matrix + * @param covarianceMatricesProviders provider for covariance matrix * @param estimatedMeasurementParameters measurement parameters to estimate * @param measurementProcessNoiseMatrix provider for measurement process noise matrix */ protected UnscentedKalmanModel(final List propagatorBuilders, - final List covarianceMatrixProviders, + final List covarianceMatricesProviders, final ParameterDriversList estimatedMeasurementParameters, final CovarianceMatrixProvider measurementProcessNoiseMatrix) { - this.builders = propagatorBuilders; - this.estimatedMeasurementsParameters = estimatedMeasurementParameters; - this.measurementParameterColumns = new HashMap<>(estimatedMeasurementsParameters.getDrivers().size()); - this.currentMeasurementNumber = 0; - this.currentDate = propagatorBuilders.get(0).getInitialOrbitDate(); - this.previousDate = currentDate; - this.covarianceMatrixProviders = covarianceMatrixProviders; - this.measurementProcessNoiseMatrix = measurementProcessNoiseMatrix; - - final Map orbitalParameterColumns = new HashMap<>(6 * builders.size()); - orbitsStartColumns = new int[builders.size()]; - orbitsEndColumns = new int[builders.size()]; - int columns = 0; - allEstimatedOrbitalParameters = new ParameterDriversList(); - estimatedOrbitalParameters = new ParameterDriversList[builders.size()]; - // Set estimated orbital parameters - for (int k = 0; k < builders.size(); ++k) { - estimatedOrbitalParameters[k] = new ParameterDriversList(); - orbitsStartColumns[k] = columns; - final String suffix = propagatorBuilders.size() > 1 ? "[" + k + "]" : null; - for (final ParameterDriver driver : builders.get(k).getOrbitalParametersDrivers().getDrivers()) { - if (driver.getReferenceDate() == null) { - driver.setReferenceDate(currentDate); - } - if (suffix != null && !driver.getName().endsWith(suffix)) { - // we add suffix only conditionally because the method may already have been called - // and suffixes may have already been appended - driver.setName(driver.getName() + suffix); - } - if (driver.isSelected()) { - allEstimatedOrbitalParameters.add(driver); - estimatedOrbitalParameters[k].add(driver); - orbitalParameterColumns.put(driver.getName(), columns++); - } - } - orbitsEndColumns[k] = columns; - } + super(propagatorBuilders, covarianceMatricesProviders, estimatedMeasurementParameters, measurementProcessNoiseMatrix); - // Set estimated propagation parameters - allEstimatedPropagationParameters = new ParameterDriversList(); - estimatedPropagationParameters = new ParameterDriversList[builders.size()]; - final List estimatedPropagationParametersNames = new ArrayList<>(); - for (int k = 0; k < builders.size(); ++k) { - estimatedPropagationParameters[k] = new ParameterDriversList(); - for (final ParameterDriver driver : builders.get(k).getPropagationParametersDrivers().getDrivers()) { - if (driver.getReferenceDate() == null) { - driver.setReferenceDate(currentDate); - } - if (driver.isSelected()) { - allEstimatedPropagationParameters.add(driver); - estimatedPropagationParameters[k].add(driver); - final String driverName = driver.getName(); - // Add the driver name if it has not been added yet - if (!estimatedPropagationParametersNames.contains(driverName)) { - estimatedPropagationParametersNames.add(driverName); - } - } - } - } - estimatedPropagationParametersNames.sort(Comparator.naturalOrder()); - - // Populate the map of propagation drivers' columns and update the total number of columns - propagationParameterColumns = new HashMap<>(estimatedPropagationParametersNames.size()); - for (final String driverName : estimatedPropagationParametersNames) { - propagationParameterColumns.put(driverName, columns++); - } - - // Populate the map of measurement drivers' columns and update the total number of columns - for (final ParameterDriver parameter : estimatedMeasurementsParameters.getDrivers()) { - // Verify if the driver reference date has been set - if (parameter.getReferenceDate() == null) { - parameter.setReferenceDate(currentDate); - } - measurementParameterColumns.put(parameter.getName(), columns); - columns++; + // Record the initial reference values + int stateDimension = 0; + for (final ParameterDriver ignored : getEstimatedOrbitalParameters().getDrivers()) { + stateDimension += 1; } - - // set angle and orbit types - angleTypes = new PositionAngleType[builders.size()]; - orbitTypes = new OrbitType[builders.size()]; - for (int k = 0; k < builders.size(); k++) { - angleTypes[k] = builders.get(k).getPositionAngleType(); - orbitTypes[k] = builders.get(k).getOrbitType(); + for (final ParameterDriver ignored : getEstimatedPropagationParameters().getDrivers()) { + stateDimension += 1; } - - // set covariance indirection - this.covarianceIndirection = new int[covarianceMatrixProviders.size()][columns]; - - for (int k = 0; k < covarianceIndirection.length; ++k) { - final ParameterDriversList orbitDrivers = builders.get(k).getOrbitalParametersDrivers(); - final ParameterDriversList parametersDrivers = builders.get(k).getPropagationParametersDrivers(); - Arrays.fill(covarianceIndirection[k], -1); - int i = 0; - for (final ParameterDriver driver : orbitDrivers.getDrivers()) { - final Integer c = orbitalParameterColumns.get(driver.getName()); - covarianceIndirection[k][i++] = (c == null) ? -1 : c.intValue(); - } - for (final ParameterDriver driver : parametersDrivers.getDrivers()) { - final Integer c = propagationParameterColumns.get(driver.getName()); - if (c != null) { - covarianceIndirection[k][i++] = c.intValue(); - } - } - for (final ParameterDriver driver : estimatedMeasurementParameters.getDrivers()) { - final Integer c = measurementParameterColumns.get(driver.getName()); - if (c != null) { - covarianceIndirection[k][i++] = c.intValue(); - } - } + for (final ParameterDriver ignored : getEstimatedMeasurementsParameters().getDrivers()) { + stateDimension += 1; } - // Initialize the estimated state and fill its values - final RealVector correctedState = MatrixUtils.createRealVector(columns); - - int p = 0; - for (final ParameterDriver driver : allEstimatedOrbitalParameters.getDrivers()) { - correctedState.setEntry(p++, driver.getValue()); + this.referenceValues = new double[stateDimension]; + int index = 0; + for (final ParameterDriver driver : getEstimatedOrbitalParameters().getDrivers()) { + referenceValues[index++] = driver.getReferenceValue(); } - for (final ParameterDriver driver : allEstimatedPropagationParameters.getDrivers()) { - correctedState.setEntry(p++, driver.getValue()); + for (final ParameterDriver driver : getEstimatedPropagationParameters().getDrivers()) { + referenceValues[index++] = driver.getReferenceValue(); } - for (final ParameterDriver driver : estimatedMeasurementsParameters.getDrivers()) { - correctedState.setEntry(p++, driver.getValue()); + for (final ParameterDriver driver : getEstimatedMeasurementsParameters().getDrivers()) { + referenceValues[index++] = driver.getReferenceValue(); } - - this.predictedSpacecraftStates = new SpacecraftState[propagatorBuilders.size()]; - for (int i = 0; i < propagatorBuilders.size(); i++) { - predictedSpacecraftStates[i] = propagatorBuilders.get(i).buildPropagator(propagatorBuilders.get(i).getSelectedNormalizedParameters()).getInitialState(); - } - this.correctedSpacecraftStates = predictedSpacecraftStates.clone(); - - // Number of estimated measurement parameters - final int nbMeas = estimatedMeasurementParameters.getNbParams(); - - final RealMatrix correctedCovariance = MatrixUtils.createRealMatrix(columns, columns); - for (int k = 0; k < covarianceMatrixProviders.size(); k++) { - // Number of estimated dynamic parameters (orbital + propagation) - final int nbDyn = orbitsEndColumns[k] - orbitsStartColumns[k] + - estimatedPropagationParameters[k].getNbParams(); - // Covariance matrix - final RealMatrix noiseK = MatrixUtils.createRealMatrix(nbDyn + nbMeas, nbDyn + nbMeas); - final RealMatrix noiseP = covarianceMatrixProviders.get(k). - getInitialCovarianceMatrix(correctedSpacecraftStates[k]); - noiseK.setSubMatrix(noiseP.getData(), 0, 0); - if (measurementProcessNoiseMatrix != null) { - final RealMatrix noiseM = measurementProcessNoiseMatrix. - getInitialCovarianceMatrix(correctedSpacecraftStates[k]); - noiseK.setSubMatrix(noiseM.getData(), nbDyn, nbDyn); - } - - KalmanEstimatorUtil.checkDimension(noiseK.getRowDimension(), - builders.get(k).getOrbitalParametersDrivers(), - builders.get(k).getPropagationParametersDrivers(), - estimatedMeasurementsParameters); - - final int[] indK = covarianceIndirection[k]; - for (int i = 0; i < indK.length; ++i) { - if (indK[i] >= 0) { - for (int j = 0; j < indK.length; ++j) { - if (indK[j] >= 0) { - correctedCovariance.setEntry(indK[i], indK[j], noiseK.getEntry(i, j)); - } - } - } - } - } - - // Initialize corrected estimate - this.correctedEstimate = new ProcessEstimate(0.0, correctedState, correctedCovariance); - } /** {@inheritDoc} */ @@ -312,94 +95,54 @@ public UnscentedEvolution getEvolution(final double previousTime, final RealVect final ObservedMeasurement observedMeasurement = measurement.getObservedMeasurement(); for (final ParameterDriver driver : observedMeasurement.getParametersDrivers()) { if (driver.getReferenceDate() == null) { - driver.setReferenceDate(builders.get(0).getInitialOrbitDate()); + driver.setReferenceDate(getBuilders().get(0).getInitialOrbitDate()); } } // Increment measurement number - ++currentMeasurementNumber; + incrementCurrentMeasurementNumber(); // Update the current date - currentDate = measurement.getObservedMeasurement().getDate(); - - // Initialize arrays of predicted states and measurements - final RealVector[] predictedStates = new RealVector[sigmaPoints.length]; + setCurrentDate(measurement.getObservedMeasurement().getDate()); - // Loop on builders - for (int k = 0; k < builders.size(); k++ ) { + // Initialize array of predicted sigma points + final RealVector[] predictedSigmaPoints = new RealVector[sigmaPoints.length]; - // Sigma points for the current builder - final RealVector[] currentSigmaPoints = new ArrayRealVector[sigmaPoints.length]; - for (int i = 0; i < sigmaPoints.length; i++) { - currentSigmaPoints[i] = sigmaPoints[i].getSubVector(orbitsStartColumns[k], orbitsEndColumns[k] - orbitsStartColumns[k]); - } + // Propagate each sigma point + for (int i = 0; i < sigmaPoints.length; i++) { - // Predict states - final List states = predictStates(currentSigmaPoints, k); + // Set parameters for this sigma point + final RealVector sigmaPoint = sigmaPoints[i].copy(); + updateParameters(sigmaPoint); - // Loop on states - for (int i = 0; i < states.size(); ++i) { - if (k == 0) { - predictedStates[i] = new ArrayRealVector(sigmaPoints[i].getDimension()); - } - // Current predicted state - final SpacecraftState predicted = states.get(i); - // First, convert the predicted state to an array - final double[] predictedArray = new double[currentSigmaPoints[i].getDimension()]; - orbitTypes[k].mapOrbitToArray(predicted.getOrbit(), angleTypes[k], predictedArray, null); - predictedStates[i].setSubVector(orbitsStartColumns[k], new ArrayRealVector(predictedArray)); - } + // Get propagators + final Propagator[] propagators = getEstimatedPropagators(); + // Do prediction + predictedSigmaPoints[i] = predictState(observedMeasurement.getDate(), sigmaPoint, propagators); } - // compute process noise matrix - final RealMatrix processNoise = MatrixUtils.createRealMatrix(sigmaPoints[0].getDimension(), sigmaPoints[0].getDimension()); - - for (int k = 0; k < covarianceMatrixProviders.size(); ++k) { - - // Number of estimated measurement parameters - final int nbMeas = estimatedMeasurementsParameters.getNbParams(); - - // Number of estimated dynamic parameters (orbital + propagation) - final int nbDyn = orbitsEndColumns[k] - orbitsStartColumns[k] + - estimatedPropagationParameters[k].getNbParams(); - - // Covariance matrix - final RealMatrix noiseK = MatrixUtils.createRealMatrix(nbDyn + nbMeas, nbDyn + nbMeas); - final RealMatrix noiseP = covarianceMatrixProviders.get(k). - getProcessNoiseMatrix(correctedSpacecraftStates[k], - predictedSpacecraftStates[k]); - noiseK.setSubMatrix(noiseP.getData(), 0, 0); - if (measurementProcessNoiseMatrix != null) { - final RealMatrix noiseM = measurementProcessNoiseMatrix. - getProcessNoiseMatrix(correctedSpacecraftStates[k], - predictedSpacecraftStates[k]); - noiseK.setSubMatrix(noiseM.getData(), nbDyn, nbDyn); - } + // Reset the driver reference values based on the first sigma point + int d = 0; + for (final DelegatingDriver driver : getEstimatedOrbitalParameters().getDrivers()) { + driver.setReferenceValue(referenceValues[d]); + driver.setNormalizedValue(predictedSigmaPoints[0].getEntry(d)); + referenceValues[d] = driver.getValue(); - KalmanEstimatorUtil.checkDimension(noiseK.getRowDimension(), - builders.get(k).getOrbitalParametersDrivers(), - builders.get(k).getPropagationParametersDrivers(), - estimatedMeasurementsParameters); - - final int[] indK = covarianceIndirection[k]; - for (int i = 0; i < indK.length; ++i) { - if (indK[i] >= 0) { - for (int j = 0; j < indK.length; ++j) { - if (indK[j] >= 0) { - processNoise.setEntry(indK[i], indK[j], noiseK.getEntry(i, j)); - } - } - } + // Make remaining sigma points relative to the first + for (int i = 1; i < predictedSigmaPoints.length; ++i) { + predictedSigmaPoints[i].setEntry(d, predictedSigmaPoints[i].getEntry(d) - predictedSigmaPoints[0].getEntry(d)); } + predictedSigmaPoints[0].setEntry(d, 0.0); + d += 1; } - // Update epoch - previousDate = currentDate; + // compute process noise matrix + final RealMatrix normalizedProcessNoise = getNormalizedProcessNoise(sigmaPoints[0].getDimension()); // Return - return new UnscentedEvolution(measurement.getTime(), predictedStates, processNoise); + return new UnscentedEvolution(measurement.getTime(), predictedSigmaPoints, normalizedProcessNoise); } /** {@inheritDoc} */ @@ -409,31 +152,48 @@ public RealVector[] getPredictedMeasurements(final RealVector[] predictedSigmaPo // Observed measurement final ObservedMeasurement observedMeasurement = measurement.getObservedMeasurement(); + // Standard deviation as a vector + final RealVector theoreticalStandardDeviation = + MatrixUtils.createRealVector(observedMeasurement.getTheoreticalStandardDeviation()); + // Initialize arrays of predicted states and measurements final RealVector[] predictedMeasurements = new RealVector[predictedSigmaPoints.length]; - // Initialize the relevant states used for measurement estimation - final SpacecraftState[][] statesForMeasurementEstimation = new SpacecraftState[predictedSigmaPoints.length][builders.size()]; - - // Convert sigma points to spacecraft states - for (int k = 0; k < builders.size(); k++ ) { - - // Loop on sigma points - for (int i = 0; i < predictedSigmaPoints.length; i++) { - final SpacecraftState state = new SpacecraftState(orbitTypes[k].mapArrayToOrbit(predictedSigmaPoints[i].toArray(), null, - angleTypes[k], currentDate, builders.get(k).getMu(), - builders.get(k).getFrame())); - statesForMeasurementEstimation[i][k] = state; + // Loop on sigma points to predict measurements + for (int i = 0; i < predictedSigmaPoints.length; i++) { + // Set parameters for this sigma point + final RealVector predictedSigmaPoint = predictedSigmaPoints[i].copy(); + updateParameters(predictedSigmaPoint); + + // Get propagators + Propagator[] propagators = getEstimatedPropagators(); + + // "updateParameters" sets the correct orbital and parameters info, but doesn't reset the time. + for (int k = 0; k < propagators.length; ++k) { + final SpacecraftState predictedState = propagators[k].getInitialState(); + final Orbit predictedOrbit = getBuilders().get(k).getOrbitType().convertType( + new CartesianOrbit(predictedState.getPVCoordinates(), + predictedState.getFrame(), + observedMeasurement.getDate(), + predictedState.getMu() + ) + ); + getBuilders().get(k).resetOrbit(predictedOrbit); } + propagators = getEstimatedPropagators(); - } + // Predicted states + final SpacecraftState[] predictedStates = new SpacecraftState[propagators.length]; + for (int k = 0; k < propagators.length; ++k) { + predictedStates[k] = propagators[k].getInitialState(); + } - // Loop on sigma points to predict measurements - for (int i = 0; i < predictedSigmaPoints.length; i++) { - final EstimatedMeasurement estimated = observedMeasurement.estimate(currentMeasurementNumber, currentMeasurementNumber, + // Calculated estimated measurement from predicted sigma point + final EstimatedMeasurement estimated = estimateMeasurement(observedMeasurement, getCurrentMeasurementNumber(), KalmanEstimatorUtil.filterRelevant(observedMeasurement, - statesForMeasurementEstimation[i])); - predictedMeasurements[i] = new ArrayRealVector(estimated.getEstimatedValue()); + predictedStates)); + predictedMeasurements[i] = new ArrayRealVector(estimated.getEstimatedValue()) + .ebeDivide(theoreticalStandardDeviation); } // Return the predicted measurements @@ -445,235 +205,165 @@ public RealVector[] getPredictedMeasurements(final RealVector[] predictedSigmaPo @Override public RealVector getInnovation(final MeasurementDecorator measurement, final RealVector predictedMeas, final RealVector predictedState, final RealMatrix innovationCovarianceMatrix) { + // Set parameters from predicted state + final RealVector predictedStateCopy = predictedState.copy(); + updateParameters(predictedStateCopy); + + // Standard deviation as a vector + final RealVector theoreticalStandardDeviation = + MatrixUtils.createRealVector(measurement.getObservedMeasurement().getTheoreticalStandardDeviation()); + + // Get propagators + Propagator[] propagators = getEstimatedPropagators(); + + // "updateParameters" sets the correct orbital info, but doesn't reset the time. + for (int k = 0; k < propagators.length; ++k) { + final SpacecraftState predicted = propagators[k].getInitialState(); + final Orbit predictedOrbit = getBuilders().get(k).getOrbitType().convertType( + new CartesianOrbit(predicted.getPVCoordinates(), + predicted.getFrame(), + measurement.getObservedMeasurement().getDate(), + predicted.getMu() + ) + ); + getBuilders().get(k).resetOrbit(predictedOrbit); + } + propagators = getEstimatedPropagators(); - // Loop on builders - for (int k = 0; k < builders.size(); k++) { + // Predicted states + for (int k = 0; k < propagators.length; ++k) { + setPredictedSpacecraftState(propagators[k].getInitialState(), k); + } - // Update predicted states - final double[] predictedStateArray = predictedState.getSubVector(orbitsStartColumns[k], orbitsEndColumns[k] - orbitsStartColumns[k]).toArray(); - final Orbit predictedOrbit = orbitTypes[k].mapArrayToOrbit(predictedStateArray, null, angleTypes[k], - currentDate, builders.get(k).getMu(), builders.get(k).getFrame()); - predictedSpacecraftStates[k] = new SpacecraftState(predictedOrbit); + // set estimated value to the predicted value from the filter + final EstimatedMeasurement predictedMeasurement = + estimateMeasurement(measurement.getObservedMeasurement(), getCurrentMeasurementNumber(), + KalmanEstimatorUtil.filterRelevant(measurement.getObservedMeasurement(), + getPredictedSpacecraftStates())); + setPredictedMeasurement(predictedMeasurement); + predictedMeasurement.setEstimatedValue(predictedMeas.ebeMultiply(theoreticalStandardDeviation).toArray()); - // Update the builder with the predicted orbit - builders.get(k).resetOrbit(predictedOrbit); + // Check for outliers + KalmanEstimatorUtil.applyDynamicOutlierFilter(predictedMeasurement, innovationCovarianceMatrix); - } + // Compute the innovation vector + return KalmanEstimatorUtil.computeInnovationVector(predictedMeasurement, + predictedMeasurement.getObservedMeasurement().getTheoreticalStandardDeviation()); + } - // Predicted measurement - predictedMeasurement = measurement.getObservedMeasurement().estimate(currentMeasurementNumber, currentMeasurementNumber, - KalmanEstimatorUtil.filterRelevant(measurement.getObservedMeasurement(), - predictedSpacecraftStates)); - predictedMeasurement.setEstimatedValue(predictedMeas.toArray()); - // set estimated value to the predicted value by the filter - KalmanEstimatorUtil.applyDynamicOutlierFilter(predictedMeasurement, innovationCovarianceMatrix); + private RealVector predictState(final AbsoluteDate date, + final RealVector previousState, + final Propagator[] propagators) { - // Compute the innovation vector (not normalized for unscented Kalman filter) - return KalmanEstimatorUtil.computeInnovationVector(predictedMeasurement); + // Initialise predicted state + final RealVector predictedState = previousState.copy(); - } + // Orbital parameters counter + int jOrb = 0; - /** - * Predict the predicted states for the given sigma points. - * @param sigmaPoints current sigma points - * @param index the index corresponding to the satellite one is dealing with - * @return predicted state for the given sigma point - */ - private List predictStates(final RealVector[] sigmaPoints, final int index) { - - // Loop on sigma points to create the propagator parallelizer - final List propagators = new ArrayList<>(sigmaPoints.length); - for (int k = 0; k < sigmaPoints.length; ++k) { - // Current sigma point - final double[] currentPoint = sigmaPoints[k].copy().toArray(); - // Create the corresponding orbit propagator - final Propagator currentPropagator = createPropagator(currentPoint, index); - // Add it to the list of propagators - propagators.add(currentPropagator); - } + // Loop over propagators + for (int k = 0; k < propagators.length; ++k) { - // Create the propagator parallelizer and predict states - // (the shift is done to start a little bit before the previous measurement epoch) - final PropagatorsParallelizer parallelizer = new PropagatorsParallelizer(propagators, interpolators -> { }); - final List states = parallelizer.propagate(previousDate.shiftedBy(-1.0e-3), currentDate); + // Record original state + final SpacecraftState originalState = propagators[k].getInitialState(); - // Return - return states; + // Propagate + final SpacecraftState predicted = propagators[k].propagate(date); - } + // Update the builder with the predicted orbit + // This updates the orbital drivers with the values of the predicted orbit + getBuilders().get(k).resetOrbit(predicted.getOrbit()); + + // The orbital parameters in the state vector are replaced with their predicted values + // The propagation & measurement parameters are not changed by the prediction (i.e. the propagation) + // As the propagator builder was previously updated with the predicted orbit, + // the selected orbital drivers are already up to date with the prediction + for (DelegatingDriver orbitalDriver : getBuilders().get(k).getOrbitalParametersDrivers().getDrivers()) { + if (orbitalDriver.isSelected()) { + orbitalDriver.setReferenceValue(referenceValues[jOrb]); + predictedState.setEntry(jOrb, orbitalDriver.getNormalizedValue()); + + jOrb += 1; + } + } - /** - * Create a propagator for the given sigma point. - * @param point input sigma point - * @param index the index corresponding to the satellite one is dealing with - * @return the corresponding orbit propagator - */ - private Propagator createPropagator(final double[] point, final int index) { - // Create a new instance of the current propagator builder - final PropagatorBuilder copy = builders.get(index).copy(); - // Convert the given sigma point to an orbit - final Orbit orbit = orbitTypes[index].mapArrayToOrbit(point, null, angleTypes[index], copy.getInitialOrbitDate(), - copy.getMu(), copy.getFrame()); - copy.resetOrbit(orbit); - // Create the propagator - final Propagator propagator = copy.buildPropagator(copy.getSelectedNormalizedParameters()); - return propagator; + // Set the builder back to the original time + getBuilders().get(k).resetOrbit(originalState.getOrbit()); + + } + + return predictedState; } + /** Finalize estimation. * @param observedMeasurement measurement that has just been processed * @param estimate corrected estimate */ public void finalizeEstimation(final ObservedMeasurement observedMeasurement, final ProcessEstimate estimate) { - - correctedEstimate = estimate; - - // Loop on builders - for (int k = 0; k < builders.size(); k++ ) { - - // Update corrected state - final double[] correctedStateArray = estimate.getState().getSubVector(orbitsStartColumns[k], orbitsEndColumns[k] - orbitsStartColumns[k]).toArray(); - final Orbit correctedOrbit = orbitTypes[k].mapArrayToOrbit(correctedStateArray, null, angleTypes[k], - currentDate, builders.get(k).getMu(), builders.get(k).getFrame()); - correctedSpacecraftStates[k] = new SpacecraftState(correctedOrbit); - - // Update the builder - builders.get(k).resetOrbit(correctedOrbit); - + // Update the parameters with the estimated state + // The min/max values of the parameters are handled by the ParameterDriver implementation + setCorrectedEstimate(estimate); + updateParameters(estimate.getState()); + + // Get the estimated propagator (mirroring parameter update in the builder) + // and the estimated spacecraft state + final Propagator[] estimatedPropagators = getEstimatedPropagators(); + for (int k = 0; k < estimatedPropagators.length; ++k) { + setCorrectedSpacecraftState(estimatedPropagators[k].getInitialState(), k); } // Corrected measurement - correctedMeasurement = observedMeasurement.estimate(currentMeasurementNumber, - currentMeasurementNumber, - KalmanEstimatorUtil.filterRelevant(observedMeasurement, - getCorrectedSpacecraftStates())); + setCorrectedMeasurement(estimateMeasurement(observedMeasurement, getCurrentMeasurementNumber(), + KalmanEstimatorUtil.filterRelevant(observedMeasurement, + getCorrectedSpacecraftStates()))); } - /** Get the propagators estimated with the values set in the propagators builders. - * @return propagators based on the current values in the builder + /** + * Estimate measurement (without derivatives). + * @param measurement type + * @param observedMeasurement observed measurement + * @param measurementNumber measurement number + * @param spacecraftStates states + * @return estimated measurement + * @since 12.1 */ - public Propagator[] getEstimatedPropagators() { - // Return propagators built with current instantiation of the propagator builders - final Propagator[] propagators = new Propagator[builders.size()]; - for (int k = 0; k < builders.size(); ++k) { - propagators[k] = builders.get(k).buildPropagator(builders.get(k).getSelectedNormalizedParameters()); - } - return propagators; + private static > EstimatedMeasurement estimateMeasurement(final ObservedMeasurement observedMeasurement, + final int measurementNumber, + final SpacecraftState[] spacecraftStates) { + 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; } - /** Get the current corrected estimate. - * @return current corrected estimate + /** Update parameter drivers with a normalised state, adjusting state according to the driver limits. + * @param normalizedState the input state + * The min/max allowed values are handled by the parameter themselves. */ - public ProcessEstimate getEstimate() { - return correctedEstimate; - } - - /** {@inheritDoc} */ - @Override - public ParameterDriversList getEstimatedOrbitalParameters() { - return allEstimatedOrbitalParameters; - } - - /** {@inheritDoc} */ - @Override - public ParameterDriversList getEstimatedPropagationParameters() { - return allEstimatedPropagationParameters; - } - - /** {@inheritDoc} */ - @Override - public ParameterDriversList getEstimatedMeasurementsParameters() { - return estimatedMeasurementsParameters; - } - - /** {@inheritDoc} */ - @Override - public SpacecraftState[] getPredictedSpacecraftStates() { - return predictedSpacecraftStates.clone(); - } - - /** {@inheritDoc} */ - @Override - public SpacecraftState[] getCorrectedSpacecraftStates() { - return correctedSpacecraftStates.clone(); - } - - /** {@inheritDoc} */ - @Override - public RealVector getPhysicalEstimatedState() { - // Method {@link ParameterDriver#getValue()} is used to get - // the physical values of the state. - // The scales'array is used to get the size of the state vector - final RealVector physicalEstimatedState = new ArrayRealVector(getEstimate().getState().getDimension()); + private void updateParameters(final RealVector normalizedState) { int i = 0; for (final DelegatingDriver driver : getEstimatedOrbitalParameters().getDrivers()) { - physicalEstimatedState.setEntry(i++, driver.getValue()); + // let the parameter handle min/max clipping + driver.setReferenceValue(referenceValues[i]); + driver.setNormalizedValue(normalizedState.getEntry(i)); + normalizedState.setEntry(i++, driver.getNormalizedValue()); } for (final DelegatingDriver driver : getEstimatedPropagationParameters().getDrivers()) { - physicalEstimatedState.setEntry(i++, driver.getValue()); + // let the parameter handle min/max clipping + driver.setNormalizedValue(normalizedState.getEntry(i)); + normalizedState.setEntry(i++, driver.getNormalizedValue()); } for (final DelegatingDriver driver : getEstimatedMeasurementsParameters().getDrivers()) { - physicalEstimatedState.setEntry(i++, driver.getValue()); + // let the parameter handle min/max clipping + driver.setNormalizedValue(normalizedState.getEntry(i)); + normalizedState.setEntry(i++, driver.getNormalizedValue()); } - - return physicalEstimatedState; } - - /** {@inheritDoc} */ - @Override - public RealMatrix getPhysicalEstimatedCovarianceMatrix() { - return correctedEstimate.getCovariance(); - } - - - /** {@inheritDoc} */ - @Override - public RealMatrix getPhysicalStateTransitionMatrix() { - return null; - } - - /** {@inheritDoc} */ - @Override - public RealMatrix getPhysicalMeasurementJacobian() { - return null; - } - - /** {@inheritDoc} */ - @Override - public RealMatrix getPhysicalInnovationCovarianceMatrix() { - return correctedEstimate.getInnovationCovariance(); - } - - /** {@inheritDoc} */ - @Override - public RealMatrix getPhysicalKalmanGain() { - return correctedEstimate.getKalmanGain(); - } - - /** {@inheritDoc} */ - @Override - public int getCurrentMeasurementNumber() { - return currentMeasurementNumber; - } - - /** {@inheritDoc} */ - @Override - public AbsoluteDate getCurrentDate() { - return currentDate; - } - - /** {@inheritDoc} */ - @Override - public EstimatedMeasurement getPredictedMeasurement() { - return predictedMeasurement; - } - - /** {@inheritDoc} */ - @Override - public EstimatedMeasurement getCorrectedMeasurement() { - return correctedMeasurement; - } - } diff --git a/src/main/java/org/orekit/files/ccsds/section/CommentsContainer.java b/src/main/java/org/orekit/files/ccsds/section/CommentsContainer.java index bd4e02e4b2..19fdc0173d 100644 --- a/src/main/java/org/orekit/files/ccsds/section/CommentsContainer.java +++ b/src/main/java/org/orekit/files/ccsds/section/CommentsContainer.java @@ -35,7 +35,7 @@ public class CommentsContainer implements Section { /** Comments, as one line per string. */ - private final List comments; + private List comments; /** Indicator for accepting comments. */ private boolean acceptComments; @@ -105,6 +105,13 @@ public List getComments() { return Collections.unmodifiableList(comments); } + /** Set the comments. This removes all previous comments and replaces them with the new ones. + * @param comments List with new comments + */ + public void setComments(final List comments) { + this.comments = comments; + } + /** Check if container is still accepting comments. *

    * A container that still accept comments does not contain any other data. diff --git a/src/main/java/org/orekit/files/general/EphemerisFile.java b/src/main/java/org/orekit/files/general/EphemerisFile.java index 1c0572686d..3767919170 100644 --- a/src/main/java/org/orekit/files/general/EphemerisFile.java +++ b/src/main/java/org/orekit/files/general/EphemerisFile.java @@ -301,7 +301,8 @@ default Frame getInertialFrame() { * @return a propagator for this ephemeris segment. */ default BoundedPropagator getPropagator() { - return new EphemerisSegmentPropagator<>(this, new FrameAlignedProvider(getInertialFrame())); + return new EphemerisSegmentPropagator<>(this, + new FrameAlignedProvider(getInertialFrame())); } /** diff --git a/src/main/java/org/orekit/files/general/EphemerisSegmentPropagator.java b/src/main/java/org/orekit/files/general/EphemerisSegmentPropagator.java index 516d81b8a1..c26cad85bf 100644 --- a/src/main/java/org/orekit/files/general/EphemerisSegmentPropagator.java +++ b/src/main/java/org/orekit/files/general/EphemerisSegmentPropagator.java @@ -16,10 +16,7 @@ */ package org.orekit.files.general; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; - +import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.orekit.attitudes.AttitudeProvider; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; @@ -32,11 +29,13 @@ import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.analytical.AbstractAnalyticalPropagator; import org.orekit.time.AbsoluteDate; -import org.orekit.time.TimeInterpolator; -import org.orekit.utils.ImmutableTimeStampedCache; +import org.orekit.utils.SortedListTrimmer; import org.orekit.utils.TimeStampedPVCoordinates; import org.orekit.utils.TimeStampedPVCoordinatesHermiteInterpolator; +import java.util.ArrayList; +import java.util.List; + /** * A {@link Propagator} based on a {@link EphemerisSegment}. * @@ -48,17 +47,15 @@ * * @author Evan Ward */ -class EphemerisSegmentPropagator extends AbstractAnalyticalPropagator +public class EphemerisSegmentPropagator extends AbstractAnalyticalPropagator implements BoundedPropagator { - /** - * Sorted cache of state vectors. A duplication of the information in {@link - * #ephemeris} that could be avoided by duplicating the logic of {@link - * ImmutableTimeStampedCache#getNeighbors(AbsoluteDate)} for a general {@link List}. - */ - private final ImmutableTimeStampedCache cache; /** Tabular data from which this propagator is built. */ private final EphemerisSegment ephemeris; + /** Interpolator to use. + * @since 12.2 + */ + private final TimeStampedPVCoordinatesHermiteInterpolator interpolator; /** Inertial frame used for creating orbits. */ private final Frame inertialFrame; /** Frame of the ephemeris data. */ @@ -70,17 +67,16 @@ class EphemerisSegmentPropagator extends Abs * @param ephemeris segment containing the data for this propagator. * @param attitudeProvider provider for attitude computation */ - EphemerisSegmentPropagator(final EphemerisSegment ephemeris, - final AttitudeProvider attitudeProvider) { + public EphemerisSegmentPropagator(final EphemerisSegment ephemeris, + final AttitudeProvider attitudeProvider) { super(attitudeProvider); - this.cache = new ImmutableTimeStampedCache<>( - ephemeris.getInterpolationSamples(), - ephemeris.getCoordinates()); - this.ephemeris = ephemeris; + this.ephemeris = ephemeris; + this.interpolator = new TimeStampedPVCoordinatesHermiteInterpolator(ephemeris.getInterpolationSamples(), + ephemeris.getAvailableDerivatives()); this.ephemerisFrame = ephemeris.getFrame(); - this.inertialFrame = ephemeris.getInertialFrame(); + this.inertialFrame = ephemeris.getInertialFrame(); // set the initial state so getFrame() works - final TimeStampedPVCoordinates ic = cache.getEarliest(); + final TimeStampedPVCoordinates ic = ephemeris.getCoordinates().get(0); final TimeStampedPVCoordinates icInertial = ephemerisFrame .getTransformTo(inertialFrame, ic.getDate()) .transformPVCoordinates(ic); @@ -100,20 +96,14 @@ class EphemerisSegmentPropagator extends Abs @Override public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame frame) { - final Stream neighbors = this.cache.getNeighbors(date); - - // cast stream to super type - final Stream castedNeighbors = neighbors.map(neighbor -> (TimeStampedPVCoordinates) neighbor); - - // convert to list - final List castedNeighborsList = castedNeighbors.collect(Collectors.toList()); - - // create interpolator - final TimeInterpolator interpolator = - new TimeStampedPVCoordinatesHermiteInterpolator(castedNeighborsList.size(), ephemeris.getAvailableDerivatives()); + final TimeStampedPVCoordinates interpolatedPVCoordinates = interpolate(date); + return ephemerisFrame.getTransformTo(frame, date).transformPVCoordinates(interpolatedPVCoordinates); + } - final TimeStampedPVCoordinates point = interpolator.interpolate(date, castedNeighborsList); - return ephemerisFrame.getTransformTo(frame, date).transformPVCoordinates(point); + @Override + public Vector3D getPosition(final AbsoluteDate date, final Frame frame) { + final Vector3D interpolatedPosition = interpolate(date).getPosition(); + return ephemerisFrame.getStaticTransformTo(frame, date).transformPosition(interpolatedPosition); } @Override @@ -153,4 +143,21 @@ public void resetInitialState(final SpacecraftState state) { throw new OrekitException(OrekitMessages.NON_RESETABLE_STATE); } + /** Interpolate ephemeris segment at date. + * + * @param date interpolation date + * @return interpolated position-velocity vector + */ + private TimeStampedPVCoordinates interpolate(final AbsoluteDate date) { + final List neighbors = new SortedListTrimmer(interpolator.getNbInterpolationPoints()). + getNeighborsSubList(date, ephemeris.getCoordinates()); + + // cast stream to super type + final List castedNeighbors = new ArrayList<>(neighbors.size()); + castedNeighbors.addAll(neighbors); + + // create interpolator + return interpolator.interpolate(date, castedNeighbors); + } + } diff --git a/src/main/java/org/orekit/files/ilrs/CPF.java b/src/main/java/org/orekit/files/ilrs/CPF.java index ee284e42fa..aaf0199108 100644 --- a/src/main/java/org/orekit/files/ilrs/CPF.java +++ b/src/main/java/org/orekit/files/ilrs/CPF.java @@ -114,8 +114,7 @@ public List getComments() { * @since 11.0.1 */ public void addSatelliteCoordinates(final String id, final List coord) { - createIfNeeded(id); - ephemeris.get(id).coordinates.addAll(coord); + ephemeris.computeIfAbsent(id, i -> new CPFEphemeris(i)).coordinates.addAll(coord); } /** @@ -125,8 +124,7 @@ public void addSatelliteCoordinates(final String id, final List c * @since 11.0.1 */ public void addSatelliteCoordinate(final String id, final CPFCoordinate coord) { - createIfNeeded(id); - ephemeris.get(id).coordinates.add(coord); + ephemeris.computeIfAbsent(id, i -> new CPFEphemeris(i)).coordinates.add(coord); } /** @@ -181,16 +179,6 @@ public void setFilter(final CartesianDerivativesFilter filter) { this.filter = filter; } - /** - * Create the satellite ephemeris corresponding to the given ID (if needed). - * @param id satellite ILRS identifier - */ - private void createIfNeeded(final String id) { - if (ephemeris.get(id) == null) { - ephemeris.put(id, new CPFEphemeris(id)); - } - } - /** An ephemeris entry for a single satellite contains in a CPF file. */ public class CPFEphemeris implements EphemerisFile.SatelliteEphemeris, diff --git a/src/main/java/org/orekit/files/rinex/HatanakaCompressFilter.java b/src/main/java/org/orekit/files/rinex/HatanakaCompressFilter.java index 00fbdafd2e..32985a1e68 100644 --- a/src/main/java/org/orekit/files/rinex/HatanakaCompressFilter.java +++ b/src/main/java/org/orekit/files/rinex/HatanakaCompressFilter.java @@ -29,6 +29,7 @@ import org.hipparchus.util.FastMath; import org.orekit.data.DataFilter; import org.orekit.data.DataSource; +import org.orekit.data.LineOrientedFilteringReader; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; import org.orekit.gnss.SatelliteSystem; @@ -86,20 +87,11 @@ public DataSource filter(final DataSource original) { } /** Filtering of Hatanaka compressed characters stream. */ - private static class HatanakaReader extends Reader { + private static class HatanakaReader extends LineOrientedFilteringReader { /** Format of the current file. */ private final CompactRinexFormat format; - /** Line-oriented input. */ - private final BufferedReader reader; - - /** Pending uncompressed output lines. */ - private CharSequence pending; - - /** Number of characters already output in pending lines. */ - private int countOut; - /** Simple constructor. * @param name file name * @param input underlying compressed stream @@ -107,56 +99,14 @@ private static class HatanakaReader extends Reader { */ HatanakaReader(final String name, final Reader input) throws IOException { - - reader = new BufferedReader(input); - - // check header - format = CompactRinexFormat.getFormat(name, reader); - - pending = null; - - } - - /** {@inheritDoc} */ - @Override - public int read(final char[] b, final int offset, final int len) throws IOException { - - if (pending == null) { - // we need to read another section from the underlying characters stream and uncompress it - countOut = 0; - final String firstLine = reader.readLine(); - if (firstLine == null) { - // there are no lines left - return -1; - } else { - pending = format.uncompressSection(firstLine); - } - } - - // copy as many characters as possible from current line - int n = FastMath.min(len, pending.length() - countOut); - for (int i = 0; i < n; ++i) { - b[offset + i] = pending.charAt(countOut + i); - } - - if (n < len) { - // line has been completed and we can still output end of line - b[offset + n] = '\n'; - pending = null; - ++n; - } else { - // there are still some pending characters - countOut += n; - } - - return n; - + super(name, input); + format = CompactRinexFormat.getFormat(name, getBufferedReader()); } /** {@inheritDoc} */ @Override - public void close() throws IOException { - reader.close(); + protected CharSequence filterLine(final int lineNumber, final String originalLine) throws IOException { + return format.uncompressSection(originalLine); } } 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 fb05ef9ffe..cb36aca64e 100644 --- a/src/main/java/org/orekit/files/rinex/clock/RinexClock.java +++ b/src/main/java/org/orekit/files/rinex/clock/RinexClock.java @@ -17,12 +17,16 @@ package org.orekit.files.rinex.clock; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.SortedSet; +import java.util.TreeSet; import java.util.function.Function; +import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitIllegalArgumentException; import org.orekit.errors.OrekitMessages; import org.orekit.files.rinex.AppliedDCBS; @@ -32,7 +36,10 @@ import org.orekit.gnss.SatelliteSystem; import org.orekit.gnss.TimeSystem; import org.orekit.time.AbsoluteDate; +import org.orekit.time.ChronologicalComparator; +import org.orekit.time.ClockOffset; import org.orekit.time.DateComponents; +import org.orekit.time.SampledClockModel; import org.orekit.time.TimeComponents; import org.orekit.time.TimeScale; import org.orekit.utils.TimeSpanMap; @@ -81,7 +88,7 @@ public class RinexClock { private String comments; /** Satellite system code. */ - private Map> systemObservationTypes; + private final Map> systemObservationTypes; /** Time system. */ private TimeSystem timeSystem; @@ -96,13 +103,13 @@ public class RinexClock { private int numberOfLeapSecondsGNSS; /** List of applied differential code bias corrections. */ - private List listAppliedDCBS; + private final List listAppliedDCBS; /** List of antenna center variation corrections. */ - private List listAppliedPCVS; + private final List listAppliedPCVS; /** List of the data types in the file. */ - private List clockDataTypes; + private final List clockDataTypes; /** Station name for calibration and discontinuity data. */ private String stationName; @@ -120,35 +127,45 @@ public class RinexClock { private String analysisCenterName; /** Reference clocks. */ - private TimeSpanMap> referenceClocks; + private final TimeSpanMap> referenceClocks; /** Earth centered frame name as a string. */ private String frameName; - /** Maps {@link #coordinateSystem} to a {@link Frame}. */ + /** Maps {@link #frameName} to a {@link Frame}. */ private final Function frameBuilder; /** List of the receivers in the file. */ - private List receivers; + private final List receivers; /** List of the satellites in the file. */ - private List satellites; + private final List satellites; /** A map containing receiver/satellite information. */ - private Map> clockData; + private final Map> clockData; + + /** Earliest epoch. + * @since 12.1 + */ + private AbsoluteDate earliestEpoch; + + /** Latest epoch. + * @since 12.1 + */ + private AbsoluteDate latestEpoch; /** Constructor. * @param frameBuilder for constructing a reference frame from the identifier */ public RinexClock(final Function frameBuilder) { // Initialize fields with default data - this.systemObservationTypes = new HashMap>(); - this.listAppliedDCBS = new ArrayList(); - this.listAppliedPCVS = new ArrayList(); - this.clockDataTypes = new ArrayList(); - this.receivers = new ArrayList(); - this.satellites = new ArrayList(); - this.clockData = new HashMap>(); + this.systemObservationTypes = new HashMap<>(); + this.listAppliedDCBS = new ArrayList<>(); + this.listAppliedPCVS = new ArrayList<>(); + this.clockDataTypes = new ArrayList<>(); + this.receivers = new ArrayList<>(); + this.satellites = new ArrayList<>(); + this.clockData = new HashMap<>(); this.agencyName = ""; this.analysisCenterID = ""; this.analysisCenterName = ""; @@ -164,12 +181,14 @@ public RinexClock(final Function frameBuilder) this.numberOfLeapSeconds = 0; this.numberOfLeapSecondsGNSS = 0; this.programName = ""; - this.referenceClocks = null; + this.referenceClocks = new TimeSpanMap<>(null); this.satelliteSystem = null; this.stationIdentifier = ""; this.stationName = ""; this.timeScale = null; this.timeSystem = null; + this.earliestEpoch = AbsoluteDate.FUTURE_INFINITY; + this.latestEpoch = AbsoluteDate.PAST_INFINITY; } /** Add a new satellite with a given identifier to the list of stored satellites. @@ -191,6 +210,7 @@ public void addReceiver(final Receiver receiver) { for (Receiver rec : receivers) { if (rec.designator.equals(receiver.designator)) { notInList = false; + break; } } // only add satellites which have not been added before @@ -383,8 +403,9 @@ public Map> getSystemObservationTypes() { */ public void addSystemObservationType(final SatelliteSystem satSystem, final ObservationType observationType) { - systemObservationTypes.putIfAbsent(satSystem, new ArrayList()); - systemObservationTypes.get(satSystem).add(observationType); + systemObservationTypes. + computeIfAbsent(satSystem, s -> new ArrayList<>()). + add(observationType); } /** Getter for the file time system. @@ -562,16 +583,13 @@ public TimeSpanMap> getReferenceClocks() { return referenceClocks; } - /** Add a list of reference clocks wich will be used after a specified date. + /** Add a list of reference clocks which will be used after a specified date. * If the reference map has not been already created, it will be. * @param referenceClockList the reference clock list * @param startDate the date the list will be valid after. */ public void addReferenceClockList(final List referenceClockList, final AbsoluteDate startDate) { - if (referenceClocks == null) { - referenceClocks = new TimeSpanMap<>(null); - } referenceClocks.addValidAfter(referenceClockList, startDate, false); } @@ -590,7 +608,6 @@ public void setFrameName(final String frameName) { this.frameName = frameName; } - /** Getter for the receivers. * @return the list of the receivers */ @@ -612,6 +629,25 @@ public Frame getFrame() { return frameBuilder.apply(frameName); } + /** Extract the clock model. + * @param name receiver/satellite name + * @param nbInterpolationPoints number of points to use in interpolation + * @return extracted clock model + * @since 12.1 + */ + public SampledClockModel extractClockModel(final String name, + final int nbInterpolationPoints) { + final List sample = new ArrayList<>(); + clockData. + get(name). + forEach(c -> { + final double offset = c.clockBias; + final double rate = c.numberOfValues > 2 ? c.clockRate : Double.NaN; + final double acceleration = c.numberOfValues > 4 ? c.clockAcceleration : Double.NaN; + sample.add(new ClockOffset(c.getEpoch(), offset, rate, acceleration)); + }); + return new SampledClockModel(sample, nbInterpolationPoints); + } /** Getter for an unmodifiable map of clock data. * @return the clock data @@ -627,8 +663,166 @@ public Map> getClockData() { */ public void addClockData(final String id, final ClockDataLine clockDataLine) { - clockData.putIfAbsent(id, new ArrayList()); - clockData.get(id).add(clockDataLine); + clockData.computeIfAbsent(id, i -> new ArrayList<>()).add(clockDataLine); + final AbsoluteDate epoch = clockDataLine.getEpoch(); + if (epoch.isBefore(earliestEpoch)) { + earliestEpoch = epoch; + } + if (epoch.isAfter(latestEpoch)) { + latestEpoch = epoch; + } + } + + /** Get earliest epoch from the {@link #getClockData() clock data}. + * @return earliest epoch from the {@link #getClockData() clock data}, + * or {@link AbsoluteDate#FUTURE_INFINITY} if no data has been added + * @since 12.1 + */ + public AbsoluteDate getEarliestEpoch() { + return earliestEpoch; + } + + /** Get latest epoch from the {@link #getClockData() clock data}. + * @return latest epoch from the {@link #getClockData() clock data}, + * or {@link AbsoluteDate#PAST_INFINITY} if no data has been added + * @since 12.1 + */ + public AbsoluteDate getLatestEpoch() { + return latestEpoch; + } + + /** Splice several Rinex clock files together. + *

    + * Splicing Rinex clock files is intended to be used when continuous computation + * covering more than one file is needed. The metadata (version number, agency, …) + * will be retrieved from the earliest file only. Receivers and satellites + * will be merged from all files. Some receivers or satellites may be missing + * in some files… Once sorted (which is done internally), if the gap between + * segments from two file is larger than {@code maxGap}, then an error + * will be triggered. + *

    + *

    + * The spliced file only contains the receivers and satellites that were present + * in all files. Receivers and satellites present in some files and absent from + * other files are silently dropped. + *

    + *

    + * Depending on producer, successive clock files either have a gap between the last + * entry of one file and the first entry of the next file (for example files with + * a 5 minutes epoch interval may end at 23:55 and the next file start at 00:00), + * or both files have one point exactly at the splicing date (i.e. 24:00 one day + * and 00:00 next day). In the later case, the last point of the early file is dropped + * and the first point of the late file takes precedence, hence only one point remains + * in the spliced file ; this design choice is made to enforce continuity and + * regular interpolation. + *

    + * @param clocks clock files to merge + * @param maxGap maximum time gap between files + * @return merged clock file + * @since 12.1 + */ + public static RinexClock splice(final Collection clocks, + final double maxGap) { + + // sort the files + final ChronologicalComparator comparator = new ChronologicalComparator(); + final SortedSet sorted = + new TreeSet<>((c1, c2) -> comparator.compare(c1.earliestEpoch, c2.earliestEpoch)); + sorted.addAll(clocks); + + // prepare spliced file + final RinexClock first = sorted.first(); + final RinexClock spliced = new RinexClock(first.frameBuilder); + spliced.setFormatVersion(first.getFormatVersion()); + spliced.setSatelliteSystem(first.satelliteSystem); + spliced.setProgramName(first.getProgramName()); + spliced.setAgencyName(first.getAgencyName()); + spliced.setCreationDateString(first.getCreationDateString()); + spliced.setCreationTimeString(first.getCreationTimeString()); + spliced.setCreationTimeZoneString(first.getCreationTimeZoneString()); + spliced.setCreationDate(first.getCreationDate()); + spliced.addComment(first.getComments()); + first. + getSystemObservationTypes(). + forEach((s, l) -> l.forEach(o -> spliced.addSystemObservationType(s, o))); + spliced.setTimeSystem(first.getTimeSystem()); + spliced.setTimeScale(first.getTimeScale()); + spliced.setNumberOfLeapSeconds(first.getNumberOfLeapSeconds()); + spliced.setNumberOfLeapSecondsGNSS(first.getNumberOfLeapSecondsGNSS()); + first.getListAppliedDCBS().forEach(spliced::addAppliedDCBS); + first.getListAppliedPCVS().forEach(spliced::addAppliedPCVS); + first.getClockDataTypes().forEach(spliced::addClockDataType); + spliced.setStationName(first.getStationName()); + spliced.setStationIdentifier(first.getStationIdentifier()); + spliced.setExternalClockReference(first.getExternalClockReference()); + spliced.setAnalysisCenterID(first.getAnalysisCenterID()); + spliced.setAnalysisCenterName(first.getAnalysisCenterName()); + spliced.setFrameName(first.getFrameName()); + + // merge reference clocks maps + sorted.forEach(rc -> { + TimeSpanMap.Span> span = rc.getReferenceClocks().getFirstSpan(); + while (span != null) { + if (span.getData() != null) { + spliced.addReferenceClockList(span.getData(), span.getStart()); + } + span = span.next(); + } + }); + + final List clockIds = new ArrayList<>(); + + // identify the receivers that are present in all files + first. + getReceivers(). + stream(). + filter(r -> availableInAllFiles(r.getDesignator(), sorted)). + forEach(r -> { + spliced.addReceiver(r); + clockIds.add(r.getDesignator()); + }); + + // identify the satellites that are present in all files + first. + getSatellites(). + stream(). + filter(s -> availableInAllFiles(s, sorted)). + forEach(s -> { + spliced.addSatellite(s); + clockIds.add(s); + }); + + // add the clock lines + for (final String clockId : clockIds) { + AbsoluteDate previous = null; + for (final RinexClock rc : sorted) { + if (previous != null) { + if (rc.getEarliestEpoch().durationFrom(previous) > maxGap) { + throw new OrekitException(OrekitMessages.TOO_LONG_TIME_GAP_BETWEEN_DATA_POINTS, + rc.getEarliestEpoch().durationFrom(previous)); + } + } + previous = rc.getLatestEpoch(); + rc.getClockData().get(clockId).forEach(cd -> spliced.addClockData(clockId, cd)); + } + } + + return spliced; + + } + + /** Check if clock data is available in all files. + * @param clockId clock id + * @param files clock files + * @return true if clock is available in all files + */ + private static boolean availableInAllFiles(final String clockId, final Collection files) { + for (final RinexClock rc : files) { + if (!rc.getClockData().containsKey(clockId)) { + return false; + } + } + return true; } /** Clock data for a single station. @@ -639,39 +833,39 @@ public void addClockData(final String id, public class ClockDataLine { /** Clock data type. */ - private ClockDataType dataType; + private final ClockDataType dataType; /** Receiver/Satellite name. */ - private String name; + private final String name; /** Epoch date components. */ - private DateComponents dateComponents; + private final DateComponents dateComponents; /** Epoch time components. */ - private TimeComponents timeComponents; + private final TimeComponents timeComponents; /** Number of data values to follow. * This number might not represent the non zero values in the line. */ - private int numberOfValues; + private final int numberOfValues; /** Clock bias (seconds). */ - private double clockBias; + private final double clockBias; /** Clock bias sigma (seconds). */ - private double clockBiasSigma; + private final double clockBiasSigma; /** Clock rate (dimensionless). */ - private double clockRate; + private final double clockRate; /** Clock rate sigma (dimensionless). */ - private double clockRateSigma; + private final double clockRateSigma; /** Clock acceleration (seconds^-1). */ - private double clockAcceleration; + private final double clockAcceleration; /** Clock acceleration sigma (seconds^-1). */ - private double clockAccelrerationSigma; + private final double clockAccelerationSigma; /** Constructor. * @param type the clock data type @@ -704,7 +898,7 @@ public ClockDataLine (final ClockDataType type, final String name, this.clockRate = clockRate; this.clockRateSigma = clockRateSigma; this.clockAcceleration = clockAcceleration; - this.clockAccelrerationSigma = clockAccelerationSigma; + this.clockAccelerationSigma = clockAccelerationSigma; } /** Getter for the clock data type. @@ -787,7 +981,7 @@ public double getClockAcceleration() { * @return the clock acceleration sigma in seconds^-1 */ public double getClockAccelerationSigma() { - return clockAccelrerationSigma; + return clockAccelerationSigma; } } @@ -796,19 +990,19 @@ public double getClockAccelerationSigma() { public static class ReferenceClock { /** Receiver/satellite embedding the reference clock name. */ - private String referenceName; + private final String referenceName; /** Clock ID. */ - private String clockID; + private final String clockID; /** A priori clock constraint (in seconds). */ - private double clockConstraint; + private final double clockConstraint; /** Start date of the validity period. */ - private AbsoluteDate startDate; + private final AbsoluteDate startDate; /** End date of the validity period. */ - private AbsoluteDate endDate; + private final AbsoluteDate endDate; /** Constructor. * @param referenceName the name of the receiver/satellite embedding the reference clock @@ -867,19 +1061,19 @@ public AbsoluteDate getEndDate() { public static class Receiver { /** Designator. */ - private String designator; + private final String designator; /** Receiver identifier. */ - private String receiverIdentifier; + private final String receiverIdentifier; /** X coordinates in file considered Earth centered frame (in meters). */ - private double x; + private final double x; /** Y coordinates in file considered Earth centered frame (in meters). */ - private double y; + private final double y; /** Z coordinates in file considered Earth centered frame (in meters). */ - private double z; + private final double z; /** Constructor. * @param designator the designator diff --git a/src/main/java/org/orekit/files/rinex/clock/RinexClockParser.java b/src/main/java/org/orekit/files/rinex/clock/RinexClockParser.java index 7913a8ce5c..ba1fb09d5d 100644 --- a/src/main/java/org/orekit/files/rinex/clock/RinexClockParser.java +++ b/src/main/java/org/orekit/files/rinex/clock/RinexClockParser.java @@ -19,9 +19,7 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; +import java.io.Reader; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; @@ -33,8 +31,10 @@ import java.util.function.Function; import java.util.regex.Pattern; +import org.hipparchus.exception.LocalizedCoreFormats; import org.orekit.annotation.DefaultDataContext; import org.orekit.data.DataContext; +import org.orekit.data.DataSource; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; import org.orekit.files.rinex.AppliedDCBS; @@ -43,6 +43,7 @@ import org.orekit.files.rinex.clock.RinexClock.Receiver; import org.orekit.files.rinex.clock.RinexClock.ReferenceClock; import org.orekit.frames.Frame; +import org.orekit.gnss.IGSUtils; import org.orekit.gnss.ObservationType; import org.orekit.gnss.SatelliteSystem; import org.orekit.gnss.TimeSystem; @@ -51,7 +52,6 @@ import org.orekit.time.TimeComponents; import org.orekit.time.TimeScale; import org.orekit.time.TimeScales; -import org.orekit.utils.IERSConventions; /** A parser for the clock file from the IGS. * This parser handles versions 2.0 to 3.04 of the RINEX clock files. @@ -74,7 +74,7 @@ public class RinexClockParser { private static final List HANDLED_VERSIONS = Arrays.asList(2.00, 3.00, 3.01, 3.02, 3.04); /** Pattern for date format yyyy-mm-dd hh:mm. */ - private static final Pattern DATE_PATTERN_1 = Pattern.compile("^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}.*$");; + private static final Pattern DATE_PATTERN_1 = Pattern.compile("^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}.*$"); /** Pattern for date format yyyymmdd hhmmss zone or YYYYMMDD HHMMSS zone. */ private static final Pattern DATE_PATTERN_2 = Pattern.compile("^[0-9]{8}\\s{1,2}[0-9]{6}.*$"); @@ -103,23 +103,22 @@ public class RinexClockParser { /** Set of time scales. */ private final TimeScales timeScales; - /** - * Create an clock file parser using default values. - * - *

    This constructor uses the {@link DataContext#getDefault() default data context}. - * + /** Create a clock file parser using default values. + *

    + * This constructor uses the {@link DataContext#getDefault() default data context}, + * and {@link IGSUtils#guessFrame}. + *

    * @see #RinexClockParser(Function) */ @DefaultDataContext public RinexClockParser() { - this(RinexClockParser::guessFrame); + this(IGSUtils::guessFrame); } - /** - * Create a clock file parser and specify the frame builder. - * - *

    This constructor uses the {@link DataContext#getDefault() default data context}. - * + /** Create a clock file parser and specify the frame builder. + *

    + * This constructor uses the {@link DataContext#getDefault() default data context}. + *

    * @param frameBuilder is a function that can construct a frame from a clock file * coordinate system string. The coordinate system can be * any 5 character string e.g. ITR92, IGb08. @@ -137,35 +136,11 @@ public RinexClockParser(final Function frameBui * @param timeScales the set of time scales used for parsing dates. */ public RinexClockParser(final Function frameBuilder, - final TimeScales timeScales) { - + final TimeScales timeScales) { this.frameBuilder = frameBuilder; this.timeScales = timeScales; } - /** - * Default string to {@link Frame} conversion for {@link #CLockFileParser()}. - * - *

    This method uses the {@link DataContext#getDefault() default data context}. - * - * @param name of the frame. - * @return by default, return ITRF based on 2010 conventions, - * with tidal effects considered during EOP interpolation. - *

    If String matches to other already recorded frames, it will return the corresponding frame.

    - * Already embedded frames are: - *

    - ITRF96 - */ - @DefaultDataContext - private static Frame guessFrame(final String name) { - if (name.equals("ITRF96")) { - return DataContext.getDefault().getFrames() - .getITRF(IERSConventions.IERS_1996, false); - } else { - return DataContext.getDefault().getFrames() - .getITRF(IERSConventions.IERS_2010, false); - } - } - /** * Parse an IGS clock file from an input stream using the UTF-8 charset. * @@ -175,29 +150,24 @@ private static Frame guessFrame(final String name) { * * @param stream to read the IGS clock file from * @return a parsed IGS clock file - * @throws IOException if {@code stream} throws one * @see #parse(String) * @see #parse(BufferedReader, String) + * @see #parse(DataSource) */ - public RinexClock parse(final InputStream stream) throws IOException { - try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) { - return parse(reader, stream.toString()); - } + public RinexClock parse(final InputStream stream) { + return parse(new DataSource("", () -> stream)); } /** * Parse an IGS clock file from a file on the local file system. * @param fileName file name * @return a parsed IGS clock file - * @throws IOException if one is thrown while opening or reading from {@code fileName} * @see #parse(InputStream) * @see #parse(BufferedReader, String) + * @see #parse(DataSource) */ - public RinexClock parse(final String fileName) throws IOException { - try (BufferedReader reader = Files.newBufferedReader(Paths.get(fileName), - StandardCharsets.UTF_8)) { - return parse(reader, fileName); - } + public RinexClock parse(final String fileName) { + return parse(new DataSource(Paths.get(fileName).toFile())); } /** @@ -205,39 +175,56 @@ public RinexClock parse(final String fileName) throws IOException { * @param reader containing the clock file * @param fileName file name * @return a parsed IGS clock file - * @throws IOException if {@code reader} throws one * @see #parse(InputStream) * @see #parse(String) + * @see #parse(DataSource) + */ + public RinexClock parse(final BufferedReader reader, final String fileName) { + return parse(new DataSource(fileName, () -> reader)); + } + + /** Parse an IGS clock file from a {@link DataSource}. + * @param source source for clock file + * @return a parsed IGS clock file + * @see #parse(InputStream) + * @see #parse(String) + * @see #parse(BufferedReader, String) + * @since 12.1 */ - public RinexClock parse(final BufferedReader reader, - final String fileName) throws IOException { + public RinexClock parse(final DataSource source) { // initialize internal data structures final ParseInfo pi = new ParseInfo(); - int lineNumber = 0; - Iterable candidateParsers = Collections.singleton(LineParser.HEADER_VERSION); - nextLine: - for (String line = reader.readLine(); line != null; line = reader.readLine()) { - ++lineNumber; - for (final LineParser candidate : candidateParsers) { - if (candidate.canHandle(line)) { - try { - candidate.parse(line, pi); - candidateParsers = candidate.allowedNext(); - continue nextLine; - } catch (StringIndexOutOfBoundsException | NumberFormatException | InputMismatchException e) { - throw new OrekitException(e, - OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, - lineNumber, fileName, line); + try (Reader reader = source.getOpener().openReaderOnce(); + BufferedReader br = new BufferedReader(reader)) { + pi.lineNumber = 0; + Iterable candidateParsers = Collections.singleton(LineParser.HEADER_VERSION); + nextLine: + for (String line = br.readLine(); line != null; line = br.readLine()) { + ++pi.lineNumber; + for (final LineParser candidate : candidateParsers) { + if (candidate.canHandle(line)) { + try { + candidate.parse(line, pi); + candidateParsers = candidate.allowedNext(); + continue nextLine; + } catch (StringIndexOutOfBoundsException | + NumberFormatException | InputMismatchException e) { + throw new OrekitException(e, OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, + pi.lineNumber, source.getName(), line); + } } } - } - // no parsers found for this line - throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, - lineNumber, fileName, line); + // no parsers found for this line + throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, + pi.lineNumber, source.getName(), line); + + } + } catch (IOException ioe) { + throw new OrekitException(ioe, LocalizedCoreFormats.SIMPLE_MESSAGE, ioe.getLocalizedMessage()); } return pi.file; @@ -247,11 +234,14 @@ public RinexClock parse(final BufferedReader reader, /** Transient data used for parsing a clock file. */ private class ParseInfo { + /** Current line number of the navigation message. */ + private int lineNumber; + /** Set of time scales for parsing dates. */ private final TimeScales timeScales; /** The corresponding clock file object. */ - private RinexClock file; + private final RinexClock file; /** Current satellite system for observation type parsing. */ private SatelliteSystem currentSatelliteSystem; @@ -262,8 +252,8 @@ private class ParseInfo { /** Current end date for reference clocks. */ private AbsoluteDate referenceClockEndDate; - /** Current reference clock list. */ - private List currentReferenceClocks; + /** Pending reference clocks list. */ + private List pendingReferenceClocks; /** Current clock data type. */ private ClockDataType currentDataType; @@ -287,6 +277,7 @@ private class ParseInfo { protected ParseInfo () { this.timeScales = RinexClockParser.this.timeScales; this.file = new RinexClock(frameBuilder); + this.pendingReferenceClocks = new ArrayList<>(); } } @@ -320,11 +311,13 @@ public void parse(final String line, final ParseInfo pi) { final String satelliteSystemString = line.substring(40, 45).trim(); // Check satellite if system is recorded - if (!satelliteSystemString.equals("")) { + if (!satelliteSystemString.isEmpty()) { // Record satellite system and default time system in clock file object final SatelliteSystem satelliteSystem = SatelliteSystem.parseSatelliteSystem(satelliteSystemString); pi.file.setSatelliteSystem(satelliteSystem); - pi.file.setTimeScale(satelliteSystem.getObservationTimeScale().getTimeScale(pi.timeScales)); + if (satelliteSystem.getObservationTimeScale() != null) { + pi.file.setTimeScale(satelliteSystem.getObservationTimeScale().getTimeScale(pi.timeScales)); + } } // Set time scale to UTC by default if (pi.file.getTimeScale() == null) { @@ -501,24 +494,26 @@ public void parse(final String line, final ParseInfo pi) { /** {@inheritDoc} */ @Override public void parse(final String line, final ParseInfo pi) { + // First element, if present, is the related satellite system + final String system = line.substring(0, 1); + if (!" ".equals(system)) { + final SatelliteSystem satelliteSystem = SatelliteSystem.parseSatelliteSystem(system); + + // Second element is the program name + final String progDCBS = line.substring(2, 20).trim(); + + // Third element is the source of the corrections + String sourceDCBS = ""; + if (pi.file.getFormatVersion() < 3.04) { + sourceDCBS = line.substring(19, 60).trim(); + } else { + sourceDCBS = line.substring(22, 65).trim(); + } - // First element is the related satellite system - final SatelliteSystem satelliteSystem = SatelliteSystem.parseSatelliteSystem(line.substring(0, 1)); - - // Second element is the program name - final String progDCBS = line.substring(2, 20).trim(); - - // Third element is the source of the corrections - String sourceDCBS = ""; - if (pi.file.getFormatVersion() < 3.04) { - sourceDCBS = line.substring(19, 60).trim(); - } else { - sourceDCBS = line.substring(22, 65).trim(); - } - - // Check if sought fields were not actually blanks - if (!progDCBS.equals("")) { - pi.file.addAppliedDCBS(new AppliedDCBS(satelliteSystem, progDCBS, sourceDCBS)); + // Check if sought fields were not actually blanks + if (!progDCBS.isEmpty()) { + pi.file.addAppliedDCBS(new AppliedDCBS(satelliteSystem, progDCBS, sourceDCBS)); + } } } @@ -531,23 +526,26 @@ public void parse(final String line, final ParseInfo pi) { @Override public void parse(final String line, final ParseInfo pi) { - // First element is the related satellite system - final SatelliteSystem satelliteSystem = SatelliteSystem.parseSatelliteSystem(line.substring(0, 1)); + // First element, if present, is the related satellite system + final String system = line.substring(0, 1); + if (!" ".equals(system)) { + final SatelliteSystem satelliteSystem = SatelliteSystem.parseSatelliteSystem(system); - // Second element is the program name - final String progPCVS = line.substring(2, 20).trim(); + // Second element is the program name + final String progPCVS = line.substring(2, 20).trim(); - // Third element is the source of the corrections - String sourcePCVS = ""; - if (pi.file.getFormatVersion() < 3.04) { - sourcePCVS = line.substring(19, 60).trim(); - } else { - sourcePCVS = line.substring(22, 65).trim(); - } + // Third element is the source of the corrections + String sourcePCVS = ""; + if (pi.file.getFormatVersion() < 3.04) { + sourcePCVS = line.substring(19, 60).trim(); + } else { + sourcePCVS = line.substring(22, 65).trim(); + } - // Check if sought fields were not actually blanks - if (!progPCVS.equals("") || !sourcePCVS.equals("")) { - pi.file.addAppliedPCVS(new AppliedPCVS(satelliteSystem, progPCVS, sourcePCVS)); + // Check if sought fields were not actually blanks + if (!progPCVS.isEmpty() || !sourcePCVS.isEmpty()) { + pi.file.addAppliedPCVS(new AppliedPCVS(satelliteSystem, progPCVS, sourcePCVS)); + } } } @@ -646,8 +644,12 @@ public void parse(final String line, final ParseInfo pi) { Scanner s2 = s1.useDelimiter(SPACES); Scanner scanner = s2.useLocale(Locale.US)) { - // Initialize current reference clock list corresponding to the period - pi.currentReferenceClocks = new ArrayList(); + if (!pi.pendingReferenceClocks.isEmpty()) { + // Modify time span map of the reference clocks to accept the pending reference clock + pi.file.addReferenceClockList(pi.pendingReferenceClocks, + pi.referenceClockStartDate); + pi.pendingReferenceClocks = new ArrayList<>(); + } // First element is the number of reference clocks corresponding to the period scanner.nextInt(); @@ -715,10 +717,8 @@ public void parse(final String line, final ParseInfo pi) { // Add reference clock to current reference clock list final ReferenceClock referenceClock = new ReferenceClock(referenceName, clockID, clockConstraint, pi.referenceClockStartDate, pi.referenceClockEndDate); - pi.currentReferenceClocks.add(referenceClock); + pi.pendingReferenceClocks.add(referenceClock); - // Modify time span map of the reference clocks to accept the new reference clock - pi.file.addReferenceClockList(pi.currentReferenceClocks, pi.referenceClockStartDate); } } @@ -835,7 +835,10 @@ public void parse(final String line, final ParseInfo pi) { /** {@inheritDoc} */ @Override public void parse(final String line, final ParseInfo pi) { - // do nothing... + if (!pi.pendingReferenceClocks.isEmpty()) { + // Modify time span map of the reference clocks to accept the pending reference clock + pi.file.addReferenceClockList(pi.pendingReferenceClocks, pi.referenceClockStartDate); + } } /** {@inheritDoc} */ @@ -1013,7 +1016,7 @@ private static void parseDateTimeZone(final String dateString, final ParseInfo p time = dateString.substring(9, 16).trim(); zone = dateString.substring(16).trim(); - if (!zone.equals("")) { + if (!zone.isEmpty()) { // Get date and time components dateComponents = new DateComponents(Integer.parseInt(date.substring(0, 4)), Integer.parseInt(date.substring(4, 6)), diff --git a/src/main/java/org/orekit/files/rinex/observation/ObservationDataSet.java b/src/main/java/org/orekit/files/rinex/observation/ObservationDataSet.java index f298c30174..b902ea383f 100644 --- a/src/main/java/org/orekit/files/rinex/observation/ObservationDataSet.java +++ b/src/main/java/org/orekit/files/rinex/observation/ObservationDataSet.java @@ -87,7 +87,7 @@ public int getEventFlag() { } /** Get list of observation data. - * @return unmodifiable view of of observation data for the observed satellite + * @return unmodifiable view of observation data for the observed satellite */ public List getObservationData() { return Collections.unmodifiableList(observationData); diff --git a/src/main/java/org/orekit/files/rinex/observation/RinexObservation.java b/src/main/java/org/orekit/files/rinex/observation/RinexObservation.java index cb4ab4e38f..f955f7fc79 100644 --- a/src/main/java/org/orekit/files/rinex/observation/RinexObservation.java +++ b/src/main/java/org/orekit/files/rinex/observation/RinexObservation.java @@ -25,6 +25,8 @@ import org.orekit.errors.OrekitMessages; import org.orekit.files.rinex.RinexFile; import org.orekit.time.AbsoluteDate; +import org.orekit.time.ClockOffset; +import org.orekit.time.SampledClockModel; /** Container for Rinex observation file. * @author Luc Maisonobe @@ -68,7 +70,7 @@ public void addObservationDataSet(final ObservationDataSet observationsDataSet) final AbsoluteDate previous = observations.get(observations.size() - 1).getDate(); final double factor = current.durationFrom(previous) / header.getInterval(); final double acceptable = FastMath.max(0.0, FastMath.rint(factor)); - if (FastMath.abs(factor - acceptable) > 0.001) { + if (FastMath.abs(factor - acceptable) > 0.01) { throw new OrekitIllegalArgumentException(OrekitMessages.INCONSISTENT_SAMPLING_DATE, previous.shiftedBy(acceptable * header.getInterval()), current); @@ -87,4 +89,31 @@ public void addObservationDataSet(final ObservationDataSet observationsDataSet) } + /** Extract the receiver clock model. + * @param nbInterpolationPoints number of points to use in interpolation + * @return extracted clock model or null if all {@link + * ObservationDataSet#getRcvrClkOffset() clock offsets} are zero + * @since 12.1 + */ + public SampledClockModel extractClockModel(final int nbInterpolationPoints) { + final List sample = new ArrayList<>(); + boolean someNonZero = false; + AbsoluteDate previous = null; + for (final ObservationDataSet ods : observations) { + if (previous == null || ods.getDate().durationFrom(previous) > 0.5 * getHeader().getInterval()) { + // this is a new date + sample.add(new ClockOffset(ods.getDate(), ods.getRcvrClkOffset(), + Double.NaN, Double.NaN)); + someNonZero |= ods.getRcvrClkOffset() != 0; + } + previous = ods.getDate(); + } + + // build a clock model only if at least some non-zero offsets have been found + return someNonZero ? + new SampledClockModel(sample, nbInterpolationPoints) : + null; + + } + } 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 601df60287..869f43a397 100644 --- a/src/main/java/org/orekit/files/rinex/observation/RinexObservationHeader.java +++ b/src/main/java/org/orekit/files/rinex/observation/RinexObservationHeader.java @@ -117,20 +117,22 @@ public class RinexObservationHeader extends RinexBaseHeader { /** Time of last observation record. */ private AbsoluteDate tLastObs; - /** Real time-derived receiver clock offset. */ - private int clkOffset; + /** Flag for application of real time-derived receiver clock offset. + * @since 12.1 + */ + private boolean clockOffsetApplied; /** List of applied differential code bias corrections. */ - private List listAppliedDCBS; + private final List listAppliedDCBS; /** List of antenna center variation corrections. */ - private List listAppliedPCVS; + private final List listAppliedPCVS; /** List of phase shift correction used to generate phases consistent w/r to cycle shifts. */ - private List phaseShiftCorrections; + private final List phaseShiftCorrections; /** List of scale factor corrections. */ - private Map> scaleFactorCorrections; + private final Map> scaleFactorCorrections; /** List of GLONASS satellite-channel associations. * @since 12.0 @@ -196,7 +198,7 @@ public RinexObservationHeader() { antennaAzimuth = Double.NaN; antennaHeight = Double.NaN; eccentricities = Vector2D.ZERO; - clkOffset = -1; + clockOffsetApplied = false; nbSat = -1; interval = Double.NaN; leapSeconds = 0; @@ -385,16 +387,36 @@ public Vector2D getEccentricities() { /** Set the realtime-derived receiver clock offset. * @param clkOffset realtime-derived receiver clock offset + * @deprecated as of 12.1, replaced by {@link #setClockOffsetApplied(boolean)} */ + @Deprecated public void setClkOffset(final int clkOffset) { - this.clkOffset = clkOffset; + setClockOffsetApplied(clkOffset > 0); } /** Get the realtime-derived receiver clock offset. * @return realtime-derived receiver clock offset + * @deprecated as of 12.1, replaced by #@link {@link #getClockOffsetApplied()} */ + @Deprecated public int getClkOffset() { - return clkOffset; + return getClockOffsetApplied() ? 1 : 0; + } + + /** Set the application flag for realtime-derived receiver clock offset. + * @param clockOffsetApplied application flag for realtime-derived receiver clock offset + * @since 12.1 + */ + public void setClockOffsetApplied(final boolean clockOffsetApplied) { + this.clockOffsetApplied = clockOffsetApplied; + } + + /** Get the application flag for realtime-derived receiver clock offset. + * @return application flag for realtime-derived receiver clock offset + * @since 12.1 + */ + public boolean getClockOffsetApplied() { + return clockOffsetApplied; } /** Set the observation interval in seconds. @@ -684,11 +706,8 @@ public List getPhaseShiftCorrections() { * @param scaleFactorCorrection scale factor correction */ public void addScaleFactorCorrection(final SatelliteSystem satelliteSystem, final ScaleFactorCorrection scaleFactorCorrection) { - List sfc = scaleFactorCorrections.get(satelliteSystem); - if (sfc == null) { - sfc = new ArrayList<>(); - scaleFactorCorrections.put(satelliteSystem, sfc); - } + final List sfc = scaleFactorCorrections.computeIfAbsent(satelliteSystem, + k -> new ArrayList<>()); sfc.add(scaleFactorCorrection); } @@ -740,11 +759,7 @@ public int getNbSat() { * @since 12.0 */ public void setNbObsPerSatellite(final SatInSystem sat, final ObservationType type, final int nbObs) { - Map satNbObs = nbObsPerSat.get(sat); - if (satNbObs == null) { - satNbObs = new HashMap<>(); - nbObsPerSat.put(sat, satNbObs); - } + final Map satNbObs = nbObsPerSat.computeIfAbsent(sat, k -> new HashMap<>()); satNbObs.put(type, nbObs); } diff --git a/src/main/java/org/orekit/files/rinex/observation/RinexObservationParser.java b/src/main/java/org/orekit/files/rinex/observation/RinexObservationParser.java index c858485fed..e319fbd94b 100644 --- a/src/main/java/org/orekit/files/rinex/observation/RinexObservationParser.java +++ b/src/main/java/org/orekit/files/rinex/observation/RinexObservationParser.java @@ -138,6 +138,7 @@ public RinexObservation parse(final DataSource source) { parseInfo.lineNumber, source.getName(), line); } + } catch (IOException ioe) { throw new OrekitException(ioe, LocalizedCoreFormats.SIMPLE_MESSAGE, ioe.getLocalizedMessage()); } @@ -166,6 +167,12 @@ private class ParseInfo { /** Date of the observation. */ private AbsoluteDate tObs; + /** Indicator that time of first observation was already fixed. */ + private boolean tFirstFixed; + + /** Indicator that time of last observation was already fixed. */ + private boolean tLastFixed; + /** Receiver clock offset (seconds). */ private double rcvrClkOffset; @@ -242,6 +249,8 @@ private class ParseInfo { this.file = new RinexObservation(); this.lineNumber = 0; this.tObs = AbsoluteDate.PAST_INFINITY; + this.tFirstFixed = false; + this.tLastFixed = false; this.timeScale = null; this.nbTypes = -1; this.nbSatObs = -1; @@ -256,6 +265,33 @@ private class ParseInfo { this.satObs = new ArrayList<>(); } + /** Set observation date, taking care of receiver/absolute time scales. + * @param rawDate date as parsed, prior to any time scale modification + */ + private void setTObs(final AbsoluteDate rawDate) { + final RinexObservationHeader header = file.getHeader(); + if (header.getClockOffsetApplied()) { + // date was already in an absolute time scale + tObs = rawDate; + } else { + // the epoch was expressed in receiver clock + // we need to convert it to absolute date + if (FastMath.abs(rawDate.durationFrom(header.getTFirstObs())) < 1.0e-6 && + !tFirstFixed) { + // we need to fix the first date in the header too + header.setTFirstObs(header.getTFirstObs().shiftedBy(-rcvrClkOffset)); + tFirstFixed = true; + } + if (FastMath.abs(rawDate.durationFrom(header.getTLastObs())) < 1.0e-6 && + !tLastFixed) { + // we need to fix the last date in the header too + header.setTLastObs(header.getTLastObs().shiftedBy(-rcvrClkOffset)); + tLastFixed = true; + } + tObs = rawDate.shiftedBy(-rcvrClkOffset); + } + } + } /** Parsers for specific lines. */ @@ -321,11 +357,9 @@ private enum LineParser { /** Parser for approximative position. */ APPROX_POSITION_XYZ(line -> RinexLabels.APPROX_POSITION_XYZ.matches(RinexUtils.getLabel(line)), - (line, parseInfo) -> { - parseInfo.file.getHeader().setApproxPos(new Vector3D(RinexUtils.parseDouble(line, 0, 14), - RinexUtils.parseDouble(line, 14, 14), - RinexUtils.parseDouble(line, 28, 14))); - }, + (line, parseInfo) -> parseInfo.file.getHeader().setApproxPos(new Vector3D(RinexUtils.parseDouble(line, 0, 14), + RinexUtils.parseDouble(line, 14, 14), + RinexUtils.parseDouble(line, 28, 14))), LineParser::headerNext), /** Parser for antenna reference point. */ @@ -339,11 +373,9 @@ private enum LineParser { /** Parser for antenna reference point. */ ANTENNA_DELTA_X_Y_Z(line -> RinexLabels.ANTENNA_DELTA_X_Y_Z.matches(RinexUtils.getLabel(line)), - (line, parseInfo) -> { - parseInfo.file.getHeader().setAntennaReferencePoint(new Vector3D(RinexUtils.parseDouble(line, 0, 14), - RinexUtils.parseDouble(line, 14, 14), - RinexUtils.parseDouble(line, 28, 14))); - }, + (line, parseInfo) -> parseInfo.file.getHeader().setAntennaReferencePoint(new Vector3D(RinexUtils.parseDouble(line, 0, 14), + RinexUtils.parseDouble(line, 14, 14), + RinexUtils.parseDouble(line, 28, 14))), LineParser::headerNext), /** Parser for antenna phase center. */ @@ -359,11 +391,9 @@ private enum LineParser { /** Parser for antenna bore sight. */ ANTENNA_B_SIGHT_XYZ(line -> RinexLabels.ANTENNA_B_SIGHT_XYZ.matches(RinexUtils.getLabel(line)), - (line, parseInfo) -> { - parseInfo.file.getHeader().setAntennaBSight(new Vector3D(RinexUtils.parseDouble(line, 0, 14), - RinexUtils.parseDouble(line, 14, 14), - RinexUtils.parseDouble(line, 28, 14))); - }, + (line, parseInfo) -> parseInfo.file.getHeader().setAntennaBSight(new Vector3D(RinexUtils.parseDouble(line, 0, 14), + RinexUtils.parseDouble(line, 14, 14), + RinexUtils.parseDouble(line, 28, 14))), LineParser::headerNext), /** Parser for antenna zero direction. */ @@ -401,11 +431,9 @@ private enum LineParser { /** Parser for center of mass. */ CENTER_OF_MASS_XYZ(line -> RinexLabels.CENTER_OF_MASS_XYZ.matches(RinexUtils.getLabel(line)), - (line, parseInfo) -> { - parseInfo.file.getHeader().setCenterMass(new Vector3D(RinexUtils.parseDouble(line, 0, 14), - RinexUtils.parseDouble(line, 14, 14), - RinexUtils.parseDouble(line, 28, 14))); - }, + (line, parseInfo) -> parseInfo.file.getHeader().setCenterMass(new Vector3D(RinexUtils.parseDouble(line, 0, 14), + RinexUtils.parseDouble(line, 14, 14), + RinexUtils.parseDouble(line, 28, 14))), LineParser::headerNext), /** Parser for DOI. @@ -523,20 +551,18 @@ private enum LineParser { /** Parser for time of last observation. */ TIME_OF_LAST_OBS(line -> RinexLabels.TIME_OF_LAST_OBS.matches(RinexUtils.getLabel(line)), - (line, parseInfo) -> { - parseInfo.file.getHeader().setTLastObs(new AbsoluteDate(RinexUtils.parseInt(line, 0, 6), - RinexUtils.parseInt(line, 6, 6), - RinexUtils.parseInt(line, 12, 6), - RinexUtils.parseInt(line, 18, 6), - RinexUtils.parseInt(line, 24, 6), - RinexUtils.parseDouble(line, 30, 13), - parseInfo.timeScale)); - }, + (line, parseInfo) -> parseInfo.file.getHeader().setTLastObs(new AbsoluteDate(RinexUtils.parseInt(line, 0, 6), + RinexUtils.parseInt(line, 6, 6), + RinexUtils.parseInt(line, 12, 6), + RinexUtils.parseInt(line, 18, 6), + RinexUtils.parseInt(line, 24, 6), + RinexUtils.parseDouble(line, 30, 13), + parseInfo.timeScale)), LineParser::headerNext), /** Parser for indicator of receiver clock offset application. */ RCV_CLOCK_OFFS_APPL(line -> RinexLabels.RCV_CLOCK_OFFS_APPL.matches(RinexUtils.getLabel(line)), - (line, parseInfo) -> parseInfo.file.getHeader().setClkOffset(RinexUtils.parseInt(line, 0, 6)), + (line, parseInfo) -> parseInfo.file.getHeader().setClockOffsetApplied(RinexUtils.parseInt(line, 0, 6) > 0), LineParser::headerNext), /** Parser for differential code bias corrections. */ @@ -649,25 +675,25 @@ private enum LineParser { // C1C signal final String c1c = RinexUtils.parseString(line, 1, 3); - if (c1c.length() > 0) { + if (!c1c.isEmpty()) { parseInfo.file.getHeader().setC1cCodePhaseBias(RinexUtils.parseDouble(line, 5, 8)); } // C1P signal final String c1p = RinexUtils.parseString(line, 14, 3); - if (c1p.length() > 0) { + if (!c1p.isEmpty()) { parseInfo.file.getHeader().setC1pCodePhaseBias(RinexUtils.parseDouble(line, 18, 8)); } // C2C signal final String c2c = RinexUtils.parseString(line, 27, 3); - if (c2c.length() > 0) { + if (!c2c.isEmpty()) { parseInfo.file.getHeader().setC2cCodePhaseBias(RinexUtils.parseDouble(line, 31, 8)); } // C2P signal final String c2p = RinexUtils.parseString(line, 40, 3); - if (c2p.length() > 0) { + if (!c2p.isEmpty()) { parseInfo.file.getHeader().setC2pCodePhaseBias(RinexUtils.parseDouble(line, 44, 8)); } @@ -695,7 +721,7 @@ private enum LineParser { PRN_NB_OF_OBS(line -> RinexLabels.PRN_NB_OF_OBS.matches(RinexUtils.getLabel(line)), (line, parseInfo) -> { final String systemName = RinexUtils.parseString(line, 3, 1); - if (systemName.length() > 0) { + if (!systemName.isEmpty()) { final SatelliteSystem system = SatelliteSystem.parseSatelliteSystem(systemName); final int prn = RinexUtils.parseInt(line, 4, 2); parseInfo.currentSat = new SatInSystem(system, @@ -713,7 +739,7 @@ private enum LineParser { (i + size) <= RinexUtils.LABEL_INDEX && parseInfo.nbTypes < types.size(); i += increment) { final String nb = RinexUtils.parseString(line, i, size); - if (nb.length() > 0) { + if (!nb.isEmpty()) { parseInfo.file.getHeader().setNbObsPerSatellite(parseInfo.currentSat, types.get(parseInfo.nbTypes), RinexUtils.parseInt(line, i, size)); } @@ -751,7 +777,7 @@ private enum LineParser { parseInfo.file.getHeader().getReceiverNumber() == null || parseInfo.file.getHeader().getAntennaNumber() == null || Double.isNaN(parseInfo.file.getHeader().getAntennaHeight()) && - parseInfo.file.getHeader().getAntennaReferencePoint() == null || + parseInfo.file.getHeader().getAntennaReferencePoint() == null || parseInfo.file.getHeader().getTFirstObs() == null || parseInfo.file.getHeader().getTypeObs().isEmpty()) { throw new OrekitException(OrekitMessages.INCOMPLETE_HEADER, parseInfo.name); @@ -840,13 +866,13 @@ private enum LineParser { if (!parseInfo.specialRecord) { // observations epoch - parseInfo.tObs = new AbsoluteDate(RinexUtils.convert2DigitsYear(RinexUtils.parseInt(line, 1, 2)), - RinexUtils.parseInt(line, 4, 2), - RinexUtils.parseInt(line, 7, 2), - RinexUtils.parseInt(line, 10, 2), - RinexUtils.parseInt(line, 13, 2), - RinexUtils.parseDouble(line, 15, 11), - parseInfo.timeScale); + parseInfo.setTObs(new AbsoluteDate(RinexUtils.convert2DigitsYear(RinexUtils.parseInt(line, 1, 2)), + RinexUtils.parseInt(line, 4, 2), + RinexUtils.parseInt(line, 7, 2), + RinexUtils.parseInt(line, 10, 2), + RinexUtils.parseInt(line, 13, 2), + RinexUtils.parseDouble(line, 15, 11), + parseInfo.timeScale)); // satellites list RINEX_2_DATA_SAT_LIST.parsingMethod.parse(line, parseInfo); @@ -998,13 +1024,13 @@ private enum LineParser { if (!parseInfo.specialRecord) { // observations epoch - parseInfo.tObs = new AbsoluteDate(RinexUtils.parseInt(line, 2, 4), - RinexUtils.parseInt(line, 7, 2), - RinexUtils.parseInt(line, 10, 2), - RinexUtils.parseInt(line, 13, 2), - RinexUtils.parseInt(line, 16, 2), - RinexUtils.parseDouble(line, 18, 11), - parseInfo.timeScale); + parseInfo.setTObs(new AbsoluteDate(RinexUtils.parseInt(line, 2, 4), + RinexUtils.parseInt(line, 7, 2), + RinexUtils.parseInt(line, 10, 2), + RinexUtils.parseInt(line, 13, 2), + RinexUtils.parseInt(line, 16, 2), + RinexUtils.parseDouble(line, 18, 11), + parseInfo.timeScale)); } 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 fec1974595..0cadd0e273 100644 --- a/src/main/java/org/orekit/files/rinex/observation/RinexObservationWriter.java +++ b/src/main/java/org/orekit/files/rinex/observation/RinexObservationWriter.java @@ -37,6 +37,8 @@ import org.orekit.gnss.SatInSystem; import org.orekit.gnss.SatelliteSystem; import org.orekit.time.AbsoluteDate; +import org.orekit.time.ClockModel; +import org.orekit.time.ClockTimeScale; import org.orekit.time.DateTimeComponents; import org.orekit.time.TimeScale; import org.orekit.time.TimeScalesFactory; @@ -122,6 +124,9 @@ public class RinexObservationWriter implements AutoCloseable { /** Output name for error messages. */ private final String outputName; + /** Receiver clock offset model. */ + private ClockModel receiverClockModel; + /** Time scale for writing dates. */ private TimeScale timeScale; @@ -160,6 +165,14 @@ public void close() throws IOException { processPending(); } + /** Set receiver clock model. + * @param receiverClockModel receiver clock model + * @since 12.1 + */ + public void setReceiverClockModel(final ClockModel receiverClockModel) { + this.receiverClockModel = receiverClockModel; + } + /** Write a complete observation file. *

    * This method calls {@link #prepareComments(List)} and @@ -212,6 +225,12 @@ public void writeHeader(final RinexObservationHeader header) header.getSatelliteSystem().getObservationTimeScale() : ObservationTimeScale.GPS; timeScale = observationTimeScale.getTimeScale(TimeScalesFactory.getTimeScales()); + if (!header.getClockOffsetApplied() && receiverClockModel != null) { + // getClockOffsetApplied returned false, which means the measurements + // should *NOT* be put in system time scale, and the receiver has a clock model + // we have to set up a time scale corresponding to this receiver clock + timeScale = new ClockTimeScale(timeScale.getName(), timeScale, receiverClockModel); + } // RINEX VERSION / TYPE outputField("%9.2f", header.getFormatVersion(), 9); @@ -401,10 +420,8 @@ public void writeHeader(final RinexObservationHeader header) } // RCV CLOCK OFFS APPL - if (header.getClkOffset() >= 0) { - outputField(SIX_DIGITS_INTEGER, header.getClkOffset(), 6); - finishHeaderLine(RinexLabels.RCV_CLOCK_OFFS_APPL); - } + outputField(SIX_DIGITS_INTEGER, header.getClockOffsetApplied() ? 1 : 0, 6); + finishHeaderLine(RinexLabels.RCV_CLOCK_OFFS_APPL); // SYS / DCBS APPLIED for (final AppliedDCBS appliedDCBS : header.getListAppliedDCBS()) { diff --git a/src/main/java/org/orekit/files/sinex/SinexLoader.java b/src/main/java/org/orekit/files/sinex/SinexLoader.java index f2a1d36904..7412767564 100644 --- a/src/main/java/org/orekit/files/sinex/SinexLoader.java +++ b/src/main/java/org/orekit/files/sinex/SinexLoader.java @@ -50,6 +50,7 @@ import org.orekit.frames.ITRFVersion; import org.orekit.gnss.SatelliteSystem; import org.orekit.gnss.TimeSystem; +import org.orekit.models.earth.displacement.PsdCorrection; import org.orekit.time.AbsoluteDate; import org.orekit.time.ChronologicalComparator; import org.orekit.time.DateComponents; @@ -80,6 +81,96 @@ */ public class SinexLoader implements EopHistoryLoader { + /** Station X position coordinate. + * @since 12.1 + */ + private static final String STAX = "STAX"; + + /** Station Y position coordinate. + * @since 12.1 + */ + private static final String STAY = "STAY"; + + /** Station Z position coordinate. + * @since 12.1 + */ + private static final String STAZ = "STAZ"; + + /** Station X velocity coordinate. + * @since 12.1 + */ + private static final String VELX = "VELX"; + + /** Station Y velocity coordinate. + * @since 12.1 + */ + private static final String VELY = "VELY"; + + /** Station Z velocity coordinate. + * @since 12.1 + */ + private static final String VELZ = "VELZ"; + + /** Post-Seismic Deformation amplitude for exponential correction along East direction. + * @since 12.1 + */ + private static final String AEXP_E = "AEXP_E"; + + /** Post-Seismic Deformation relaxation time for exponential correction along East direction. + * @since 12.1 + */ + private static final String TEXP_E = "TEXP_E"; + + /** Post-Seismic Deformation amplitude for logarithmic correction along East direction. + * @since 12.1 + */ + private static final String ALOG_E = "ALOG_E"; + + /** Post-Seismic Deformation relaxation time for logarithmic correction along East direction. + * @since 12.1 + */ + private static final String TLOG_E = "TLOG_E"; + + /** Post-Seismic Deformation amplitude for exponential correction along North direction. + * @since 12.1 + */ + private static final String AEXP_N = "AEXP_N"; + + /** Post-Seismic Deformation relaxation time for exponential correction along North direction. + * @since 12.1 + */ + private static final String TEXP_N = "TEXP_N"; + + /** Post-Seismic Deformation amplitude for logarithmic correction along North direction. + * @since 12.1 + */ + private static final String ALOG_N = "ALOG_N"; + + /** Post-Seismic Deformation relaxation time for logarithmic correction along North direction. + * @since 12.1 + */ + private static final String TLOG_N = "TLOG_N"; + + /** Post-Seismic Deformation amplitude for exponential correction along up direction. + * @since 12.1 + */ + private static final String AEXP_U = "AEXP_U"; + + /** Post-Seismic Deformation relaxation time for exponential correction along up direction. + * @since 12.1 + */ + private static final String TEXP_U = "TEXP_U"; + + /** Post-Seismic Deformation amplitude for logarithmic correction along up direction. + * @since 12.1 + */ + private static final String ALOG_U = "ALOG_U"; + + /** Post-Seismic Deformation relaxation time for logarithmic correction along up direction. + * @since 12.1 + */ + private static final String TLOG_U = "TLOG_U"; + /** Length of day. */ private static final String LOD = "LOD"; @@ -155,7 +246,7 @@ public class SinexLoader implements EopHistoryLoader { private final DcbDescription dcbDescription; /** Data set. */ - private Map eop; + private final Map eop; /** ITRF Version used for EOP parsing. */ private ITRFVersion itrfVersionEop; @@ -364,6 +455,64 @@ private class Parser implements DataLoader { /** Start character of a comment line. */ private static final String COMMENT = "*"; + /** Station x position coordinate. + * @since 12.1 + */ + private double px; + + /** Station y position coordinate. + * @since 12.1 + */ + private double py; + + /** Station z position coordinate. + * @since 12.1 + */ + private double pz; + + /** Station x velocity coordinate. + * @since 12.1 + */ + private double vx; + + /** Station y velocity coordinate. + * @since 12.1 + */ + private double vy; + + /** Station z velocity coordinate. + * @since 12.1 + */ + private double vz; + + /** Correction axis. + * @since 12.1 + */ + private PsdCorrection.Axis axis; + + /** Correction time evolution. + * @since 12.1 + */ + private PsdCorrection.TimeEvolution evolution; + + /** Correction amplitude. + * @since 12.1 + */ + private double amplitude; + + /** Correction relaxation time. + * @since 12.1 + */ + private double relaxationTime; + + /** Simple constructor. + */ + Parser() { + resetPosition(); + resetVelocity(); + resetPsdCorrection(); + } + /** {@inheritDoc} */ @Override public boolean stillAcceptsData() { @@ -386,12 +535,11 @@ public void loadData(final InputStream input, final String name) boolean inEcc = false; boolean inEpoch = false; boolean inEstimate = false; - Vector3D position = Vector3D.ZERO; - Vector3D velocity = Vector3D.ZERO; String startDateString = ""; String endDateString = ""; String creationDateString = ""; + // According to Sinex standard, the epochs are given in UTC scale. // Except for DCB files for which a TIME_SYSTEM key is present. TimeScale scale = scales.getUTC(); @@ -552,47 +700,119 @@ public void loadData(final InputStream input, final String name) if (station != null || EOP_TYPES.contains(dataType)) { // switch on coordinates data switch (dataType) { - case "STAX": + case STAX: // station X coordinate - final double x = parseDouble(line, 47, 22); - position = new Vector3D(x, position.getY(), position.getZ()); - station.setPosition(position); + px = parseDouble(line, 47, 22); + finalizePositionIfComplete(station, currentDate); break; - case "STAY": + case STAY: // station Y coordinate - final double y = parseDouble(line, 47, 22); - position = new Vector3D(position.getX(), y, position.getZ()); - station.setPosition(position); + py = parseDouble(line, 47, 22); + finalizePositionIfComplete(station, currentDate); break; - case "STAZ": + case STAZ: // station Z coordinate - final double z = parseDouble(line, 47, 22); - position = new Vector3D(position.getX(), position.getY(), z); - station.setPosition(position); - // set the reference epoch (identical for all coordinates) - station.setEpoch(currentDate); - // reset position vector - position = Vector3D.ZERO; + pz = parseDouble(line, 47, 22); + finalizePositionIfComplete(station, currentDate); break; - case "VELX": + case VELX: // station X velocity (value is in m/y) - final double vx = parseDouble(line, 47, 22) / Constants.JULIAN_YEAR; - velocity = new Vector3D(vx, velocity.getY(), velocity.getZ()); - station.setVelocity(velocity); + vx = parseDouble(line, 47, 22) / Constants.JULIAN_YEAR; + finalizeVelocityIfComplete(station); break; - case "VELY": + case VELY: // station Y velocity (value is in m/y) - final double vy = parseDouble(line, 47, 22) / Constants.JULIAN_YEAR; - velocity = new Vector3D(velocity.getX(), vy, velocity.getZ()); - station.setVelocity(velocity); + vy = parseDouble(line, 47, 22) / Constants.JULIAN_YEAR; + finalizeVelocityIfComplete(station); break; - case "VELZ": + case VELZ: // station Z velocity (value is in m/y) - final double vz = parseDouble(line, 47, 22) / Constants.JULIAN_YEAR; - velocity = new Vector3D(velocity.getX(), velocity.getY(), vz); - station.setVelocity(velocity); - // reset position vector - velocity = Vector3D.ZERO; + vz = parseDouble(line, 47, 22) / Constants.JULIAN_YEAR; + finalizeVelocityIfComplete(station); + break; + case AEXP_E: + // amplitude of exponential correction for Post-Seismic Deformation + evolution = PsdCorrection.TimeEvolution.EXP; + axis = PsdCorrection.Axis.EAST; + amplitude = parseDouble(line, 47, 22); + finalizePsdCorrectionIfComplete(station, currentDate); + break; + case TEXP_E: + // relaxation toime of exponential correction for Post-Seismic Deformation + evolution = PsdCorrection.TimeEvolution.EXP; + axis = PsdCorrection.Axis.EAST; + relaxationTime = parseDouble(line, 47, 22) * Constants.JULIAN_YEAR; + finalizePsdCorrectionIfComplete(station, currentDate); + break; + case ALOG_E: + // amplitude of exponential correction for Post-Seismic Deformation + evolution = PsdCorrection.TimeEvolution.LOG; + axis = PsdCorrection.Axis.EAST; + amplitude = parseDouble(line, 47, 22); + finalizePsdCorrectionIfComplete(station, currentDate); + break; + case TLOG_E: + // relaxation toime of exponential correction for Post-Seismic Deformation + evolution = PsdCorrection.TimeEvolution.LOG; + axis = PsdCorrection.Axis.EAST; + relaxationTime = parseDouble(line, 47, 22) * Constants.JULIAN_YEAR; + finalizePsdCorrectionIfComplete(station, currentDate); + break; + case AEXP_N: + // amplitude of exponential correction for Post-Seismic Deformation + evolution = PsdCorrection.TimeEvolution.EXP; + axis = PsdCorrection.Axis.NORTH; + amplitude = parseDouble(line, 47, 22); + finalizePsdCorrectionIfComplete(station, currentDate); + break; + case TEXP_N: + // relaxation toime of exponential correction for Post-Seismic Deformation + evolution = PsdCorrection.TimeEvolution.EXP; + axis = PsdCorrection.Axis.NORTH; + relaxationTime = parseDouble(line, 47, 22) * Constants.JULIAN_YEAR; + finalizePsdCorrectionIfComplete(station, currentDate); + break; + case ALOG_N: + // amplitude of exponential correction for Post-Seismic Deformation + evolution = PsdCorrection.TimeEvolution.LOG; + axis = PsdCorrection.Axis.NORTH; + amplitude = parseDouble(line, 47, 22); + finalizePsdCorrectionIfComplete(station, currentDate); + break; + case TLOG_N: + // relaxation toime of exponential correction for Post-Seismic Deformation + evolution = PsdCorrection.TimeEvolution.LOG; + axis = PsdCorrection.Axis.NORTH; + relaxationTime = parseDouble(line, 47, 22) * Constants.JULIAN_YEAR; + finalizePsdCorrectionIfComplete(station, currentDate); + break; + case AEXP_U: + // amplitude of exponential correction for Post-Seismic Deformation + evolution = PsdCorrection.TimeEvolution.EXP; + axis = PsdCorrection.Axis.UP; + amplitude = parseDouble(line, 47, 22); + finalizePsdCorrectionIfComplete(station, currentDate); + break; + case TEXP_U: + // relaxation toime of exponential correction for Post-Seismic Deformation + evolution = PsdCorrection.TimeEvolution.EXP; + axis = PsdCorrection.Axis.UP; + relaxationTime = parseDouble(line, 47, 22) * Constants.JULIAN_YEAR; + finalizePsdCorrectionIfComplete(station, currentDate); + break; + case ALOG_U: + // amplitude of exponential correction for Post-Seismic Deformation + evolution = PsdCorrection.TimeEvolution.LOG; + axis = PsdCorrection.Axis.UP; + amplitude = parseDouble(line, 47, 22); + finalizePsdCorrectionIfComplete(station, currentDate); + break; + case TLOG_U: + // relaxation toime of exponential correction for Post-Seismic Deformation + evolution = PsdCorrection.TimeEvolution.LOG; + axis = PsdCorrection.Axis.UP; + relaxationTime = parseDouble(line, 47, 22) * Constants.JULIAN_YEAR; + finalizePsdCorrectionIfComplete(station, currentDate); break; case XPO: // X polar motion @@ -694,23 +914,21 @@ public void loadData(final InputStream input, final String name) final double valueDcb = unitDcb.toSI(Double.parseDouble(parseString(line, 70, 21))); // Verifying if present - if (siteCode.equals("")) { - final String id = satellitePrn; - DcbSatellite dcbSatellite = getDcbSatellite(id); + if (siteCode.isEmpty()) { + DcbSatellite dcbSatellite = getDcbSatellite(satellitePrn); if (dcbSatellite == null) { - dcbSatellite = new DcbSatellite(id); + dcbSatellite = new DcbSatellite(satellitePrn); dcbSatellite.setDescription(dcbDescription); } final Dcb dcb = dcbSatellite.getDcbData(); // Add the data to the DCB object. dcb.addDcbLine(obs1, obs2, beginDate, finalDate, valueDcb); // Adding the object to the HashMap if not present. - addDcbSatellite(dcbSatellite, id); + addDcbSatellite(dcbSatellite, satellitePrn); } else { - final String id = siteCode; - DcbStation dcbStation = getDcbStation(id); + DcbStation dcbStation = getDcbStation(siteCode); if (dcbStation == null) { - dcbStation = new DcbStation(id); + dcbStation = new DcbStation(siteCode); dcbStation.setDescription(dcbDescription); } final SatelliteSystem satSystem = SatelliteSystem.parseSatelliteSystem(satellitePrn); @@ -721,7 +939,7 @@ public void loadData(final InputStream input, final String name) } dcbStation.getDcbData(satSystem).addDcbLine(obs1, obs2, beginDate, finalDate, valueDcb); // Adding the object to the HashMap if not present. - addDcbStation(dcbStation, id); + addDcbStation(dcbStation, siteCode); } } else { @@ -774,6 +992,74 @@ private double parseDoubleWithUnit(final String line, final int startUnit, final return unit.toSI(parseDouble(line, startDouble, lengthDouble)); } + /** Finalize station position if complete. + * @param station station + * @param epoch coordinates epoch + * @since 12.1 + */ + private void finalizePositionIfComplete(final Station station, final AbsoluteDate epoch) { + if (!Double.isNaN(px + py + pz)) { + // all coordinates are available, position is complete + station.setPosition(new Vector3D(px, py, pz)); + station.setEpoch(epoch); + resetPosition(); + } + } + + /** Reset position. + * @since 12.1 + */ + private void resetPosition() { + px = Double.NaN; + py = Double.NaN; + pz = Double.NaN; + } + + /** Finalize station velocity if complete. + * @param station station + * @since 12.1 + */ + private void finalizeVelocityIfComplete(final Station station) { + if (!Double.isNaN(vx + vy + vz)) { + // all coordinates are available, velocity is complete + station.setVelocity(new Vector3D(vx, vy, vz)); + resetVelocity(); + } + } + + /** Reset velocity. + * @since 12.1 + */ + private void resetVelocity() { + vx = Double.NaN; + vy = Double.NaN; + vz = Double.NaN; + } + + /** Finalize a Post-Seismic Deformation correction model if complete. + * @param station station + * @param epoch coordinates epoch + * @since 12.1 + */ + private void finalizePsdCorrectionIfComplete(final Station station, final AbsoluteDate epoch) { + if (!Double.isNaN(amplitude + relaxationTime)) { + // both amplitude and relaxation time are available, correction is complete + final PsdCorrection correction = new PsdCorrection(axis, evolution, epoch, amplitude, relaxationTime); + station.addPsdCorrectionValidAfter(correction, epoch); + resetPsdCorrection(); + } + } + + /** Reset Post-Seismic Deformation correction model. + * @since 12.1 + */ + private void resetPsdCorrection() { + axis = null; + evolution = null; + amplitude = Double.NaN; + relaxationTime = Double.NaN; + } + } /** @@ -825,9 +1111,7 @@ private AbsoluteDate stringEpochToAbsoluteDate(final String stringDate, final bo */ private void addStation(final Station station) { // Check if the station already exists - if (stations.get(station.getSiteCode()) == null) { - stations.put(station.getSiteCode(), station); - } + stations.putIfAbsent(station.getSiteCode(), station); } /** @@ -838,9 +1122,7 @@ private void addStation(final Station station) { */ private void addDcbStation(final DcbStation dcb, final String siteCode) { // Check if the DCB for the current station already exists - if (dcbStations.get(siteCode) == null) { - dcbStations.put(siteCode, dcb); - } + dcbStations.putIfAbsent(siteCode, dcb); } /** @@ -850,9 +1132,7 @@ private void addDcbStation(final DcbStation dcb, final String siteCode) { * @since 12.0 */ private void addDcbSatellite(final DcbSatellite dcb, final String prn) { - if (dcbSatellites.get(prn) == null) { - dcbSatellites.put(prn, dcb); - } + dcbSatellites.putIfAbsent(prn, dcb); } /** diff --git a/src/main/java/org/orekit/files/sinex/Station.java b/src/main/java/org/orekit/files/sinex/Station.java index 6052bdb500..897ab60e69 100644 --- a/src/main/java/org/orekit/files/sinex/Station.java +++ b/src/main/java/org/orekit/files/sinex/Station.java @@ -16,12 +16,15 @@ */ package org.orekit.files.sinex; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; +import org.orekit.models.earth.displacement.PsdCorrection; import org.orekit.time.AbsoluteDate; import org.orekit.utils.TimeSpanMap; @@ -55,12 +58,17 @@ public class Station { private ReferenceSystem eccRefSystem; /** TimeSpanMap of site antenna eccentricities. */ - private TimeSpanMap eccentricitiesTimeSpanMap; + private final TimeSpanMap eccentricitiesTimeSpanMap; /** Antenna type. * @since 12.0 */ - private TimeSpanMap antennaTypesMap; + private final TimeSpanMap antennaTypesMap; + + /** Post-Seismic Deformation. + * @since 12.0 + */ + private final TimeSpanMap> psdMap; /** Station position. */ private Vector3D position; @@ -77,6 +85,7 @@ public class Station { public Station() { this.eccentricitiesTimeSpanMap = new TimeSpanMap<>(null); this.antennaTypesMap = new TimeSpanMap<>(null); + this.psdMap = new TimeSpanMap<>(null); this.position = Vector3D.ZERO; this.velocity = Vector3D.ZERO; } @@ -216,6 +225,41 @@ public void addStationEccentricitiesValidAfter(final Vector3D entry, final Absol eccentricitiesTimeSpanMap.addValidAfter(entry, earliestValidityDate, false); } + /** Get the TimeSpanMap of Post-Seismic Deformation. + * @return the TimeSpanMap of Post-Seismic Deformation + * @since 12.1 + */ + public TimeSpanMap> getPsdTimeSpanMap() { + return psdMap; + } + + /** Add a Post-Seismic Deformation entry valid after a limit date.
    + * Using {@code addPsdCorrectionValidAfter(entry, t)} will make {@code entry} + * valid in [t, +∞[ (note the closed bracket). + * @param entry Post-Seismic Deformation entry + * @param earliestValidityDate date after which the entry is valid + * (must be different from all dates already used for transitions) + * @since 12.1 + */ + public void addPsdCorrectionValidAfter(final PsdCorrection entry, final AbsoluteDate earliestValidityDate) { + + // get the list of corrections active just after earthquake date + List corrections = psdMap.get(earliestValidityDate.shiftedBy(1.0e-3)); + + if (corrections == null || + earliestValidityDate.durationFrom(corrections.get(0).getEarthquakeDate()) > 1.0e-3) { + // either this is the first earthquake we consider or + // this earthquake is after another one already considered + // we need to create a new list of corrections for this new earthquake + corrections = new ArrayList<>(); + psdMap.addValidAfter(corrections, earliestValidityDate, false); + } + + // add the entry to the current list + corrections.add(entry); + + } + /** * Get the antenna type for the given epoch. * If there is no antenna types for the given epoch, an diff --git a/src/main/java/org/orekit/files/sp3/NsgfV00Filter.java b/src/main/java/org/orekit/files/sp3/NsgfV00Filter.java new file mode 100644 index 0000000000..2a1c7dddba --- /dev/null +++ b/src/main/java/org/orekit/files/sp3/NsgfV00Filter.java @@ -0,0 +1,101 @@ +/* Copyright 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.files.sp3; + +import java.io.IOException; +import java.util.function.Function; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.orekit.data.DataFilter; +import org.orekit.data.DataSource; +import org.orekit.data.LineOrientedFilteringReader; + +/** Filter for some non-official files from CDDIS. + *

    + * Some files produced by UKRI/NERC/British Geological Survey Space Geodesy Facility (SGF) + * claim to be SP3c but are really SP3d since they have more than 4 comments lines. This + * filter can be used to parse them. + *

    + * @see SP3 + * precise orbit file is not compliant with SP3 format c (extra comment line) + * @since 12.1 + */ +public class NsgfV00Filter implements DataFilter { + + /** Default regular expression for NSGF V00 files. */ + public static final String DEFAULT_V00_PATTERN = ".*nsgf\\.orb\\.[^.]+\\.v00\\.sp3$"; + + /** Pattern matching file names to which filtering should be applied. */ + private final Pattern pattern; + + /** Renaming function. */ + private final Function renaming; + + /** Simple constructor. + * @param nameRegexp regular expression matching file names to which filtering should be applied + * @param renaming function to apply for renaming files (and avoid the filter to be applied in infinite recursion) + */ + public NsgfV00Filter(final String nameRegexp, final Function renaming) { + this.pattern = Pattern.compile(nameRegexp); + this.renaming = renaming; + } + + /** Simple constructor. + *

    + * This uses {@link #DEFAULT_V00_PATTERN} as the regular expression matching files + * that must be filtered, and replaces "v00" by "v70" to generate the filtered name. + *

    + */ + public NsgfV00Filter() { + this(DEFAULT_V00_PATTERN, s -> s.replace("v00", "v70")); + } + + /** {@inheritDoc} */ + @Override + public DataSource filter(final DataSource original) throws IOException { + final Matcher matcher = pattern.matcher(original.getName()); + if (matcher.matches()) { + // this is a v00 file from NSGF + // we need to parse it as an SP3d file even if it claims being an SP3c file + final String oName = original.getName(); + final String fName = renaming.apply(oName); + return new DataSource(fName, + () -> new LineOrientedFilteringReader(oName, original.getOpener().openReaderOnce()) { + + /** {@inheritDoc} */ + @Override + protected CharSequence filterLine(final int lineNumber, final String originalLine) { + if (lineNumber == 1 && originalLine.startsWith("#c")) { + // the 'c' format marker appears in the first header line + // we replace it by a 'd' format marker + return "#d" + originalLine.substring(2); + } else { + // don't filter any other lines + return originalLine; + } + } + + }); + } else { + // this is a regular file, no need to filter it + return original; + } + } + +} + diff --git a/src/main/java/org/orekit/files/sp3/SP3.java b/src/main/java/org/orekit/files/sp3/SP3.java index 767aa99987..fb73334178 100644 --- a/src/main/java/org/orekit/files/sp3/SP3.java +++ b/src/main/java/org/orekit/files/sp3/SP3.java @@ -26,14 +26,18 @@ import java.util.SortedSet; import java.util.TreeSet; +import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; import org.hipparchus.util.Precision; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; import org.orekit.files.general.EphemerisFile; import org.orekit.frames.Frame; +import org.orekit.frames.Transform; +import org.orekit.gnss.IGSUtils; import org.orekit.time.AbsoluteDate; import org.orekit.time.ChronologicalComparator; +import org.orekit.utils.PVCoordinates; /** * Represents a parsed SP3 orbit file. @@ -57,7 +61,7 @@ public class SP3 implements EphemerisFile { private final Frame frame; /** A map containing satellite information. */ - private Map satellites; + private final Map satellites; /** * Create a new SP3 file object. @@ -67,7 +71,21 @@ public class SP3 implements EphemerisFile { * @param frame reference frame */ public SP3(final double mu, final int interpolationSamples, final Frame frame) { - this.header = new SP3Header(); + this(new SP3Header(), mu, interpolationSamples, frame); + } + + /** + * Create a new SP3 file object. + * + * @param header header + * @param mu is the standard gravitational parameter in m³ / s². + * @param interpolationSamples number of samples to use in interpolation. + * @param frame reference frame + * @since 12.1 + */ + public SP3(final SP3Header header, + final double mu, final int interpolationSamples, final Frame frame) { + this.header = header; this.mu = mu; this.interpolationSamples = interpolationSamples; this.frame = frame; @@ -184,6 +202,7 @@ public static SP3 splice(final Collection sp3) { // prepare spliced file final SP3 first = sorted.first(); final SP3 spliced = new SP3(first.mu, first.interpolationSamples, first.frame); + spliced.header.setVersion(first.header.getVersion()); spliced.header.setFilter(first.header.getFilter()); spliced.header.setType(first.header.getType()); spliced.header.setTimeSystem(first.header.getTimeSystem()); @@ -199,6 +218,7 @@ public static SP3 splice(final Collection sp3) { spliced.header.setAgency(first.header.getAgency()); spliced.header.setPosVelBase(first.header.getPosVelBase()); spliced.header.setClockBase(first.header.getClockBase()); + first.header.getComments().forEach(spliced.header::addComment); // identify the satellites that are present in all files final List commonSats = new ArrayList<>(first.header.getSatIds()); @@ -277,6 +297,91 @@ public static SP3 splice(final Collection sp3) { } + /** Change the frame of an SP3 file. + * @param original original SP3 file + * @param newFrame frame to use for the changed SP3 file + * @return changed SP3 file + * @since 12.1 + */ + public static SP3 changeFrame(final SP3 original, final Frame newFrame) { + + // copy header + final SP3Header header = new SP3Header(); + final SP3Header originalHeader = original.header; + header.setVersion(originalHeader.getVersion()); + header.setFilter(originalHeader.getFilter()); + header.setType(originalHeader.getType()); + header.setTimeSystem(originalHeader.getTimeSystem()); + header.setDataUsed(originalHeader.getDataUsed()); + header.setEpoch(originalHeader.getEpoch()); + header.setGpsWeek(originalHeader.getGpsWeek()); + header.setSecondsOfWeek(originalHeader.getSecondsOfWeek()); + header.setModifiedJulianDay(originalHeader.getModifiedJulianDay()); + header.setDayFraction(originalHeader.getDayFraction()); + header.setEpochInterval(originalHeader.getEpochInterval()); + header.setOrbitTypeKey(originalHeader.getOrbitTypeKey()); + header.setAgency(originalHeader.getAgency()); + header.setPosVelBase(originalHeader.getPosVelBase()); + header.setClockBase(originalHeader.getClockBase()); + header.setNumberOfEpochs(originalHeader.getNumberOfEpochs()); + + originalHeader.getComments().forEach(header::addComment); + + // change the frame in the header + header.setCoordinateSystem(IGSUtils.frameName(newFrame)); + + // prepare file + final SP3 changed = new SP3(header, + original.mu, + original.interpolationSamples, + newFrame); + + // first loop to add satellite ids only + final List ids = originalHeader.getSatIds(); + ids.forEach(changed::addSatellite); + + // now that the header knows the number of satellites, + // it can lazily allocate the accuracies array, + // so we can perform a second loop to set individual accuracies + for (int i = 0; i < ids.size(); ++i) { + header.setAccuracy(i, originalHeader.getAccuracy(ids.get(i))); + } + + // convert data to new frame + for (final Map.Entry entry : original.satellites.entrySet()) { + final SP3Ephemeris originalEphemeris = original.getEphemeris(entry.getKey()); + final SP3Ephemeris changedEphemeris = changed.getEphemeris(entry.getKey()); + for (final SP3Segment segment : originalEphemeris.getSegments()) { + for (SP3Coordinate c : segment.getCoordinates()) { + final Transform t = originalEphemeris.getFrame().getTransformTo(newFrame, c.getDate()); + final PVCoordinates newPV = t.transformPVCoordinates(c); + final Vector3D newP = newPV.getPosition(); + final Vector3D newPA = c.getPositionAccuracy() == null ? + null : + t.transformVector(c.getPositionAccuracy()); + final Vector3D newV = c.getVelocity() == null ? Vector3D.ZERO : newPV.getVelocity(); + final Vector3D newVA = c.getVelocityAccuracy() == null ? + null : + t.transformVector(c.getVelocityAccuracy()); + final SP3Coordinate newC = new SP3Coordinate(c.getDate(), + newP, newPA, newV, newVA, + c.getClockCorrection(), + c.getClockAccuracy(), + c.getClockRateChange(), + c.getClockRateAccuracy(), + c.hasClockEvent(), + c.hasClockPrediction(), + c.hasOrbitManeuverEvent(), + c.hasOrbitPrediction()); + changedEphemeris.addCoordinate(newC, originalHeader.getEpochInterval()); + } + } + } + + return changed; + + } + /** Check if instance can be spliced after previous one. * @param previous SP3 file (should already be sorted to be before current instance), can be null * @return true if last entry of previous file should be dropped as first entry of current file diff --git a/src/main/java/org/orekit/files/sp3/SP3Ephemeris.java b/src/main/java/org/orekit/files/sp3/SP3Ephemeris.java index b5480444bc..c79b2090ec 100644 --- a/src/main/java/org/orekit/files/sp3/SP3Ephemeris.java +++ b/src/main/java/org/orekit/files/sp3/SP3Ephemeris.java @@ -23,7 +23,10 @@ import org.orekit.files.general.EphemerisFile; import org.orekit.frames.Frame; import org.orekit.time.AbsoluteDate; +import org.orekit.time.AggregatedClockModel; +import org.orekit.time.ClockModel; import org.orekit.utils.CartesianDerivativesFilter; +import org.orekit.utils.TimeSpanMap; /** Single satellite ephemeris from an {@link SP3 SP3} file. * @author Luc Maisonobe @@ -135,4 +138,24 @@ public void addCoordinate(final SP3Coordinate coord, final double maxGap) { segment.addCoordinate(coord); } + /** Extract the clock model. + *

    + * There are always 2n+1 {@link AggregatedClockModel#getModels()} + * underlying clock models when there are n {@link #getSegments() segments} + * in the ephemeris. This happens because there are {@link TimeSpanMap.Span + * spans} with {@code null} {@link TimeSpanMap.Span#getData()} before the + * first segment, between all regular segments and after last segment. + *

    + * @return extracted clock model + * @since 12.1 + */ + public AggregatedClockModel extractClockModel() { + // set up the map for all segments clock models + final TimeSpanMap models = new TimeSpanMap<>(null); + segments.forEach(segment -> models.addValidBetween(segment.extractClockModel(), + segment.getStart(), + segment.getStop())); + return new AggregatedClockModel(models); + } + } diff --git a/src/main/java/org/orekit/files/sp3/SP3Parser.java b/src/main/java/org/orekit/files/sp3/SP3Parser.java index 0bad1d5fdb..f321b0df65 100644 --- a/src/main/java/org/orekit/files/sp3/SP3Parser.java +++ b/src/main/java/org/orekit/files/sp3/SP3Parser.java @@ -39,6 +39,8 @@ import org.orekit.errors.OrekitMessages; import org.orekit.files.general.EphemerisFileParser; import org.orekit.frames.Frame; +import org.orekit.frames.ITRFVersion; +import org.orekit.gnss.IGSUtils; import org.orekit.gnss.TimeSystem; import org.orekit.time.AbsoluteDate; import org.orekit.time.DateComponents; @@ -64,7 +66,10 @@ */ public class SP3Parser implements EphemerisFileParser { - /** String representation of the center of ephemeris coordinate system. **/ + /** String representation of the center of ephemeris coordinate system. + * @deprecated as of 12.1 not used anymore + */ + @Deprecated public static final String SP3_FRAME_CENTER_STRING = "EARTH"; /** Spaces delimiters. */ @@ -88,10 +93,11 @@ public class SP3Parser implements EphemerisFileParser { *

    This constructor uses the {@link DataContext#getDefault() default data context}. * * @see #SP3Parser(double, int, Function) + * @see IGSUtils#guessFrame(String) */ @DefaultDataContext public SP3Parser() { - this(Constants.EIGEN5C_EARTH_MU, 7, SP3Parser::guessFrame); + this(Constants.EIGEN5C_EARTH_MU, 7, IGSUtils::guessFrame); } /** @@ -108,6 +114,7 @@ public SP3Parser() { * coordinate system string. The coordinate system can be * any 5 character string e.g. ITR92, IGb08. * @see #SP3Parser(double, int, Function, TimeScales) + * @see IGSUtils#guessFrame(String) */ @DefaultDataContext public SP3Parser(final double mu, @@ -143,16 +150,25 @@ public SP3Parser(final double mu, /** * Default string to {@link Frame} conversion for {@link #SP3Parser()}. * - *

    This method uses the {@link DataContext#getDefault() default data context}. + *

    + * This method uses the {@link DataContext#getDefault() default data context}. + * If the frame names has a form like IGS##, or ITR##, or SLR##, where ## + * is a two digits number, then this number will be used to build the + * appropriate {@link ITRFVersion}. Otherwise (for example if name is + * UNDEF or WGS84), then a default {@link + * org.orekit.frames.Frames#getITRF(IERSConventions, boolean) ITRF} + * will be created. + *

    * * @param name of the frame. * @return ITRF based on 2010 conventions, - * with tidal effects considered during EOP interpolation. + * with tidal effects considered during EOP interpolation + * @deprecated as of 12.1, replaced by {@link IGSUtils#guessFrame(String)} */ + @Deprecated @DefaultDataContext - private static Frame guessFrame(final String name) { - return DataContext.getDefault().getFrames() - .getITRF(IERSConventions.IERS_2010, false); + public static Frame guessFrame(final String name) { + return IGSUtils.guessFrame(name); } @Override @@ -166,7 +182,7 @@ public SP3 parse(final DataSource source) { } // initialize internal data structures - final ParseInfo pi = new ParseInfo(source.getName()); + final ParseInfo pi = new ParseInfo(source.getName(), this); int lineNumber = 0; Iterable candidateParsers = Collections.singleton(LineParser.HEADER_VERSION); @@ -210,15 +226,15 @@ public SP3 parse(final DataSource source) { *

    Note: The class intentionally does not provide accessor * methods, as it is only used internally for parsing a SP3 file.

    */ - private class ParseInfo { + private static class ParseInfo { /** File name. * @since 12.0 */ private final String fileName; - /** Set of time scales for parsing dates. */ - private final TimeScales timeScales; + /** Englobing parser. */ + private final SP3Parser parser; /** The corresponding SP3File object. */ private SP3 file; @@ -282,17 +298,18 @@ private class ParseInfo { /** Create a new {@link ParseInfo} object. * @param fileName file name + * @param parser englobing parser */ - protected ParseInfo(final String fileName) { + protected ParseInfo(final String fileName, + final SP3Parser parser) { this.fileName = fileName; - this.timeScales = SP3Parser.this.timeScales; - file = new SP3(mu, interpolationSamples, frameBuilder.apply(SP3_FRAME_CENTER_STRING)); + this.parser = parser; latestEpoch = null; latestPosition = null; latestClock = 0.0; hasVelocityEntries = false; epoch = DateTimeComponents.JULIAN_EPOCH; - timeScale = timeScales.getGPS(); + timeScale = parser.timeScales.getGPS(); maxSatellites = 0; nbAccuracies = 0; done = false; @@ -314,12 +331,13 @@ public void parse(final String line, final ParseInfo pi) { scanner.skip("#"); final String v = scanner.next(); - pi.file.getHeader().setVersion(v.substring(0, 1).toLowerCase().charAt(0)); + final SP3Header header = new SP3Header(); + header.setVersion(v.substring(0, 1).toLowerCase().charAt(0)); pi.hasVelocityEntries = "V".equals(v.substring(1, 2)); - pi.file.getHeader().setFilter(pi.hasVelocityEntries ? - CartesianDerivativesFilter.USE_PV : - CartesianDerivativesFilter.USE_P); + header.setFilter(pi.hasVelocityEntries ? + CartesianDerivativesFilter.USE_PV : + CartesianDerivativesFilter.USE_P); final int year = Integer.parseInt(v.substring(2)); final int month = scanner.nextInt(); @@ -332,19 +350,21 @@ public void parse(final String line, final ParseInfo pi) { hour, minute, second); final int numEpochs = scanner.nextInt(); - pi.file.getHeader().setNumberOfEpochs(numEpochs); + header.setNumberOfEpochs(numEpochs); // data used indicator final String fullSpec = scanner.next(); final List dataUsed = new ArrayList<>(); for (final String specifier : fullSpec.split("\\+")) { - dataUsed.add(DataUsed.parse(specifier, pi.fileName, pi.file.getHeader().getVersion())); + dataUsed.add(DataUsed.parse(specifier, pi.fileName, header.getVersion())); } - pi.file.getHeader().setDataUsed(dataUsed); + header.setDataUsed(dataUsed); - pi.file.getHeader().setCoordinateSystem(scanner.next()); - pi.file.getHeader().setOrbitTypeKey(scanner.next()); - pi.file.getHeader().setAgency(scanner.next()); + header.setCoordinateSystem(scanner.next()); + header.setOrbitTypeKey(scanner.next()); + header.setAgency(scanner.hasNext() ? scanner.next() : ""); + pi.file = new SP3(header, pi.parser.mu, pi.parser.interpolationSamples, + pi.parser.frameBuilder.apply(header.getCoordinateSystem())); } } @@ -405,7 +425,7 @@ public void parse(final String line, final ParseInfo pi) { int startIdx = 9; while (count++ < pi.maxSatellites && (startIdx + 3) <= lineLength) { final String satId = line.substring(startIdx, startIdx + 3).trim(); - if (satId.length() > 0) { + if (!satId.isEmpty()) { pi.file.addSatellite(satId); } startIdx += 3; @@ -430,7 +450,7 @@ public void parse(final String line, final ParseInfo pi) { int startIdx = 9; while (pi.nbAccuracies < pi.maxSatellites && (startIdx + 3) <= lineLength) { final String sub = line.substring(startIdx, startIdx + 3).trim(); - if (sub.length() > 0) { + if (!sub.isEmpty()) { final int exponent = Integer.parseInt(sub); // the accuracy is calculated as 2**exp (in mm) pi.file.getHeader().setAccuracy(pi.nbAccuracies++, @@ -470,7 +490,7 @@ public void parse(final String line, final ParseInfo pi) { ts = TimeSystem.parseTimeSystem(tsStr); } pi.file.getHeader().setTimeSystem(ts); - pi.timeScale = ts.getTimeScale(pi.timeScales); + pi.timeScale = ts.getTimeScale(pi.parser.timeScales); // now we know the time scale used, we can set the file epoch pi.file.getHeader().setEpoch(new AbsoluteDate(pi.epoch, pi.timeScale)); @@ -706,9 +726,9 @@ public void parse(final String line, final ParseInfo pi) { if (pi.latestPosition.getNorm() > 0) { if (line.length() < 69 || - line.substring(61, 63).trim().length() == 0 || - line.substring(64, 66).trim().length() == 0 || - line.substring(67, 69).trim().length() == 0) { + line.substring(61, 63).trim().isEmpty() || + line.substring(64, 66).trim().isEmpty() || + line.substring(67, 69).trim().isEmpty()) { pi.latestPositionAccuracy = null; } else { pi.latestPositionAccuracy = new Vector3D(SP3Utils.siAccuracy(SP3Utils.POSITION_ACCURACY_UNIT, @@ -722,7 +742,7 @@ public void parse(final String line, final ParseInfo pi) { Integer.parseInt(line.substring(67, 69).trim()))); } - if (line.length() < 73 || line.substring(70, 73).trim().length() == 0) { + if (line.length() < 73 || line.substring(70, 73).trim().isEmpty()) { pi.latestClockAccuracy = Double.NaN; } else { pi.latestClockAccuracy = SP3Utils.siAccuracy(SP3Utils.CLOCK_ACCURACY_UNIT, @@ -730,10 +750,10 @@ public void parse(final String line, final ParseInfo pi) { Integer.parseInt(line.substring(70, 73).trim())); } - pi.latestClockEvent = line.length() < 75 ? false : line.substring(74, 75).equals("E"); - pi.latestClockPrediction = line.length() < 76 ? false : line.substring(75, 76).equals("P"); - pi.latestOrbitManeuverEvent = line.length() < 79 ? false : line.substring(78, 79).equals("M"); - pi.latestOrbitPrediction = line.length() < 80 ? false : line.substring(79, 80).equals("P"); + pi.latestClockEvent = line.length() >= 75 && line.charAt(74) == 'E'; + pi.latestClockPrediction = line.length() >= 76 && line.charAt(75) == 'P'; + pi.latestOrbitManeuverEvent = line.length() >= 79 && line.charAt(78) == 'M'; + pi.latestOrbitPrediction = line.length() >= 80 && line.charAt(79) == 'P'; if (!pi.hasVelocityEntries) { final SP3Coordinate coord = @@ -799,9 +819,9 @@ public void parse(final String line, final ParseInfo pi) { final Vector3D velocityAccuracy; if (line.length() < 69 || - line.substring(61, 63).trim().length() == 0 || - line.substring(64, 66).trim().length() == 0 || - line.substring(67, 69).trim().length() == 0) { + line.substring(61, 63).trim().isEmpty() || + line.substring(64, 66).trim().isEmpty() || + line.substring(67, 69).trim().isEmpty()) { velocityAccuracy = null; } else { velocityAccuracy = new Vector3D(SP3Utils.siAccuracy(SP3Utils.VELOCITY_ACCURACY_UNIT, @@ -816,7 +836,7 @@ public void parse(final String line, final ParseInfo pi) { } final double clockRateAccuracy; - if (line.length() < 73 || line.substring(70, 73).trim().length() == 0) { + if (line.length() < 73 || line.substring(70, 73).trim().isEmpty()) { clockRateAccuracy = Double.NaN; } else { clockRateAccuracy = SP3Utils.siAccuracy(SP3Utils.CLOCK_RATE_ACCURACY_UNIT, diff --git a/src/main/java/org/orekit/files/sp3/SP3Segment.java b/src/main/java/org/orekit/files/sp3/SP3Segment.java index cfd73070e8..2a724d13eb 100644 --- a/src/main/java/org/orekit/files/sp3/SP3Segment.java +++ b/src/main/java/org/orekit/files/sp3/SP3Segment.java @@ -20,12 +20,20 @@ import java.util.Collections; import java.util.List; +import org.hipparchus.analysis.interpolation.HermiteInterpolator; import org.orekit.attitudes.AttitudeProvider; +import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.files.general.EphemerisFile; +import org.orekit.files.general.EphemerisSegmentPropagator; import org.orekit.frames.Frame; import org.orekit.propagation.BoundedPropagator; +import org.orekit.propagation.SpacecraftState; import org.orekit.time.AbsoluteDate; +import org.orekit.time.ClockModel; +import org.orekit.time.ClockOffset; +import org.orekit.time.SampledClockModel; import org.orekit.utils.CartesianDerivativesFilter; +import org.orekit.utils.SortedListTrimmer; /** One segment of an {@link SP3Ephemeris}. * @author Thomas Neidhart @@ -66,6 +74,21 @@ public SP3Segment(final double mu, final Frame frame, this.coordinates = new ArrayList<>(); } + /** Extract the clock model. + * @return extracted clock model + * @since 12.1 + */ + public ClockModel extractClockModel() { + final List sample = new ArrayList<>(coordinates.size()); + 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)); + }); + return new SampledClockModel(sample, interpolationSamples); + } + /** {@inheritDoc} */ @Override public double getMu() { @@ -118,13 +141,64 @@ public void addCoordinate(final SP3Coordinate coord) { /** {@inheritDoc} */ @Override public BoundedPropagator getPropagator() { - return EphemerisFile.EphemerisSegment.super.getPropagator(); + return new PropagatorWithClock(new FrameAlignedProvider(getInertialFrame())); } /** {@inheritDoc} */ @Override public BoundedPropagator getPropagator(final AttitudeProvider attitudeProvider) { - return EphemerisFile.EphemerisSegment.super.getPropagator(attitudeProvider); + return new PropagatorWithClock(attitudeProvider); + } + + /** Propagator including clock. + * @since 12.1 + */ + private class PropagatorWithClock extends EphemerisSegmentPropagator { + + /** Trimmer for coordinates list. */ + private final SortedListTrimmer trimmer; + + /** Simple constructor. + * @param attitudeProvider attitude porovider + */ + PropagatorWithClock(final AttitudeProvider attitudeProvider) { + super(SP3Segment.this, attitudeProvider); + this.trimmer = new SortedListTrimmer(getInterpolationSamples()); + } + + /** {@inheritDoc} */ + @Override + protected SpacecraftState updateAdditionalStates(final SpacecraftState original) { + + final HermiteInterpolator interpolator = new HermiteInterpolator(); + + // Fill interpolator with sample + trimmer. + getNeighborsSubList(original.getDate(), coordinates). + forEach(c -> { + final double deltaT = c.getDate().durationFrom(original.getDate()); + if (filter.getMaxOrder() < 1) { + // we use only clock offset + interpolator.addSamplePoint(deltaT, + new double[] { c.getClockCorrection() }); + } else { + // we use both clock offset and clock rate + interpolator.addSamplePoint(deltaT, + new double[] { c.getClockCorrection() }, + new double[] { c.getClockRateChange() }); + } + }); + + // perform interpolation (we get derivatives even if we used only clock offset) + final double[][] derivatives = interpolator.derivatives(0.0, 1); + + // add the clock offset and its first derivative + return super.updateAdditionalStates(original). + addAdditionalState(SP3Utils.CLOCK_ADDITIONAL_STATE, derivatives[0]). + addAdditionalStateDerivative(SP3Utils.CLOCK_ADDITIONAL_STATE, derivatives[1]); + + } + } } diff --git a/src/main/java/org/orekit/files/sp3/SP3Utils.java b/src/main/java/org/orekit/files/sp3/SP3Utils.java index f77d735824..608c4c6969 100644 --- a/src/main/java/org/orekit/files/sp3/SP3Utils.java +++ b/src/main/java/org/orekit/files/sp3/SP3Utils.java @@ -46,6 +46,11 @@ public class SP3Utils { /** Velocity accuracy unit. */ public static final Unit VELOCITY_ACCURACY_UNIT = Unit.parse("mm/s").scale("10⁻⁴mm/s", 1.0e-4); + /** Additional state name for clock. + * @since 12.1 + */ + public static final String CLOCK_ADDITIONAL_STATE = "clock"; + /** Clock unit. */ public static final Unit CLOCK_UNIT = Unit.parse("µs"); diff --git a/src/main/java/org/orekit/forces/ForceModel.java b/src/main/java/org/orekit/forces/ForceModel.java index 377e3c89ba..3afdeace84 100644 --- a/src/main/java/org/orekit/forces/ForceModel.java +++ b/src/main/java/org/orekit/forces/ForceModel.java @@ -130,7 +130,7 @@ default > void addContribution(FieldSpacecraft adder.addNonKeplerianAcceleration(acceleration(s, getParameters(s.getDate().getField(), s.getDate()))); } - /** Check if force models depends on position only. + /** Check if force model depends on position only at a given, fixed date. * @return true if force model depends on position only, false * if it depends on velocity, either directly or due to a dependency * on attitude @@ -138,6 +138,16 @@ default > void addContribution(FieldSpacecraft */ boolean dependsOnPositionOnly(); + /** Check if force model depends on attitude's rotation rate or acceleration at a given, fixed date. + * If false, it essentially means that at most the attitude's rotation is used when computing the acceleration vector. + * The default implementation returns false as common forces do not. + * @return true if force model depends on attitude derivatives + * @since 12.1 + */ + default boolean dependsOnAttitudeRate() { + return false; + } + /** Compute acceleration. * @param s current state information: date, kinematics, attitude * @param parameters values of the force model parameters at state date, diff --git a/src/main/java/org/orekit/forces/drag/AbstractDragForceModel.java b/src/main/java/org/orekit/forces/drag/AbstractDragForceModel.java index 2668a9023d..30486724b7 100644 --- a/src/main/java/org/orekit/forces/drag/AbstractDragForceModel.java +++ b/src/main/java/org/orekit/forces/drag/AbstractDragForceModel.java @@ -31,10 +31,11 @@ import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.FieldPVCoordinates; +import java.util.Arrays; + /** * Base class for drag force models. * @see DragForce - * @see TimeSpanDragForce * @author Bryan Cazabonne * @since 10.2 */ @@ -44,11 +45,36 @@ public abstract class AbstractDragForceModel implements ForceModel { private final Atmosphere atmosphere; /** - * Constructor. + * Flag to use (first-order) finite differences instead of automatic differentiation when computing density derivatives w.r.t. position. + */ + private final boolean useFiniteDifferencesOnDensityWrtPosition; + + /** + * Constructor with default value for finite differences flag. * @param atmosphere atmospheric model */ protected AbstractDragForceModel(final Atmosphere atmosphere) { + this(atmosphere, true); + } + + /** + * Constructor. + * @param atmosphere atmospheric model + * @param useFiniteDifferencesOnDensityWrtPosition flag to use finite differences to compute density derivatives w.r.t. + * position (is less accurate but can be faster depending on model) + * @since 12.1 + */ + protected AbstractDragForceModel(final Atmosphere atmosphere, final boolean useFiniteDifferencesOnDensityWrtPosition) { this.atmosphere = atmosphere; + this.useFiniteDifferencesOnDensityWrtPosition = useFiniteDifferencesOnDensityWrtPosition; + } + + /** Get the atmospheric model. + * @return atmosphere model + * @since 12.1 + */ + public Atmosphere getAtmosphere() { + return atmosphere; } /** {@inheritDoc} */ @@ -119,6 +145,35 @@ protected > boolean isGradientStateDerivative( } } + /** + * Evaluate the Field density. + * @param s spacecraft state + * @return atmospheric density + * @param field type + * @since 12.1 + */ + @SuppressWarnings("unchecked") + protected > T getFieldDensity(final FieldSpacecraftState s) { + final FieldAbsoluteDate date = s.getDate(); + final Frame frame = s.getFrame(); + final FieldVector3D position = s.getPosition(); + if (isGradientStateDerivative(s)) { + if (useFiniteDifferencesOnDensityWrtPosition) { + return (T) this.getGradientDensityWrtStateUsingFiniteDifferences(date.toAbsoluteDate(), frame, (FieldVector3D) position); + } else { + return (T) this.getGradientDensityWrtState(date.toAbsoluteDate(), frame, (FieldVector3D) position); + } + } else if (isDSStateDerivative(s)) { + if (useFiniteDifferencesOnDensityWrtPosition) { + return (T) this.getDSDensityWrtStateUsingFiniteDifferences(date.toAbsoluteDate(), frame, (FieldVector3D) position); + } else { + return (T) this.getDSDensityWrtState(date.toAbsoluteDate(), frame, (FieldVector3D) position); + } + } else { + return atmosphere.getDensity(date, position, frame); + } + } + /** Check if a derivative represents a specified variable. * @param ds derivative to check * @param index index of the variable @@ -154,7 +209,7 @@ protected boolean isVariable(final Gradient g, final int index) { * From a theoretical point of view, this method computes the same values * as {@link Atmosphere#getDensity(FieldAbsoluteDate, FieldVector3D, Frame)} in the * specific case of {@link DerivativeStructure} with respect to state, so - * it is less general. However, it is *much* faster in this important case. + * it is less general. However, it can be faster depending the Field implementation. *

    *

    * The derivatives should be computed with respect to position. The input @@ -223,6 +278,55 @@ protected DerivativeStructure getDSDensityWrtStateUsingFiniteDifferences(final A return factory.build(rhoAll); } + /** Compute density and its derivatives. + * And doing the actual computation only for the derivatives with respect to position (others are set to 0.). + *

    + * The derivatives should be computed with respect to position. The input + * parameters already take into account the free parameters (6, 7 or 8 depending + * on derivation with respect to drag coefficient and lift ratio being considered or not) + * and order (always 1). Free parameters at indices 0, 1 and 2 correspond to derivatives + * with respect to position. Free parameters at indices 3, 4 and 5 correspond + * to derivatives with respect to velocity (these derivatives will remain zero + * as the atmospheric density does not depend on velocity). Free parameter + * at indexes 6 and 7 (if present) corresponds to derivatives with respect to drag coefficient + * and/or lift ratio (one of these or both). + * This 2 last derivatives will remain zero as atmospheric density does not depend on them. + *

    + * @param date current date + * @param frame inertial reference frame for state (both orbit and attitude) + * @param position position of spacecraft in inertial frame + * @return the density and its derivatives + */ + protected DerivativeStructure getDSDensityWrtState(final AbsoluteDate date, final Frame frame, + final FieldVector3D position) { + + // Retrieve derivation properties for parameter T + // It is implied here that T is a DerivativeStructure + // With order 1 and 6, 7 or 8 free parameters + // This is all checked before in method isStateDerivatives + final DSFactory factory = position.getX().getFactory(); + + // Build a DerivativeStructure using only derivatives with respect to position + final DSFactory factory3 = new DSFactory(3, 1); + final FieldVector3D position3 = + new FieldVector3D<>(factory3.variable(0, position.getX().getReal()), + factory3.variable(1, position.getY().getReal()), + factory3.variable(2, position.getZ().getReal())); + + // Get atmosphere properties in atmosphere own frame + final Frame atmFrame = atmosphere.getFrame(); + final StaticTransform toBody = frame.getStaticTransformTo(atmFrame, date); + final FieldVector3D posBodyDS = toBody.transformPosition(position3); + final FieldAbsoluteDate fieldDate = new FieldAbsoluteDate<>(position3.getX().getField(), date); + final DerivativeStructure density = atmosphere.getDensity(fieldDate, posBodyDS, atmFrame); + + // Density with derivatives: + // - The value and only the 3 first derivatives (those with respect to spacecraft position) are computed + // - Others are set to 0. + final double[] derivatives = Arrays.copyOf(density.getAllDerivatives(), factory.getCompiler().getSize()); + return factory.build(derivatives); + } + /** Compute density and its derivatives. * Using finite differences for the derivatives. * And doing the actual computation only for the derivatives with respect to position (others are set to 0.). @@ -230,7 +334,7 @@ protected DerivativeStructure getDSDensityWrtStateUsingFiniteDifferences(final A * From a theoretical point of view, this method computes the same values * as {@link Atmosphere#getDensity(FieldAbsoluteDate, FieldVector3D, Frame)} in the * specific case of {@link Gradient} with respect to state, so - * it is less general. However, it is *much* faster in this important case. + * it is less general. However, it can be faster depending the Field implementation. *

    *

    * The derivatives should be computed with respect to position. The input @@ -291,4 +395,46 @@ protected Gradient getGradientDensityWrtStateUsingFiniteDifferences(final Absolu return new Gradient(rho0, rhoAll); } + /** Compute density and its derivatives. + *

    + * The derivatives should be computed with respect to position. The input + * parameters already take into account the free parameters (6, 7 or 8 depending + * on derivation with respect to drag coefficient and lift ratio being considered or not) + * and order (always 1). Free parameters at indices 0, 1 and 2 correspond to derivatives + * with respect to position. Free parameters at indices 3, 4 and 5 correspond + * to derivatives with respect to velocity (these derivatives will remain zero + * as the atmospheric density does not depend on velocity). Free parameter + * at indexes 6 and 7 (if present) corresponds to derivatives with respect to drag coefficient + * and/or lift ratio (one of these or both). + * This 2 last derivatives will remain zero as atmospheric density does not depend on them. + *

    + * @param date current date + * @param frame inertial reference frame for state (both orbit and attitude) + * @param position position of spacecraft in inertial frame + * @return the density and its derivatives + * @since 12.1 + */ + protected Gradient getGradientDensityWrtState(final AbsoluteDate date, final Frame frame, + final FieldVector3D position) { + + // Build a Gradient using only derivatives with respect to position + final int positionDimension = 3; + final FieldVector3D position3 = + new FieldVector3D<>(Gradient.variable(positionDimension, 0, position.getX().getReal()), + Gradient.variable(positionDimension, 1, position.getY().getReal()), + Gradient.variable(positionDimension, 2, position.getZ().getReal())); + + // Get atmosphere properties in atmosphere own frame + final Frame atmFrame = atmosphere.getFrame(); + final StaticTransform toBody = frame.getStaticTransformTo(atmFrame, date); + final FieldVector3D posBodyGradient = toBody.transformPosition(position3); + final FieldAbsoluteDate fieldDate = new FieldAbsoluteDate<>(position3.getX().getField(), date); + final Gradient density = atmosphere.getDensity(fieldDate, posBodyGradient, atmFrame); + + // Density with derivatives: + // - The value and only the 3 first derivatives (those with respect to spacecraft position) are computed + // - Others are set to 0. + final double[] derivatives = Arrays.copyOf(density.getGradient(), position.getX().getFreeParameters()); + return new Gradient(density.getValue(), derivatives); + } } diff --git a/src/main/java/org/orekit/forces/drag/DragForce.java b/src/main/java/org/orekit/forces/drag/DragForce.java index 2434f8e47e..f3e4ee2bac 100644 --- a/src/main/java/org/orekit/forces/drag/DragForce.java +++ b/src/main/java/org/orekit/forces/drag/DragForce.java @@ -19,8 +19,6 @@ import java.util.List; import org.hipparchus.CalculusFieldElement; -import org.hipparchus.analysis.differentiation.DerivativeStructure; -import org.hipparchus.analysis.differentiation.Gradient; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.orekit.frames.Frame; @@ -50,22 +48,37 @@ public class DragForce extends AbstractDragForceModel { - /** Atmospheric model. */ - private final Atmosphere atmosphere; - /** Spacecraft. */ private final DragSensitive spacecraft; - /** Simple constructor. + /** Constructor with default flag for finite differences. * @param atmosphere atmospheric model * @param spacecraft the object physical and geometrical information */ public DragForce(final Atmosphere atmosphere, final DragSensitive spacecraft) { super(atmosphere); - this.atmosphere = atmosphere; this.spacecraft = spacecraft; } + /** Simple constructor. + * @param atmosphere atmospheric model + * @param spacecraft the object physical and geometrical information + * @param useFiniteDifferencesOnDensityWrtPosition flag to use finite differences to compute density derivatives w.r.t. + * position (is less accurate but can be faster depending on model) + * @since 12.1 + */ + public DragForce(final Atmosphere atmosphere, final DragSensitive spacecraft, + final boolean useFiniteDifferencesOnDensityWrtPosition) { + super(atmosphere, useFiniteDifferencesOnDensityWrtPosition); + this.spacecraft = spacecraft; + } + + /** {@inheritDoc} */ + @Override + public boolean dependsOnAttitudeRate() { + return getSpacecraft().dependsOnAttitudeRate(); + } + /** {@inheritDoc} */ @Override public Vector3D acceleration(final SpacecraftState s, final double[] parameters) { @@ -74,8 +87,8 @@ public Vector3D acceleration(final SpacecraftState s, final double[] parameters) final Frame frame = s.getFrame(); final Vector3D position = s.getPosition(); - final double rho = atmosphere.getDensity(date, position, frame); - final Vector3D vAtm = atmosphere.getVelocity(date, position, frame); + final double rho = getAtmosphere().getDensity(date, position, frame); + final Vector3D vAtm = getAtmosphere().getVelocity(date, position, frame); final Vector3D relativeVelocity = vAtm.subtract(s.getPVCoordinates().getVelocity()); return spacecraft.dragAcceleration(s, rho, relativeVelocity, parameters); @@ -83,31 +96,17 @@ public Vector3D acceleration(final SpacecraftState s, final double[] parameters) } /** {@inheritDoc} */ - @SuppressWarnings("unchecked") @Override public > FieldVector3D acceleration(final FieldSpacecraftState s, - final T[] parameters) { + final T[] parameters) { + // Density and its derivatives + final T rho = getFieldDensity(s); + // Spacecraft relative velocity with respect to the atmosphere final FieldAbsoluteDate date = s.getDate(); final Frame frame = s.getFrame(); final FieldVector3D position = s.getPosition(); - - // Density and its derivatives - final T rho; - - // Check for faster computation dedicated to derivatives with respect to state - // Using finite differences instead of automatic differentiation as it seems to be much - // faster for the drag's derivatives' computation - if (isGradientStateDerivative(s)) { - rho = (T) this.getGradientDensityWrtStateUsingFiniteDifferences(date.toAbsoluteDate(), frame, (FieldVector3D) position); - } else if (isDSStateDerivative(s)) { - rho = (T) this.getDSDensityWrtStateUsingFiniteDifferences(date.toAbsoluteDate(), frame, (FieldVector3D) position); - } else { - rho = atmosphere.getDensity(date, position, frame); - } - - // Spacecraft relative velocity with respect to the atmosphere - final FieldVector3D vAtm = atmosphere.getVelocity(date, position, frame); + final FieldVector3D vAtm = getAtmosphere().getVelocity(date, position, frame); final FieldVector3D relativeVelocity = vAtm.subtract(s.getPVCoordinates().getVelocity()); // Drag acceleration along with its derivatives @@ -121,13 +120,6 @@ public List getParametersDrivers() { return spacecraft.getDragParametersDrivers(); } - /** Get the atmospheric model. - * @return atmosphere model - */ - public Atmosphere getAtmosphere() { - return atmosphere; - } - /** Get spacecraft that are sensitive to atmospheric drag forces. * @return drag sensitive spacecraft model */ diff --git a/src/main/java/org/orekit/forces/drag/DragSensitive.java b/src/main/java/org/orekit/forces/drag/DragSensitive.java index 9a1dcde998..f91f59ca72 100644 --- a/src/main/java/org/orekit/forces/drag/DragSensitive.java +++ b/src/main/java/org/orekit/forces/drag/DragSensitive.java @@ -53,6 +53,16 @@ public interface DragSensitive { */ String LIFT_RATIO = "lift ratio"; + /** Check if model depends on attitude's rotation rate or acceleration at a given, fixed date. + * If false, it essentially means that at most the attitude's rotation is used when computing the acceleration vector. + * The default implementation returns false as common models for orbital mechanics do not. + * @return true if force model depends on attitude derivatives + * @since 12.1 + */ + default boolean dependsOnAttitudeRate() { + return false; + } + /** Get the drivers for supported parameters. * @return parameters drivers * @since 8.0 diff --git a/src/main/java/org/orekit/forces/drag/TimeSpanDragForce.java b/src/main/java/org/orekit/forces/drag/TimeSpanDragForce.java index 0c4a3e6116..8811a9dc0d 100644 --- a/src/main/java/org/orekit/forces/drag/TimeSpanDragForce.java +++ b/src/main/java/org/orekit/forces/drag/TimeSpanDragForce.java @@ -23,8 +23,6 @@ import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; -import org.hipparchus.analysis.differentiation.DerivativeStructure; -import org.hipparchus.analysis.differentiation.Gradient; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.ode.events.Action; @@ -126,8 +124,11 @@ *

    * @author Maxime Journot * @since 10.2 + * @deprecated as of 12.1 */ +@Deprecated public class TimeSpanDragForce extends AbstractDragForceModel { + // TODO: move to tests /** Prefix for dates before in the parameter drivers' name. */ public static final String DATE_BEFORE = " - Before "; @@ -135,9 +136,6 @@ public class TimeSpanDragForce extends AbstractDragForceModel { /** Prefix for dates after in the parameter drivers' name. */ public static final String DATE_AFTER = " - After "; - /** Atmospheric model. */ - private final Atmosphere atmosphere; - /** TimeSpanMap of DragSensitive objects. */ private final TimeSpanMap dragSensitiveTimeSpanMap; @@ -152,7 +150,6 @@ public class TimeSpanDragForce extends AbstractDragForceModel { public TimeSpanDragForce(final Atmosphere atmosphere, final DragSensitive spacecraft) { super(atmosphere); - this.atmosphere = atmosphere; this.dragSensitiveTimeSpanMap = new TimeSpanMap<>(spacecraft); this.timeScale = TimeScalesFactory.getUTC(); } @@ -166,7 +163,6 @@ public TimeSpanDragForce(final Atmosphere atmosphere, final DragSensitive spacecraft, final TimeScale timeScale) { super(atmosphere); - this.atmosphere = atmosphere; this.dragSensitiveTimeSpanMap = new TimeSpanMap<>(spacecraft); this.timeScale = timeScale; } @@ -247,10 +243,10 @@ public Vector3D acceleration(final SpacecraftState s, final double[] parameters) final AbsoluteDate date = s.getDate(); final Frame frame = s.getFrame(); final Vector3D position = s.getPosition(); - final double rho = atmosphere.getDensity(date, position, frame); + final double rho = getAtmosphere().getDensity(date, position, frame); // Spacecraft relative velocity with respect to the atmosphere - final Vector3D vAtm = atmosphere.getVelocity(date, position, frame); + final Vector3D vAtm = getAtmosphere().getVelocity(date, position, frame); final Vector3D relativeVelocity = vAtm.subtract(s.getPVCoordinates().getVelocity()); // Extract the proper parameters valid at date from the input array @@ -262,31 +258,17 @@ public Vector3D acceleration(final SpacecraftState s, final double[] parameters) } /** {@inheritDoc} */ - @SuppressWarnings("unchecked") @Override public > FieldVector3D acceleration(final FieldSpacecraftState s, - final T[] parameters) { - // Local atmospheric density - final FieldAbsoluteDate date = s.getDate(); - final Frame frame = s.getFrame(); - final FieldVector3D position = s.getPosition(); - + final T[] parameters) { // Density and its derivatives - final T rho; - - // Check for faster computation dedicated to derivatives with respect to state - // Using finite differences instead of automatic differentiation as it seems to be much - // faster for the drag's derivatives' computation - if (isGradientStateDerivative(s)) { - rho = (T) this.getGradientDensityWrtStateUsingFiniteDifferences(date.toAbsoluteDate(), frame, (FieldVector3D) position); - } else if (isDSStateDerivative(s)) { - rho = (T) this.getDSDensityWrtStateUsingFiniteDifferences(date.toAbsoluteDate(), frame, (FieldVector3D) position); - } else { - rho = atmosphere.getDensity(date, position, frame); - } + final T rho = getFieldDensity(s); // Spacecraft relative velocity with respect to the atmosphere - final FieldVector3D vAtm = atmosphere.getVelocity(date, position, frame); + final FieldAbsoluteDate date = s.getDate(); + final Frame frame = s.getFrame(); + final FieldVector3D position = s.getPosition(); + final FieldVector3D vAtm = getAtmosphere().getVelocity(date, position, frame); final FieldVector3D relativeVelocity = vAtm.subtract(s.getPVCoordinates().getVelocity()); // Extract the proper parameters valid at date from the input array diff --git a/src/main/java/org/orekit/forces/gravity/AbstractBodyAttraction.java b/src/main/java/org/orekit/forces/gravity/AbstractBodyAttraction.java new file mode 100644 index 0000000000..932d92315e --- /dev/null +++ b/src/main/java/org/orekit/forces/gravity/AbstractBodyAttraction.java @@ -0,0 +1,84 @@ +/* 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.gravity; + +import org.hipparchus.util.FastMath; +import org.orekit.bodies.CelestialBody; +import org.orekit.forces.ForceModel; +import org.orekit.utils.ParameterDriver; + +import java.util.Collections; +import java.util.List; + +/** Abstract class for body attraction force model. + * + * @author Romain Serra + */ +public abstract class AbstractBodyAttraction implements ForceModel { + + /** Suffix for parameter name for attraction coefficient enabling Jacobian processing. */ + public static final String ATTRACTION_COEFFICIENT_SUFFIX = " attraction coefficient"; + + /** Central attraction scaling factor. + *

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

    + */ + private static final double MU_SCALE = FastMath.scalb(1.0, 32); + + /** The body to consider. */ + private final CelestialBody body; + + /** Drivers for body attraction coefficient. */ + private final ParameterDriver gmParameterDriver; + + /** Simple constructor. + * @param body the third body to consider + */ + protected AbstractBodyAttraction(final CelestialBody body) { + this.body = body; + this.gmParameterDriver = new ParameterDriver(body.getName() + ATTRACTION_COEFFICIENT_SUFFIX, + body.getGM(), MU_SCALE, 0.0, Double.POSITIVE_INFINITY); + } + + /** Getter for the body's name. + * @return the body's name + */ + public String getBodyName() { + return body.getName(); + } + + /** Protected getter for the body. + * @return the third body considered + */ + protected CelestialBody getBody() { + return body; + } + + /** {@inheritDoc} */ + @Override + public boolean dependsOnPositionOnly() { + return true; + } + + /** {@inheritDoc} */ + @Override + public List getParametersDrivers() { + return Collections.singletonList(gmParameterDriver); + } +} diff --git a/src/main/java/org/orekit/forces/gravity/HolmesFeatherstoneAttractionModel.java b/src/main/java/org/orekit/forces/gravity/HolmesFeatherstoneAttractionModel.java index 7de3f9ef73..8f172f1e13 100644 --- a/src/main/java/org/orekit/forces/gravity/HolmesFeatherstoneAttractionModel.java +++ b/src/main/java/org/orekit/forces/gravity/HolmesFeatherstoneAttractionModel.java @@ -416,11 +416,11 @@ public > T[] gradient(final FieldAbsoluteDate< final T x = position.getX(); final T y = position.getY(); final T z = position.getZ(); - final T x2 = x.multiply(x); - final T y2 = y.multiply(y); + final T x2 = x.square(); + final T y2 = y.square(); final T rho2 = x2.add(y2); final T rho = rho2.sqrt(); - final T z2 = z.multiply(z); + final T z2 = z.square(); final T r2 = rho2.add(z2); final T r = r2.sqrt(); final T t = z.divide(r); // cos(theta), where theta is the polar angle @@ -506,9 +506,9 @@ public > T[] gradient(final FieldAbsoluteDate< final T xPos = position.getX(); final T yPos = position.getY(); final T zPos = position.getZ(); - final T rho2Pos = x.multiply(x).add(y.multiply(y)); + final T rho2Pos = x.square().add(y.square()); final T rhoPos = rho2.sqrt(); - final T r2Pos = rho2.add(z.multiply(z)); + final T r2Pos = rho2.add(z.square()); final T rPos = r2Pos.sqrt(); final T[][] jacobianPos = MathArrays.buildArray(zero.getField(), 3, 3); @@ -972,12 +972,12 @@ private > int computeTesseral(final int m, fin final T[] pnm0Plus2, final T[] pnm0Plus1, final T[] pnm1Plus1, final T[] pnm0, final T[] pnm1, final T[] pnm2) { - final T u2 = u.multiply(u); + final T u2 = u.square(); final T zero = u.getField().getZero(); // initialize recursion from sectorial terms int n = FastMath.max(2, m); if (n == m) { - pnm0[n] = zero.add(sectorial[n]); + pnm0[n] = zero.newInstance(sectorial[n]); ++n; } diff --git a/src/main/java/org/orekit/forces/gravity/J2OnlyPerturbation.java b/src/main/java/org/orekit/forces/gravity/J2OnlyPerturbation.java new file mode 100644 index 0000000000..077b1765f7 --- /dev/null +++ b/src/main/java/org/orekit/forces/gravity/J2OnlyPerturbation.java @@ -0,0 +1,220 @@ +/* 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.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.forces.ForceModel; +import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; +import org.orekit.frames.FieldStaticTransform; +import org.orekit.frames.Frame; +import org.orekit.frames.StaticTransform; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.SpacecraftState; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.TimeScalarFunction; +import org.orekit.utils.ParameterDriver; + +import java.util.Collections; +import java.util.List; + +/** J2-only force model. + * This class models the oblateness part alone of the central body's potential (degree 2 and order 0), + * whilst avoiding the computational overhead of generic NxM spherical harmonics. + * + *

    + * This J2 coefficient has same magnitude and opposite sign than the so-called unnormalized C20 coefficient. + *

    + * + *

    + * This class should not be used in combination of {@link HolmesFeatherstoneAttractionModel}, + * otherwise the J2 term would be taken into account twice. + *

    + * + * @author Romain Serra + */ +public class J2OnlyPerturbation implements ForceModel { + + /** Central body's gravitational constant. */ + private final double mu; + + /** Central body's equatorial radius. */ + private final double rEq; + + /** Central body's J2 coefficient as a function of time. */ + private final TimeScalarFunction j2OverTime; + + /** Frame where J2 applies. */ + private final Frame frame; + + /** Constructor with {@link TimeScalarFunction}. + * It is the user's responsibility to make sure the Field and double versions are consistent with each other. + * @param mu central body's gravitational constant + * @param rEq central body's equatorial radius + * @param j2OverTime J2 coefficient as a function of time. + * @param frame frame where J2 applies + */ + public J2OnlyPerturbation(final double mu, final double rEq, final TimeScalarFunction j2OverTime, + final Frame frame) { + this.mu = mu; + this.rEq = rEq; + this.j2OverTime = j2OverTime; + this.frame = frame; + } + + /** Constructor with constant J2. + * @param mu central body gravitational constant + * @param rEq central body's equatorial radius + * @param constantJ2 constant J2 coefficient + * @param frame frame where J2 applies + */ + public J2OnlyPerturbation(final double mu, final double rEq, final double constantJ2, final Frame frame) { + this.mu = mu; + this.rEq = rEq; + this.frame = frame; + this.j2OverTime = new TimeScalarFunction() { + @Override + public double value(final AbsoluteDate date) { + return constantJ2; + } + + @Override + public > T value(final FieldAbsoluteDate date) { + return date.getField().getZero().newInstance(constantJ2); + } + }; + } + + /** Constructor with spherical harmonics provider. + * @param harmonicsProvider spherical harmonics provider of unnormalized coefficients + * @param frame frame where J2 applies + */ + public J2OnlyPerturbation(final UnnormalizedSphericalHarmonicsProvider harmonicsProvider, final Frame frame) { + this.mu = harmonicsProvider.getMu(); + this.rEq = harmonicsProvider.getAe(); + this.frame = frame; + this.j2OverTime = new TimeScalarFunction() { + @Override + public double value(final AbsoluteDate date) { + return -harmonicsProvider.getUnnormalizedC20(date); + } + + @Override + public > T value(final FieldAbsoluteDate date) { + return date.getField().getZero().newInstance(value(date.toAbsoluteDate())); + } + }; + } + + /** Getter for mu. + * @return mu + */ + public double getMu() { + return mu; + } + + /** Getter for equatorial radius. + * @return equatorial radius + */ + public double getrEq() { + return rEq; + } + + /** Getter for frame. + * @return frame + */ + public Frame getFrame() { + return frame; + } + + /** Return J2 at requested date. + * @param date epoch at which J2 coefficient should be retrieved + * @return J2 coefficient + */ + public double getJ2(final AbsoluteDate date) { + return j2OverTime.value(date); + } + + /** Return J2 at requested date (Field version). + * @param field + * @param date epoch at which J2 coefficient should be retrieved + * @return J2 coefficient + */ + public > T getJ2(final FieldAbsoluteDate date) { + return j2OverTime.value(date); + } + + /** {@inheritDoc} */ + @Override + public boolean dependsOnPositionOnly() { + return true; + } + + /** {@inheritDoc} */ + @Override + public Vector3D acceleration(final SpacecraftState state, final double[] parameters) { + final Vector3D positionInJ2Frame = state.getPosition(frame); + 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 squaredRadiiRatio = rEq * rEq / squaredRadius; + final double cubedRadius = squaredRadius * FastMath.sqrt(squaredRadius); + final double factor = 3 * j2 * mu * squaredRadiiRatio / (2 * cubedRadius); + return transformedAcceleration.scalarMultiply(factor); + } + + /** {@inheritDoc} */ + @Override + public > FieldVector3D acceleration(final FieldSpacecraftState state, + final T[] parameters) { + final FieldVector3D positionInJ2Frame = state.getPosition(frame); + 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 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); + } + + /** {@inheritDoc} */ + @Override + public List getParametersDrivers() { + return Collections.emptyList(); + } +} diff --git a/src/main/java/org/orekit/forces/gravity/LenseThirringRelativity.java b/src/main/java/org/orekit/forces/gravity/LenseThirringRelativity.java index 3901df3aff..0d469e7049 100644 --- a/src/main/java/org/orekit/forces/gravity/LenseThirringRelativity.java +++ b/src/main/java/org/orekit/forces/gravity/LenseThirringRelativity.java @@ -133,7 +133,7 @@ public > FieldVector3D acceleration(final F // Radius final T r = p.getNorm(); - final T r2 = r.multiply(r); + final T r2 = r.square(); // Earth’s angular momentum per unit mass final FieldStaticTransform t = bodyFrame.getStaticTransformTo(s.getFrame(), s.getDate()); diff --git a/src/main/java/org/orekit/forces/gravity/NewtonianAttraction.java b/src/main/java/org/orekit/forces/gravity/NewtonianAttraction.java index 6a891e860b..bba1b4c458 100644 --- a/src/main/java/org/orekit/forces/gravity/NewtonianAttraction.java +++ b/src/main/java/org/orekit/forces/gravity/NewtonianAttraction.java @@ -83,7 +83,7 @@ public double getMu(final AbsoluteDate date) { */ public > T getMu(final Field field, final FieldAbsoluteDate date) { final T zero = field.getZero(); - return zero.add(gmParameterDriver.getValue(date.toAbsoluteDate())); + return zero.newInstance(gmParameterDriver.getValue(date.toAbsoluteDate())); } /** {@inheritDoc} */ diff --git a/src/main/java/org/orekit/forces/gravity/SingleBodyAbsoluteAttraction.java b/src/main/java/org/orekit/forces/gravity/SingleBodyAbsoluteAttraction.java index 39a6025ffa..b2d5d9a770 100644 --- a/src/main/java/org/orekit/forces/gravity/SingleBodyAbsoluteAttraction.java +++ b/src/main/java/org/orekit/forces/gravity/SingleBodyAbsoluteAttraction.java @@ -16,19 +16,14 @@ */ package org.orekit.forces.gravity; -import java.util.Collections; -import java.util.List; - 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.CelestialBodies; import org.orekit.bodies.CelestialBody; -import org.orekit.forces.ForceModel; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; -import org.orekit.utils.ParameterDriver; /** Body attraction force model computed as absolute acceleration towards a body. *

    @@ -61,24 +56,7 @@ * @author Luc Maisonobe * @author Julio Hernanz */ -public class SingleBodyAbsoluteAttraction implements ForceModel { - - /** Suffix for parameter name for attraction coefficient enabling Jacobian processing. */ - public static final String ATTRACTION_COEFFICIENT_SUFFIX = " attraction coefficient"; - - /** Central attraction scaling factor. - *

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

    - */ - private static final double MU_SCALE = FastMath.scalb(1.0, 32); - - /** The body to consider. */ - private final CelestialBody body; - - /** Driver for gravitational parameter. */ - private final ParameterDriver gmParameterDriver; +public class SingleBodyAbsoluteAttraction extends AbstractBodyAttraction { /** Simple constructor. * @param body the body to consider @@ -86,17 +64,7 @@ public class SingleBodyAbsoluteAttraction implements ForceModel { * {@link CelestialBodies#getMoon()}) */ public SingleBodyAbsoluteAttraction(final CelestialBody body) { - gmParameterDriver = new ParameterDriver(body.getName() + ATTRACTION_COEFFICIENT_SUFFIX, - body.getGM(), MU_SCALE, - 0.0, Double.POSITIVE_INFINITY); - - this.body = body; - } - - /** {@inheritDoc} */ - @Override - public boolean dependsOnPositionOnly() { - return true; + super(body); } /** {@inheritDoc} */ @@ -104,7 +72,7 @@ public boolean dependsOnPositionOnly() { public Vector3D acceleration(final SpacecraftState s, final double[] parameters) { // compute bodies separation vectors and squared norm - final Vector3D bodyPosition = body.getPosition(s.getDate(), s.getFrame()); + final Vector3D bodyPosition = getBody().getPosition(s.getDate(), s.getFrame()); final Vector3D satToBody = bodyPosition.subtract(s.getPosition()); final double r2Sat = satToBody.getNormSq(); @@ -116,9 +84,9 @@ public Vector3D acceleration(final SpacecraftState s, final double[] parameters) /** {@inheritDoc} */ @Override public > FieldVector3D acceleration(final FieldSpacecraftState s, - final T[] parameters) { + final T[] parameters) { // compute bodies separation vectors and squared norm - final FieldVector3D centralToBody = body.getPosition(s.getDate(), s.getFrame()); + final FieldVector3D centralToBody = getBody().getPosition(s.getDate(), s.getFrame()); final FieldVector3D satToBody = centralToBody.subtract(s.getPosition()); final T r2Sat = satToBody.getNormSq(); @@ -127,9 +95,4 @@ public > FieldVector3D acceleration(final F } - /** {@inheritDoc} */ - public List getParametersDrivers() { - return Collections.singletonList(gmParameterDriver); - } - } diff --git a/src/main/java/org/orekit/forces/gravity/SingleBodyRelativeAttraction.java b/src/main/java/org/orekit/forces/gravity/SingleBodyRelativeAttraction.java index 85805a6668..cd5f76f4c1 100644 --- a/src/main/java/org/orekit/forces/gravity/SingleBodyRelativeAttraction.java +++ b/src/main/java/org/orekit/forces/gravity/SingleBodyRelativeAttraction.java @@ -16,44 +16,21 @@ */ package org.orekit.forces.gravity; -import java.util.Collections; -import java.util.List; - 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.CelestialBodies; import org.orekit.bodies.CelestialBody; -import org.orekit.forces.ForceModel; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; import org.orekit.utils.FieldPVCoordinates; import org.orekit.utils.PVCoordinates; -import org.orekit.utils.ParameterDriver; /** Body attraction force model computed as relative acceleration towards frame center. * @author Luc Maisonabe * @author Julio Hernanz */ -public class SingleBodyRelativeAttraction implements ForceModel { - - /** Suffix for parameter name for attraction coefficient enabling Jacobian processing. */ - public static final String ATTRACTION_COEFFICIENT_SUFFIX = " attraction coefficient"; - - /** Central attraction scaling factor. - *

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

    - */ - private static final double MU_SCALE = FastMath.scalb(1.0, 32); - - /** Drivers for body attraction coefficient. */ - private final ParameterDriver gmDriver; - - /** The body to consider. */ - private final CelestialBody body; +public class SingleBodyRelativeAttraction extends AbstractBodyAttraction { /** Simple constructor. * @param body the body to consider @@ -61,24 +38,14 @@ public class SingleBodyRelativeAttraction implements ForceModel { * {@link CelestialBodies#getMoon()}) */ public SingleBodyRelativeAttraction(final CelestialBody body) { - gmDriver = new ParameterDriver(body.getName() + ATTRACTION_COEFFICIENT_SUFFIX, - body.getGM(), MU_SCALE, - 0.0, Double.POSITIVE_INFINITY); - - this.body = body; - } - - /** {@inheritDoc} */ - @Override - public boolean dependsOnPositionOnly() { - return true; + super(body); } /** {@inheritDoc} */ public Vector3D acceleration(final SpacecraftState s, final double[] parameters) { // compute bodies separation vectors and squared norm - final PVCoordinates bodyPV = body.getPVCoordinates(s.getDate(), s.getFrame()); + final PVCoordinates bodyPV = getBody().getPVCoordinates(s.getDate(), s.getFrame()); final Vector3D satToBody = bodyPV.getPosition().subtract(s.getPosition()); final double r2Sat = satToBody.getNormSq(); @@ -94,7 +61,7 @@ public > FieldVector3D acceleration(final F final T[] parameters) { // compute bodies separation vectors and squared norm - final FieldPVCoordinates bodyPV = body.getPVCoordinates(s.getDate(), s.getFrame()); + final FieldPVCoordinates bodyPV = getBody().getPVCoordinates(s.getDate(), s.getFrame()); final FieldVector3D satToBody = bodyPV.getPosition().subtract(s.getPosition()); final T r2Sat = satToBody.getNormSq(); @@ -105,9 +72,4 @@ public > FieldVector3D acceleration(final F } - /** {@inheritDoc} */ - public List getParametersDrivers() { - return Collections.singletonList(gmDriver); - } - } diff --git a/src/main/java/org/orekit/forces/gravity/ThirdBodyAttraction.java b/src/main/java/org/orekit/forces/gravity/ThirdBodyAttraction.java index 3513a36b5c..3d0f95c59a 100644 --- a/src/main/java/org/orekit/forces/gravity/ThirdBodyAttraction.java +++ b/src/main/java/org/orekit/forces/gravity/ThirdBodyAttraction.java @@ -16,43 +16,21 @@ */ package org.orekit.forces.gravity; -import java.util.Collections; -import java.util.List; - 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.CelestialBodies; import org.orekit.bodies.CelestialBody; -import org.orekit.forces.ForceModel; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; -import org.orekit.utils.ParameterDriver; /** Third body attraction force model. * * @author Fabien Maussion * @author Véronique Pommier-Maurussane */ -public class ThirdBodyAttraction implements ForceModel { - - /** Suffix for parameter name for attraction coefficient enabling Jacobian processing. */ - public static final String ATTRACTION_COEFFICIENT_SUFFIX = " attraction coefficient"; - - /** Central attraction scaling factor. - *

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

    - */ - private static final double MU_SCALE = FastMath.scalb(1.0, 32); - - /** Drivers for third body attraction coefficient. */ - private final ParameterDriver gmParameterDriver; - - /** The body to consider. */ - private final CelestialBody body; +public class ThirdBodyAttraction extends AbstractBodyAttraction { /** Simple constructor. * @param body the third body to consider @@ -60,17 +38,7 @@ public class ThirdBodyAttraction implements ForceModel { * {@link CelestialBodies#getMoon()}) */ public ThirdBodyAttraction(final CelestialBody body) { - gmParameterDriver = new ParameterDriver(body.getName() + ATTRACTION_COEFFICIENT_SUFFIX, - body.getGM(), MU_SCALE, - 0.0, Double.POSITIVE_INFINITY); - - this.body = body; - } - - /** {@inheritDoc} */ - @Override - public boolean dependsOnPositionOnly() { - return true; + super(body); } /** {@inheritDoc} */ @@ -80,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 = body.getPosition(s.getDate(), s.getFrame()); + final Vector3D centralToBody = getBody().getPosition(s.getDate(), s.getFrame()); final double r2Central = centralToBody.getNormSq(); final Vector3D satToBody = centralToBody.subtract(s.getPosition()); final double r2Sat = satToBody.getNormSq(); @@ -94,12 +62,12 @@ public Vector3D acceleration(final SpacecraftState s, final double[] parameters) /** {@inheritDoc} */ @Override public > FieldVector3D acceleration(final FieldSpacecraftState s, - final T[] parameters) { + final T[] parameters) { final T gm = parameters[0]; // compute bodies separation vectors and squared norm - final FieldVector3D centralToBody = body.getPosition(s.getDate(), s.getFrame()); + final FieldVector3D centralToBody = getBody().getPosition(s.getDate(), s.getFrame()); final T r2Central = centralToBody.getNormSq(); final FieldVector3D satToBody = centralToBody.subtract(s.getPosition()); final T r2Sat = satToBody.getNormSq(); @@ -110,10 +78,4 @@ public > FieldVector3D acceleration(final F } - /** {@inheritDoc} */ - @Override - public List getParametersDrivers() { - return Collections.singletonList(gmParameterDriver); - } - } diff --git a/src/main/java/org/orekit/forces/gravity/ThirdBodyAttractionEpoch.java b/src/main/java/org/orekit/forces/gravity/ThirdBodyAttractionEpoch.java index 18d56cad6a..9dff46a4e3 100644 --- a/src/main/java/org/orekit/forces/gravity/ThirdBodyAttractionEpoch.java +++ b/src/main/java/org/orekit/forces/gravity/ThirdBodyAttractionEpoch.java @@ -33,9 +33,6 @@ */ public class ThirdBodyAttractionEpoch extends ThirdBodyAttraction { - /** The body to consider. */ - private final CelestialBody body; - /** Simple constructor. * @param body the third body to consider * (ex: {@link org.orekit.bodies.CelestialBodyFactory#getSun()} or @@ -43,7 +40,6 @@ public class ThirdBodyAttractionEpoch extends ThirdBodyAttraction { */ public ThirdBodyAttractionEpoch(final CelestialBody body) { super(body); - this.body = body; } /** Compute acceleration. @@ -56,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 = body.getPosition(s.getDate(), s.getFrame()); + final Vector3D centralToBody = getBody().getPosition(s.getDate(), s.getFrame()); // Spacecraft Position final double rx = centralToBody.getX(); @@ -87,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 = body.getPVCoordinates(s.getDate(), s.getFrame()).getVelocity(); + final Vector3D centralToBodyVelocity = getBody().getPVCoordinates(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/NormalizedSphericalHarmonicsProvider.java b/src/main/java/org/orekit/forces/gravity/potential/NormalizedSphericalHarmonicsProvider.java index f10660828a..5a5d4af79c 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/NormalizedSphericalHarmonicsProvider.java +++ b/src/main/java/org/orekit/forces/gravity/potential/NormalizedSphericalHarmonicsProvider.java @@ -59,4 +59,15 @@ interface NormalizedSphericalHarmonics extends TimeStamped { */ NormalizedSphericalHarmonics onDate(AbsoluteDate date); + /** + * Get the normalized coefficient of degree 2 and order 0 at a specific instance in time. + * + * @param date of evaluation (may be null if model is not time-dependent) + * @return normalized C20 on {@code date}. + * @since 12.1 + */ + default double getNormalizedC20(final AbsoluteDate date) { + return onDate(date).getNormalizedCnm(2, 0); + } + } diff --git a/src/main/java/org/orekit/forces/gravity/potential/SphericalHarmonicsProvider.java b/src/main/java/org/orekit/forces/gravity/potential/SphericalHarmonicsProvider.java index a974e1a607..355696f98f 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/SphericalHarmonicsProvider.java +++ b/src/main/java/org/orekit/forces/gravity/potential/SphericalHarmonicsProvider.java @@ -23,7 +23,7 @@ *

    * Two interfaces are provided to distinguish between normalized and un-normalized * coefficients: {@link NormalizedSphericalHarmonicsProvider} and {@link - * UnnormalizedSphericalHarmonicsProvider}. To account for gravity pertubations all + * UnnormalizedSphericalHarmonicsProvider}. To account for gravity perturbations all * providers are capable of providing the coefficients on specific dates, using the {@link * NormalizedSphericalHarmonicsProvider#onDate(AbsoluteDate) onDate(AbsoluteDate)} * methods. @@ -31,7 +31,7 @@ * Typical usage when evaluating the geopotential: *

    
      *     NormalizedSphericalHarmonicsProvider provider = ...;
    - *     NormalizedShpericalHarmonics coeffs = provider.onDate(date);
    + *     NormalizedSphericalHarmonics coeffs = provider.onDate(date);
      *     double c20 = coeffs.getNormalizedCnm(2, 0);
      * 
    * diff --git a/src/main/java/org/orekit/forces/gravity/potential/UnnormalizedSphericalHarmonicsProvider.java b/src/main/java/org/orekit/forces/gravity/potential/UnnormalizedSphericalHarmonicsProvider.java index 279d18f933..c8dd09d40d 100644 --- a/src/main/java/org/orekit/forces/gravity/potential/UnnormalizedSphericalHarmonicsProvider.java +++ b/src/main/java/org/orekit/forces/gravity/potential/UnnormalizedSphericalHarmonicsProvider.java @@ -56,7 +56,6 @@ interface UnnormalizedSphericalHarmonics extends TimeStamped { } - /** * Get the un-normalized spherical harmonic coefficients at a specific instance in time. * @@ -66,4 +65,15 @@ interface UnnormalizedSphericalHarmonics extends TimeStamped { */ UnnormalizedSphericalHarmonics onDate(AbsoluteDate date); + /** + * Get the un-normalized coefficient of degree 2 and order 0 at a specific instance in time. + * + * @param date of evaluation (may be null if model is not time-dependent) + * @return un-normalized C20 on {@code date}. + * @since 12.1 + */ + default double getUnnormalizedC20(final AbsoluteDate date) { + return onDate(date).getUnnormalizedCnm(2, 0); + } + } diff --git a/src/main/java/org/orekit/forces/maneuvers/Control3DVectorCostType.java b/src/main/java/org/orekit/forces/maneuvers/Control3DVectorCostType.java index 481d8751a2..06ae45dbef 100644 --- a/src/main/java/org/orekit/forces/maneuvers/Control3DVectorCostType.java +++ b/src/main/java/org/orekit/forces/maneuvers/Control3DVectorCostType.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2024 Romain Serra +/* 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. diff --git a/src/main/java/org/orekit/forces/maneuvers/FieldImpulseManeuver.java b/src/main/java/org/orekit/forces/maneuvers/FieldImpulseManeuver.java index 1a03e59f67..43fa92f129 100644 --- a/src/main/java/org/orekit/forces/maneuvers/FieldImpulseManeuver.java +++ b/src/main/java/org/orekit/forces/maneuvers/FieldImpulseManeuver.java @@ -233,6 +233,7 @@ public Action eventOccurred(final FieldSpacecraftState s, final FieldEventDetector detector, final boolean increasing) { // filter underlying event + @SuppressWarnings("unchecked") final FieldImpulseManeuver im = (FieldImpulseManeuver) detector; final Action underlyingAction = im.trigger.getHandler().eventOccurred(s, im.trigger, increasing); @@ -245,6 +246,7 @@ public Action eventOccurred(final FieldSpacecraftState s, public FieldSpacecraftState resetState(final FieldEventDetector detector, final FieldSpacecraftState oldState) { + @SuppressWarnings("unchecked") final FieldImpulseManeuver im = (FieldImpulseManeuver) detector; final FieldAbsoluteDate date = oldState.getDate(); final FieldRotation rotation; diff --git a/src/main/java/org/orekit/forces/maneuvers/Maneuver.java b/src/main/java/org/orekit/forces/maneuvers/Maneuver.java index 8e356f45d9..e17fdf80e0 100644 --- a/src/main/java/org/orekit/forces/maneuvers/Maneuver.java +++ b/src/main/java/org/orekit/forces/maneuvers/Maneuver.java @@ -295,7 +295,7 @@ public List getParametersDrivers() { * @param parameters parameters' array called in by ForceModel interface * @return propulsion model parameters */ - private double[] getPropulsionModelParameters(final double[] parameters) { + public double[] getPropulsionModelParameters(final double[] parameters) { return Arrays.copyOfRange(parameters, 0, propulsionModel.getParametersDrivers().size()); } @@ -305,7 +305,7 @@ private double[] getPropulsionModelParameters(final double[] parameters) { * @param extends CalculusFieldElement<T> * @return propulsion model parameters */ - private > T[] getPropulsionModelParameters(final T[] parameters) { + public > T[] getPropulsionModelParameters(final T[] parameters) { return Arrays.copyOfRange(parameters, 0, propulsionModel.getParametersDrivers().size()); } @@ -314,7 +314,7 @@ private > T[] getPropulsionModelParameters(fin * @param parameters parameters' array called in by ForceModel interface * @return maneuver triggers' parameters */ - private double[] getManeuverTriggersParameters(final double[] parameters) { + public double[] getManeuverTriggersParameters(final double[] parameters) { final int nbPropulsionModelDrivers = propulsionModel.getParametersDrivers().size(); return Arrays.copyOfRange(parameters, nbPropulsionModelDrivers, nbPropulsionModelDrivers + maneuverTriggers.getParametersDrivers().size()); @@ -326,7 +326,7 @@ private double[] getManeuverTriggersParameters(final double[] parameters) { * @param extends CalculusFieldElement<T> * @return maneuver triggers' parameters */ - private > T[] getManeuverTriggersParameters(final T[] parameters) { + public > T[] getManeuverTriggersParameters(final T[] parameters) { final int nbPropulsionModelDrivers = propulsionModel.getParametersDrivers().size(); return Arrays.copyOfRange(parameters, nbPropulsionModelDrivers, nbPropulsionModelDrivers + maneuverTriggers.getParametersDrivers().size()); diff --git a/src/main/java/org/orekit/forces/maneuvers/SmallManeuverAnalyticalModel.java b/src/main/java/org/orekit/forces/maneuvers/SmallManeuverAnalyticalModel.java index 2919961c0a..aea9e1ea77 100644 --- a/src/main/java/org/orekit/forces/maneuvers/SmallManeuverAnalyticalModel.java +++ b/src/main/java/org/orekit/forces/maneuvers/SmallManeuverAnalyticalModel.java @@ -68,8 +68,7 @@ *

    * @author Luc Maisonobe */ -public class SmallManeuverAnalyticalModel - implements AdapterPropagator.DifferentialEffect { +public class SmallManeuverAnalyticalModel implements AdapterPropagator.DifferentialEffect { /** State at maneuver date (before maneuver occurrence). */ private final SpacecraftState state0; @@ -92,7 +91,7 @@ public class SmallManeuverAnalyticalModel /** Mean anomaly change factor. */ private final double ksi; - /** Build a maneuver defined in spacecraft frame. + /** Build a maneuver defined in spacecraft frame with default orbit type. * @param state0 state at maneuver date, before the maneuver * is performed * @param dV velocity increment in spacecraft frame @@ -105,6 +104,21 @@ public SmallManeuverAnalyticalModel(final SpacecraftState state0, isp); } + /** Build a maneuver defined in spacecraft frame. + * @param state0 state at maneuver date, before the maneuver + * is performed + * @param orbitType orbit type to be used later on in Jacobian conversions + * @param dV velocity increment in spacecraft frame + * @param isp engine specific impulse (s) + * @since 12.1 orbit type added as input + */ + public SmallManeuverAnalyticalModel(final SpacecraftState state0, final OrbitType orbitType, + final Vector3D dV, final double isp) { + this(state0, orbitType, state0.getFrame(), + state0.getAttitude().getRotation().applyInverseTo(dV), + isp); + } + /** Build a maneuver defined in user-specified frame. * @param state0 state at maneuver date, before the maneuver * is performed @@ -114,17 +128,30 @@ public SmallManeuverAnalyticalModel(final SpacecraftState state0, */ public SmallManeuverAnalyticalModel(final SpacecraftState state0, final Frame frame, final Vector3D dV, final double isp) { + // No orbit type specified, use equinoctial orbit type if possible, Keplerian if nearly hyperbolic orbits + this(state0, (state0.getE() < 0.9) ? OrbitType.EQUINOCTIAL : OrbitType.KEPLERIAN, frame, dV, isp); + } + + /** Build a maneuver defined in user-specified frame. + * @param state0 state at maneuver date, before the maneuver + * is performed + * @param orbitType orbit type to be used later on in Jacobian conversions + * @param frame frame in which velocity increment is defined + * @param dV velocity increment in specified frame + * @param isp engine specific impulse (s) + * @since 12.1 orbit type added as input + */ + public SmallManeuverAnalyticalModel(final SpacecraftState state0, final OrbitType orbitType, + final Frame frame, final Vector3D dV, final double isp) { this.state0 = state0; this.massRatio = FastMath.exp(-dV.getNorm() / (Constants.G0_STANDARD_GRAVITY * isp)); - - // use equinoctial orbit type if possible, Keplerian if nearly hyperbolic orbits - type = (state0.getE() < 0.9) ? OrbitType.EQUINOCTIAL : OrbitType.KEPLERIAN; + this.type = orbitType; // compute initial Jacobian final double[][] fullJacobian = new double[6][6]; j0 = new double[6][3]; - final Orbit orbit0 = type.convertType(state0.getOrbit()); + final Orbit orbit0 = orbitType.convertType(state0.getOrbit()); orbit0.getJacobianWrtCartesian(PositionAngleType.MEAN, fullJacobian); for (int i = 0; i < j0.length; ++i) { System.arraycopy(fullJacobian[i], 3, j0[i], 0, 3); @@ -135,7 +162,7 @@ public SmallManeuverAnalyticalModel(final SpacecraftState state0, final Frame fr // compute maneuver effect on Keplerian (or equinoctial) elements inertialDV = frame.getStaticTransformTo(state0.getFrame(), state0.getDate()) - .transformVector(dV); + .transformVector(dV); // compute mean anomaly change: dM(t1) = dM(t0) + ksi * da * (t1 - t0) final double mu = state0.getMu(); @@ -293,8 +320,8 @@ public void getJacobian(final Orbit orbit1, final PositionAngleType positionAngl for (int i = 0; i < 6; ++i) { for (int j = 0; j < 4; ++j) { pvJacobian[i][j] = j2[i][0] * jacobian[0][j] + j2[i][1] * jacobian[1][j] + - j2[i][2] * jacobian[2][j] + j2[i][3] * jacobian[3][j] + - j2[i][4] * jacobian[4][j] + j2[i][5] * jacobian[5][j]; + j2[i][2] * jacobian[2][j] + j2[i][3] * jacobian[3][j] + + j2[i][4] * jacobian[4][j] + j2[i][5] * jacobian[5][j]; } } @@ -304,8 +331,8 @@ public void getJacobian(final Orbit orbit1, final PositionAngleType positionAngl for (int j = 0; j < 4; ++j) { for (int i = 0; i < 6; ++i) { jacobian[i][j] = j3[i][0] * pvJacobian[0][j] + j3[i][1] * pvJacobian[1][j] + - j3[i][2] * pvJacobian[2][j] + j3[i][3] * pvJacobian[3][j] + - j3[i][4] * pvJacobian[4][j] + j3[i][5] * pvJacobian[5][j]; + j3[i][2] * pvJacobian[2][j] + j3[i][3] * pvJacobian[3][j] + + j3[i][4] * pvJacobian[4][j] + j3[i][5] * pvJacobian[5][j]; } } diff --git a/src/main/java/org/orekit/forces/maneuvers/propulsion/ScaledConstantThrustPropulsionModel.java b/src/main/java/org/orekit/forces/maneuvers/propulsion/ScaledConstantThrustPropulsionModel.java index 7e73a1e271..b4cae659a6 100644 --- a/src/main/java/org/orekit/forces/maneuvers/propulsion/ScaledConstantThrustPropulsionModel.java +++ b/src/main/java/org/orekit/forces/maneuvers/propulsion/ScaledConstantThrustPropulsionModel.java @@ -154,6 +154,6 @@ public > FieldVector3D getThrustVector(fina /** {@inheritDoc} */ @Override public > T getFlowRate(final T[] parameters) { - return parameters[0].getField().getZero().add(getInitialFlowRate()); + return parameters[0].getField().getZero().newInstance(getInitialFlowRate()); } } diff --git a/src/main/java/org/orekit/forces/radiation/AbstractLightFluxModel.java b/src/main/java/org/orekit/forces/radiation/AbstractLightFluxModel.java new file mode 100644 index 0000000000..6a79584f8b --- /dev/null +++ b/src/main/java/org/orekit/forces/radiation/AbstractLightFluxModel.java @@ -0,0 +1,135 @@ +/* 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.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.ExtendedPositionProvider; + +/** + * Abstract class for light flux models. + * Via the definition of the lighting ratio and the unocculted flux vector, derives the final value. + * + * @author Romain Serra + * @see LightFluxModel + * @since 12.1 + */ +public abstract class AbstractLightFluxModel implements LightFluxModel { + + /** Direction provider for the occulted body e.g. the Sun. */ + private final ExtendedPositionProvider occultedBody; + + /** + * Constructor. + * @param occultedBody position provider for light source + */ + protected AbstractLightFluxModel(final ExtendedPositionProvider occultedBody) { + this.occultedBody = occultedBody; + } + + /** + * Getter for the occulted body's position provider. + * @return occulted body + */ + public ExtendedPositionProvider getOccultedBody() { + return occultedBody; + } + + /** {@inheritDoc} */ + @Override + public Vector3D getLightFluxVector(final SpacecraftState state) { + final Vector3D position = state.getPosition(); + final Vector3D lightSourcePosition = getOccultedBodyPosition(state.getDate(), state.getFrame()); + final double lightingRatio = getLightingRatio(position, lightSourcePosition); + if (lightingRatio != 0.) { + final Vector3D relativePosition = position.subtract(lightSourcePosition); + final Vector3D unoccultedFlux = getUnoccultedFluxVector(relativePosition); + return unoccultedFlux.scalarMultiply(lightingRatio); + } else { + return Vector3D.ZERO; + } + } + + /** {@inheritDoc} */ + @Override + public > FieldVector3D getLightFluxVector(final FieldSpacecraftState state) { + final FieldVector3D position = state.getPosition(); + final FieldVector3D lightSourcePosition = getOccultedBodyPosition(state.getDate(), state.getFrame()); + final T lightingRatio = getLightingRatio(position, lightSourcePosition); + final FieldVector3D relativePosition = position.subtract(lightSourcePosition); + final FieldVector3D unoccultedFlux = getUnoccultedFluxVector(relativePosition); + return unoccultedFlux.scalarMultiply(lightingRatio); + } + + /** + * Method computing the occulted body's position at a given date and frame. + * @param date date + * @param frame frame + * @return position + */ + protected Vector3D getOccultedBodyPosition(final AbsoluteDate date, final Frame frame) { + return occultedBody.getPosition(date, frame); + } + + /** + * Method computing the occulted body's position at a given date and frame. Field version. + * @param date date + * @param frame frame + * @param field type + * @return position + */ + protected > FieldVector3D getOccultedBodyPosition(final FieldAbsoluteDate date, + final Frame frame) { + return occultedBody.getPosition(date, frame); + } + + /** Get the light flux vector, not considering any shadowing effect. + * @param relativePosition relative position w.r.t. light source + * @return unocculted flux + */ + protected abstract Vector3D getUnoccultedFluxVector(Vector3D relativePosition); + + /** Get the light flux vector, not considering any shadowing effect. Field version. + * @param relativePosition relative position w.r.t. light source + * @param field type + * @return unocculted flux + */ + protected abstract > FieldVector3D getUnoccultedFluxVector(FieldVector3D relativePosition); + + /** Get the lighting ratio ([0-1]). + * @param position object's position + * @param occultedBodyPosition occulted body position in same frame + * @return lighting ratio + */ + protected abstract double getLightingRatio(Vector3D position, Vector3D occultedBodyPosition); + + /** Get the lighting ratio ([0-1]). Field version. + * @param position object's position + * @param occultedBodyPosition occulted body position in same frame + * @param field type + * @return lighting ratio + */ + protected abstract > T getLightingRatio(FieldVector3D position, + FieldVector3D occultedBodyPosition); + +} diff --git a/src/main/java/org/orekit/forces/radiation/AbstractRadiationForceModel.java b/src/main/java/org/orekit/forces/radiation/AbstractRadiationForceModel.java index 9fdb0bf628..53eb7e4f8b 100644 --- a/src/main/java/org/orekit/forces/radiation/AbstractRadiationForceModel.java +++ b/src/main/java/org/orekit/forces/radiation/AbstractRadiationForceModel.java @@ -30,7 +30,6 @@ import org.hipparchus.util.FastMath; import org.hipparchus.util.MathArrays; import org.orekit.bodies.OneAxisEllipsoid; -import org.orekit.forces.ForceModel; import org.orekit.frames.Frame; import org.orekit.propagation.events.EclipseDetector; import org.orekit.propagation.events.EventDetector; @@ -47,7 +46,7 @@ * @see ECOM2 * @since 10.2 */ -public abstract class AbstractRadiationForceModel implements ForceModel { +public abstract class AbstractRadiationForceModel implements RadiationForceModel { /** Margin to force recompute lighting ratio derivatives when we are really inside penumbra. */ private static final double ANGULAR_MARGIN = 1.0e-10; @@ -82,12 +81,6 @@ protected AbstractRadiationForceModel(final ExtendedPVCoordinatesProvider sun, f occultingBodies.add(new OccultationEngine(sun, Constants.SUN_RADIUS, centralBody)); } - /** {@inheritDoc} */ - @Override - public boolean dependsOnPositionOnly() { - return false; - } - /** {@inheritDoc} */ @Override public Stream getEventDetectors() { @@ -109,7 +102,7 @@ public Stream getEventDetectors() { } // Fusion between Date detector for parameter driver span change and // Detector for umbra / penumbra events - return Stream.concat(Stream.of(detectors), ForceModel.super.getEventDetectors()); + return Stream.concat(Stream.of(detectors), RadiationForceModel.super.getEventDetectors()); } /** {@inheritDoc} */ @@ -134,7 +127,7 @@ public > Stream> getFiel withThreshold(zero.newInstance(ECLIPSE_THRESHOLD)). withHandler((state, detector, increasing) -> Action.RESET_DERIVATIVES); } - return Stream.concat(Stream.of(detectors), ForceModel.super.getFieldEventDetectors(field)); + return Stream.concat(Stream.of(detectors), RadiationForceModel.super.getFieldEventDetectors(field)); } /** diff --git a/src/main/java/org/orekit/forces/radiation/CylindricallyShadowedLightFluxModel.java b/src/main/java/org/orekit/forces/radiation/CylindricallyShadowedLightFluxModel.java new file mode 100644 index 0000000000..a4a6a93f54 --- /dev/null +++ b/src/main/java/org/orekit/forces/radiation/CylindricallyShadowedLightFluxModel.java @@ -0,0 +1,180 @@ +/* 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.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.FieldCylindricalShadowEclipseDetector; +import org.orekit.propagation.events.FieldEventDetector; +import org.orekit.utils.ExtendedPositionProvider; + +import java.util.ArrayList; +import java.util.List; + +/** + * Class defining a flux model with a single occulting body, casting a shadow whose shape is a circular cylinder + * (equivalent to the light source being infinitely distant). It is less accurate but faster to evaluate than a conical + * model. + * + * @author Romain Serra + * @see AbstractLightFluxModel + * @see LightFluxModel + * @since 12.1 + */ +public class CylindricallyShadowedLightFluxModel extends AbstractLightFluxModel { + + /** + * Max. check interval for eclipse detection. + */ + private static final double CYLINDRICAL_ECLIPSE_MAX_CHECK = 100; + + /** + * 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 + */ + public CylindricallyShadowedLightFluxModel(final double kRef, final ExtendedPositionProvider occultedBody, + final double occultingBodyRadius) { + super(occultedBody); + this.kRef = kRef; + this.occultingBodyRadius = occultingBodyRadius; + } + + /** + * Constructor with default value for reference flux. + * @param occultedBody position provider for light source + * @param occultingBodyRadius radius of central, occulting body + */ + public CylindricallyShadowedLightFluxModel(final ExtendedPositionProvider occultedBody, + final double occultingBodyRadius) { + this(4.56e-6 * FastMath.pow(149597870000.0, 2), occultedBody, occultingBodyRadius); + } + + /** + * Getter for occulting body radius. + * @return radius + */ + 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); + } + + /** {@inheritDoc} */ + @Override + protected double getLightingRatio(final Vector3D position, final Vector3D occultedBodyPosition) { + final Vector3D occultedBodyDirection = occultedBodyPosition.normalize(); + final double dotProduct = position.dotProduct(occultedBodyDirection); + if (dotProduct < 0.) { + final double distanceToCylinderAxis = (position.subtract(occultedBodyDirection.scalarMultiply(dotProduct))).getNorm(); + if (distanceToCylinderAxis <= occultingBodyRadius) { + return 0.; + } + } + return 1.; + } + + /** {@inheritDoc} */ + @Override + protected > T getLightingRatio(final FieldVector3D position, + final FieldVector3D occultedBodyPosition) { + final Field field = position.getX().getField(); + final FieldVector3D occultedBodyDirection = occultedBodyPosition.normalize(); + final T dotProduct = position.dotProduct(occultedBodyDirection); + if (dotProduct.getReal() < 0.) { + final T distanceToCylinderAxis = (position.subtract(occultedBodyDirection.scalarMultiply(dotProduct))).getNorm(); + if (distanceToCylinderAxis.getReal() <= occultingBodyRadius) { + return field.getZero(); + } + } + return field.getOne(); + } + + + /** {@inheritDoc} */ + @Override + public List getEclipseConditionsDetector() { + final List detectors = new ArrayList<>(); + detectors.add(createCylindricalShadowEclipseDetector() + .withThreshold(CYLINDRICAL_ECLIPSE_THRESHOLD).withMaxCheck(CYLINDRICAL_ECLIPSE_MAX_CHECK)); + return detectors; + } + + /** + * Method to create a new eclipse detector. + * @return detector + */ + private CylindricalShadowEclipseDetector createCylindricalShadowEclipseDetector() { + return new CylindricalShadowEclipseDetector(getOccultedBody(), getOccultingBodyRadius(), + (state, detector, increasing) -> Action.RESET_DERIVATIVES); + } + + /** {@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)); + return detectors; + } + + /** + * Method to create a new eclipse detector. Field version. + * @param field field + * @param field type + * @return detector + */ + private > FieldCylindricalShadowEclipseDetector createFieldCylindricalShadowEclipseDetector(final Field field) { + final T occultingBodyRadiusAsField = field.getZero().newInstance(getOccultingBodyRadius()); + return new FieldCylindricalShadowEclipseDetector<>(getOccultedBody(), occultingBodyRadiusAsField, + (state, detector, increasing) -> Action.RESET_DERIVATIVES); + } +} diff --git a/src/main/java/org/orekit/forces/radiation/KnockeRediffusedForceModel.java b/src/main/java/org/orekit/forces/radiation/KnockeRediffusedForceModel.java index 76d8ec5628..ed96eadaa9 100644 --- a/src/main/java/org/orekit/forces/radiation/KnockeRediffusedForceModel.java +++ b/src/main/java/org/orekit/forces/radiation/KnockeRediffusedForceModel.java @@ -34,9 +34,9 @@ import org.orekit.bodies.OneAxisEllipsoid; import org.orekit.data.DataContext; import org.orekit.forces.ForceModel; -import org.orekit.frames.FieldTransform; +import org.orekit.frames.FieldStaticTransform; import org.orekit.frames.Frame; -import org.orekit.frames.Transform; +import org.orekit.frames.StaticTransform; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; import org.orekit.time.AbsoluteDate; @@ -67,7 +67,7 @@ */ public class KnockeRediffusedForceModel implements ForceModel { - /** Earth rotation around Sun pulsation in rad/sec. */ + /** 7Earth rotation around Sun pulsation in rad/sec. */ private static final double EARTH_AROUND_SUN_PULSATION = MathUtils.TWO_PI / Constants.JULIAN_YEAR; /** Coefficient for solar irradiance computation. */ @@ -113,7 +113,7 @@ public class KnockeRediffusedForceModel implements ForceModel { private final double angularResolution; /** Earth equatorial radius in m. */ - private double equatorialRadius; + private final double equatorialRadius; /** Reference date for periodic terms: December 22nd 1981. * Without more precision, the choice is to set it at midnight, UTC. */ @@ -198,7 +198,7 @@ public Vector3D acceleration(final SpacecraftState s, eastAxisOffset = eastAxisOffset + angularResolution) { // Build rotation transformations to get first crown elementary sector center - final Transform eastRotation = new Transform(date, + final StaticTransform eastRotation = StaticTransform.of(date, new Rotation(east, eastAxisOffset, RotationConvention.VECTOR_OPERATOR)); // Get first elementary crown sector center @@ -210,7 +210,7 @@ public Vector3D acceleration(final SpacecraftState s, radialAxisOffset = radialAxisOffset + angularResolution) { // Build rotation transformations to get elementary area center - final Transform radialRotation = new Transform(date, + final StaticTransform radialRotation = StaticTransform.of(date, new Rotation(projectedToGround, radialAxisOffset, RotationConvention.VECTOR_OPERATOR)); // Get current elementary crown sector center @@ -269,7 +269,7 @@ public > FieldVector3D acceleration(final F eastAxisOffset = eastAxisOffset + angularResolution) { // Build rotation transformations to get first crown elementary sector center - final FieldTransform eastRotation = new FieldTransform<>(date, + final FieldStaticTransform eastRotation = FieldStaticTransform.of(date, new FieldRotation<>(east, zero.add(eastAxisOffset), RotationConvention.VECTOR_OPERATOR)); @@ -283,7 +283,7 @@ public > FieldVector3D acceleration(final F radialAxisOffset = radialAxisOffset + angularResolution) { // Build rotation transformations to get elementary area center - final FieldTransform radialRotation = new FieldTransform<>(date, + final FieldStaticTransform radialRotation = FieldStaticTransform.of(date, new FieldRotation<>(projectedToGround, zero.add(radialAxisOffset), RotationConvention.VECTOR_OPERATOR)); @@ -293,8 +293,8 @@ public > FieldVector3D acceleration(final F // Compute current elementary crown sector area, it results of the integration of an elementary crown sector // over the angular resolution - final T sectorArea = zero.add(equatorialRadius * equatorialRadius * - 2.0 * angularResolution * FastMath.sin(0.5 * angularResolution) * FastMath.sin(eastAxisOffset)); + final T sectorArea = zero.newInstance(equatorialRadius * equatorialRadius * + 2.0 * angularResolution * FastMath.sin(0.5 * angularResolution) * FastMath.sin(eastAxisOffset)); // Add current sector contribution to total rediffused flux rediffusedFlux = rediffusedFlux.add(computeElementaryFlux(s, currentCenter, sunPosition, earth, sectorArea)); @@ -318,7 +318,7 @@ public List getParametersDrivers() { * @param phi the equatorial latitude in rad * @return the albedo in [0;1] */ - private double computeAlbedo(final AbsoluteDate date, final double phi) { + public double computeAlbedo(final AbsoluteDate date, final double phi) { // Get duration since coefficient reference epoch final double deltaT = date.durationFrom(referenceEpoch); @@ -352,7 +352,7 @@ private double computeAlbedo(final AbsoluteDate date, final double phi) { * @param extends CalculusFieldElement * @return the albedo in [0;1] */ - private > T computeAlbedo(final FieldAbsoluteDate date, final T phi) { + public > T computeAlbedo(final FieldAbsoluteDate date, final T phi) { // Get duration since coefficient reference epoch final T deltaT = date.durationFrom(referenceEpoch); @@ -382,7 +382,7 @@ private > T computeAlbedo(final FieldAbsoluteD * @param phi the equatorial latitude in rad * @return the emissivity in [0;1] */ - private double computeEmissivity(final AbsoluteDate date, final double phi) { + public double computeEmissivity(final AbsoluteDate date, final double phi) { // Get duration since coefficient reference epoch final double deltaT = date.durationFrom(referenceEpoch); @@ -416,7 +416,7 @@ private double computeEmissivity(final AbsoluteDate date, final double phi) { * @param extends CalculusFieldElement * @return the emissivity in [0;1] */ - private > T computeEmissivity(final FieldAbsoluteDate date, final T phi) { + public > T computeEmissivity(final FieldAbsoluteDate date, final T phi) { // Get duration since coefficient reference epoch final T deltaT = date.durationFrom(referenceEpoch); @@ -443,7 +443,7 @@ private > T computeEmissivity(final FieldAbsol * @param sunPosition the Sun position in an Earth centered frame * @return the total solar flux impacting Earth in J/m^3 */ - private double computeSolarFlux(final Vector3D sunPosition) { + public double computeSolarFlux(final Vector3D sunPosition) { // Compute Earth - Sun distance in UA final double earthSunDistance = sunPosition.getNorm() / Constants.JPL_SSD_ASTRONOMICAL_UNIT; @@ -458,7 +458,7 @@ private double computeSolarFlux(final Vector3D sunPosition) { * @param extends CalculusFieldElement * @return the total solar flux impacting Earth in J/m^3 */ - private > T computeSolarFlux(final FieldVector3D sunPosition) { + public > T computeSolarFlux(final FieldVector3D sunPosition) { // Compute Earth - Sun distance in UA final T earthSunDistance = sunPosition.getNorm().divide(Constants.JPL_SSD_ASTRONOMICAL_UNIT); @@ -476,11 +476,11 @@ private > T computeSolarFlux(final FieldVector * @param elementArea the area of the current element * @return the rediffused flux from considered element on the spacecraft */ - private Vector3D computeElementaryFlux(final SpacecraftState state, - final Vector3D elementCenter, - final Vector3D sunPosition, - final OneAxisEllipsoid earth, - final double elementArea) { + public Vector3D computeElementaryFlux(final SpacecraftState state, + final Vector3D elementCenter, + final Vector3D sunPosition, + final OneAxisEllipsoid earth, + final double elementArea) { // Get satellite position final Vector3D satellitePosition = state.getPosition(); @@ -550,11 +550,11 @@ private Vector3D computeElementaryFlux(final SpacecraftState state, * @param extends CalculusFieldElement * @return the rediffused flux from considered element on the spacecraft */ - private > FieldVector3D computeElementaryFlux(final FieldSpacecraftState state, - final FieldVector3D elementCenter, - final FieldVector3D sunPosition, - final OneAxisEllipsoid earth, - final T elementArea) { + public > FieldVector3D computeElementaryFlux(final FieldSpacecraftState state, + final FieldVector3D elementCenter, + final FieldVector3D sunPosition, + final OneAxisEllipsoid earth, + final T elementArea) { // Get satellite position final FieldVector3D satellitePosition = state.getPosition(); @@ -604,7 +604,7 @@ private > FieldVector3D computeElementaryFl // Compute attenuated projected elemetary area vector final FieldVector3D projectedAreaVector = r.scalarMultiply(elementArea.multiply(FastMath.cos(alpha)).divide( - rNorm.multiply(rNorm).multiply(rNorm).multiply(zero.getPi()))); + rNorm.square().multiply(rNorm).multiply(zero.getPi()))); // Compute elementary radiation flux from current elementary area return projectedAreaVector.scalarMultiply(albedoAndIR.divide(Constants.SPEED_OF_LIGHT)); @@ -612,7 +612,7 @@ private > FieldVector3D computeElementaryFl } else { // Spacecraft does not see the elementary area - return new FieldVector3D(zero, zero, zero); + return new FieldVector3D<>(zero, zero, zero); } } diff --git a/src/main/java/org/orekit/forces/radiation/LightFluxModel.java b/src/main/java/org/orekit/forces/radiation/LightFluxModel.java new file mode 100644 index 0000000000..2e3b30e257 --- /dev/null +++ b/src/main/java/org/orekit/forces/radiation/LightFluxModel.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.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.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.events.EventDetector; +import org.orekit.propagation.events.FieldEventDetector; + +import java.util.List; + +/** + * Interface describing flux models from a light source, including shadowing effects from occulting bodies. + * Defines the flux vector itself as well as detectors for entry and exit of the different eclipse zones, if any. + * + * @author Romain Serra + * @since 12.1 + */ +public interface LightFluxModel { + + /** + * Get the light flux vector in the state's frame. + * @param state state + * @return light flux + */ + Vector3D getLightFluxVector(SpacecraftState state); + + /** + * Get the light flux vector in the state's frame. Field version. + * @param state state + * @return light flux + * @param field type + */ + > FieldVector3D getLightFluxVector(FieldSpacecraftState state); + + /** + * Retrieve detectors finding entries and exits in different eclipse zones. + * @return list of event detectors + */ + List getEclipseConditionsDetector(); + + /** + * Retrieve Field detectors finding entries and exits in different eclipse zones. + * @param field calculus field + * @return list of event detectors + * @param field type + */ + > List> getFieldEclipseConditionsDetector(Field field); +} diff --git a/src/main/java/org/orekit/forces/radiation/RadiationForceModel.java b/src/main/java/org/orekit/forces/radiation/RadiationForceModel.java new file mode 100644 index 0000000000..eff6afaa8a --- /dev/null +++ b/src/main/java/org/orekit/forces/radiation/RadiationForceModel.java @@ -0,0 +1,35 @@ +/* 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.orekit.forces.ForceModel; + +/** + * Interface for radiation-related force models. + * + * @author Romain Serra + * @since 12.1 + */ +public interface RadiationForceModel extends ForceModel { + + /** {@inheritDoc} */ + @Override + default boolean dependsOnPositionOnly() { + return false; + } + +} diff --git a/src/main/java/org/orekit/forces/radiation/RadiationPressureModel.java b/src/main/java/org/orekit/forces/radiation/RadiationPressureModel.java new file mode 100644 index 0000000000..f07f704fc3 --- /dev/null +++ b/src/main/java/org/orekit/forces/radiation/RadiationPressureModel.java @@ -0,0 +1,123 @@ +/* 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.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.events.EventDetector; +import org.orekit.propagation.events.FieldEventDetector; +import org.orekit.utils.ParameterDriver; + +import java.util.List; +import java.util.stream.Stream; + +/** + * Class representing a light-induced radiation pressure force, by leveraging on a given flux model. + * + *

    + * This class should not be used in addition to {@link SolarRadiationPressure}, which is another way of + * representing the same orbital perturbation. + *

    + * + * @author Romain Serra + * @see LightFluxModel + * @see RadiationSensitive + * @since 12.1 + */ +public class RadiationPressureModel implements RadiationForceModel { + + /** + * Light flux model (including eclipse conditions). + */ + private final LightFluxModel lightFluxModel; + + /** + * Object defining radiation properties defining the acceleration from the pressure. + */ + private final RadiationSensitive radiationSensitive; + + /** + * Constructor. + * @param lightFluxModel model for light flux + * @param radiationSensitive object defining radiation properties + */ + public RadiationPressureModel(final LightFluxModel lightFluxModel, + final RadiationSensitive radiationSensitive) { + this.lightFluxModel = lightFluxModel; + this.radiationSensitive = radiationSensitive; + } + + /** + * Getter for radiation sensitive object. + * @return radiation sensitive object + */ + public RadiationSensitive getRadiationSensitive() { + return radiationSensitive; + } + + /** + * Getter for light flux model. + * @return flux model + */ + public LightFluxModel getLightFluxModel() { + return lightFluxModel; + } + + /** {@inheritDoc} */ + @Override + public boolean dependsOnPositionOnly() { + return radiationSensitive instanceof IsotropicRadiationClassicalConvention || radiationSensitive instanceof IsotropicRadiationCNES95Convention || radiationSensitive instanceof IsotropicRadiationSingleCoefficient; + } + + /** {@inheritDoc} */ + @Override + public Vector3D acceleration(final SpacecraftState s, final double[] parameters) { + final Vector3D fluxVector = lightFluxModel.getLightFluxVector(s); + return radiationSensitive.radiationPressureAcceleration(s, fluxVector, parameters); + } + + /** {@inheritDoc} */ + @Override + public > FieldVector3D acceleration(final FieldSpacecraftState s, final T[] parameters) { + final FieldVector3D fluxVector = lightFluxModel.getLightFluxVector(s); + return radiationSensitive.radiationPressureAcceleration(s, fluxVector, parameters); + } + + /** {@inheritDoc} */ + @Override + public List getParametersDrivers() { + return radiationSensitive.getRadiationParametersDrivers(); + } + + /** {@inheritDoc} */ + @Override + public Stream getEventDetectors() { + final List eventDetectors = lightFluxModel.getEclipseConditionsDetector(); + return Stream.concat(RadiationForceModel.super.getEventDetectors(), eventDetectors.stream()); + } + + /** {@inheritDoc} */ + @Override + public > Stream> getFieldEventDetectors(final Field field) { + final List> eventDetectors = lightFluxModel.getFieldEclipseConditionsDetector(field); + return Stream.concat(RadiationForceModel.super.getFieldEventDetectors(field), eventDetectors.stream()); + } +} diff --git a/src/main/java/org/orekit/forces/radiation/SolarRadiationPressure.java b/src/main/java/org/orekit/forces/radiation/SolarRadiationPressure.java index 3e7ec5c4e7..58b55a68cf 100644 --- a/src/main/java/org/orekit/forces/radiation/SolarRadiationPressure.java +++ b/src/main/java/org/orekit/forces/radiation/SolarRadiationPressure.java @@ -16,9 +16,6 @@ */ 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; @@ -36,6 +33,9 @@ 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 @@ -118,6 +118,21 @@ public SolarRadiationPressure(final double dRef, final double pRef, this.spacecraft = spacecraft; } + /** + * Getter for radiation-sensitive spacecraft. + * @return radiation-sensitive model + * @since 12.1 + */ + public RadiationSensitive getRadiationSensitiveSpacecraft() { + return spacecraft; + } + + /** {@inheritDoc} */ + @Override + public boolean dependsOnPositionOnly() { + return spacecraft instanceof IsotropicRadiationClassicalConvention || spacecraft instanceof IsotropicRadiationCNES95Convention || spacecraft instanceof IsotropicRadiationSingleCoefficient; + } + /** {@inheritDoc} */ @Override public Vector3D acceleration(final SpacecraftState s, final double[] parameters) { diff --git a/src/main/java/org/orekit/frames/EOPHistory.java b/src/main/java/org/orekit/frames/EOPHistory.java index cce3ca9ed1..733b668124 100644 --- a/src/main/java/org/orekit/frames/EOPHistory.java +++ b/src/main/java/org/orekit/frames/EOPHistory.java @@ -405,7 +405,7 @@ public void accept(final EOPEntry neighbor) { dut = neighbor.getUT1MinusUTC(); } final T[] array = MathArrays.buildArray(date.getField(), 1); - array[0] = date.getField().getZero().add(dut); + array[0] = date.getField().getZero().newInstance(dut); interpolator.addSamplePoint(date.durationFrom(neighbor.getDate()).negate(), array); } @@ -745,7 +745,7 @@ private > T interpolate(final FieldAbsoluteDat // if T was DerivativeStructure getNeighbors(aDate, interpolationDegree + 1). forEach(entry -> { - y[0] = zero.add(selector.apply(entry)); + y[0] = zero.newInstance(selector.apply(entry)); interpolator.addSamplePoint(central.durationFrom(entry.getDate()).negate(), y); }); return interpolator.value(date.durationFrom(central))[0]; // here, we introduce derivatives again (in DerivativeStructure case) @@ -842,8 +842,8 @@ private > T[] interpolate(final FieldAbsoluteD // if T was DerivativeStructure getNeighbors(aDate, interpolationDegree + 1). forEach(entry -> { - y[0] = zero.add(selector1.apply(entry)); - y[1] = zero.add(selector2.apply(entry)); + y[0] = zero.newInstance(selector1.apply(entry)); + y[1] = zero.newInstance(selector2.apply(entry)); interpolator.addSamplePoint(central.durationFrom(entry.getDate()).negate(), y); }); return interpolator.value(date.durationFrom(central)); // here, we introduce derivatives again (in DerivativeStructure case) @@ -1052,7 +1052,7 @@ public > T[] value(final FieldAbsoluteDate // if T was DerivativeStructure cache.getNeighbors(aDate).forEach(entry -> { for (int i = 0; i < y.length; ++i) { - y[i] = zero.add(entry.correction[i]); + y[i] = zero.newInstance(entry.correction[i]); } interpolator.addSamplePoint(central.durationFrom(entry.getDate()).negate(), y); }); diff --git a/src/main/java/org/orekit/frames/FieldKinematicTransform.java b/src/main/java/org/orekit/frames/FieldKinematicTransform.java new file mode 100644 index 0000000000..c733fccb8e --- /dev/null +++ b/src/main/java/org/orekit/frames/FieldKinematicTransform.java @@ -0,0 +1,287 @@ +/* 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.frames; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.geometry.euclidean.threed.FieldRotation; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldPVCoordinates; +import org.orekit.utils.PVCoordinates; +import org.orekit.utils.TimeStampedFieldPVCoordinates; +import org.orekit.utils.TimeStampedPVCoordinates; + +/** + * 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. + * + * @author Romain Serra + * @see FieldStaticTransform + * @see FieldTransform + * @see KinematicTransform + * @since 12.1 + */ +public interface FieldKinematicTransform> extends FieldStaticTransform { + + /** + * Get the identity kinematic transform. + * + * @param type of the elements + * @param field field used by default + * @return identity transform. + */ + static > FieldKinematicTransform getIdentity(final Field field) { + return FieldTransform.getIdentity(field); + } + + /** Compute a composite velocity. + * @param first first applied transform + * @param second second applied transform + * @param the type of the field elements + * @return velocity part of the composite transform + */ + static > FieldVector3D compositeVelocity(final FieldKinematicTransform first, + final FieldKinematicTransform second) { + + final FieldVector3D v1 = first.getVelocity(); + final FieldRotation r1 = first.getRotation(); + final FieldVector3D o1 = first.getRotationRate(); + final FieldVector3D p2 = second.getTranslation(); + final FieldVector3D v2 = second.getVelocity(); + + final FieldVector3D crossP = FieldVector3D.crossProduct(o1, p2); + + return v1.add(r1.applyInverseTo(v2.add(crossP))); + } + + /** Compute a composite rotation rate. + * @param type of the elements + * @param first first applied transform + * @param second second applied transform + * @return rotation rate part of the composite transform + */ + static > FieldVector3D compositeRotationRate(final FieldKinematicTransform first, + final FieldKinematicTransform second) { + + final FieldVector3D o1 = first.getRotationRate(); + final FieldRotation r2 = second.getRotation(); + final FieldVector3D o2 = second.getRotationRate(); + + return o2.add(r2.applyTo(o1)); + } + + /** Transform {@link PVCoordinates}, without the acceleration vector. + * @param pv the position-velocity couple to transform. + * @return transformed position-velocity + */ + default FieldPVCoordinates transformOnlyPV(final FieldPVCoordinates pv) { + final FieldVector3D transformedP = transformPosition(pv.getPosition()); + final FieldVector3D crossP = FieldVector3D.crossProduct(getRotationRate(), transformedP); + final FieldVector3D transformedV = getRotation().applyTo(pv.getVelocity().add(getVelocity())).subtract(crossP); + return new FieldPVCoordinates<>(transformedP, transformedV); + } + + /** Transform {@link TimeStampedPVCoordinates}, without the acceleration vector. + *

    + * In order to allow the user more flexibility, this method does not check for + * consistency between the transform {@link #getDate() date} and the time-stamped + * position-velocity {@link TimeStampedPVCoordinates#getDate() date}. The returned + * value will always have the same {@link TimeStampedPVCoordinates#getDate() date} as + * the input argument, regardless of the instance {@link #getDate() date}. + *

    + * @param pv the position-velocity couple to transform. + * @return transformed position-velocity + */ + default TimeStampedFieldPVCoordinates transformOnlyPV(final TimeStampedFieldPVCoordinates pv) { + final FieldVector3D transformedP = transformPosition(pv.getPosition()); + final FieldVector3D crossP = FieldVector3D.crossProduct(getRotationRate(), transformedP); + final FieldVector3D transformedV = getRotation().applyTo(pv.getVelocity().add(getVelocity())).subtract(crossP); + return new TimeStampedFieldPVCoordinates<>(pv.getDate(), transformedP, transformedV, + FieldVector3D.getZero(pv.getDate().getField())); + } + + /** Get the first time derivative of the translation. + * @return first time derivative of the translation + * @see #getTranslation() + */ + FieldVector3D getVelocity(); + + /** Get the first time derivative of the rotation. + *

    The norm represents the angular rate.

    + * @return First time derivative of the rotation + * @see #getRotation() + */ + FieldVector3D getRotationRate(); + + /** + * Get the inverse transform of the instance. + * + * @return inverse transform of the instance + */ + FieldKinematicTransform getInverse(); + + /** + * Build a transform by combining two existing ones. + *

    + * Note that the dates of the two existing transformed are ignored, + * and the combined transform date is set to the date supplied in this + * constructor without any attempt to shift the raw transforms. This is a + * design choice allowing user full control of the combination. + *

    + * + * @param type of the elements + * @param date date of the transform + * @param first first transform applied + * @param second second transform applied + * @return the newly created kinematic transform that has the same effect as + * applying {@code first}, then {@code second}. + * @see #of(FieldAbsoluteDate, FieldPVCoordinates, FieldRotation, FieldVector3D) + */ + static > FieldKinematicTransform compose(final FieldAbsoluteDate date, + final FieldKinematicTransform first, + final FieldKinematicTransform second) { + final FieldVector3D composedTranslation = FieldStaticTransform.compositeTranslation(first, second); + final FieldVector3D composedTranslationRate = FieldKinematicTransform.compositeVelocity(first, second); + return of(date, new FieldPVCoordinates<>(composedTranslation, composedTranslationRate), + FieldStaticTransform.compositeRotation(first, second), + FieldKinematicTransform.compositeRotationRate(first, second)); + } + + /** + * Create a new kinematic transform from a rotation and zero, constant translation. + * + * @param type of the elements + * @param date of translation. + * @param rotation to apply after the translation. That is after translating + * applying this rotation produces positions expressed in + * the new frame. + * @param rotationRate rate of rotation + * @return the newly created kinematic transform. + * @see #of(FieldAbsoluteDate, FieldPVCoordinates, FieldRotation, FieldVector3D) + */ + static > FieldKinematicTransform of(final FieldAbsoluteDate date, + final FieldRotation rotation, + final FieldVector3D rotationRate) { + return of(date, FieldPVCoordinates.getZero(date.getField()), rotation, rotationRate); + } + + /** + * Create a new kinematic transform from a translation and its rate. + * + * @param type of the elements + * @param date of translation. + * @param pvCoordinates translation (with rate) to apply, expressed in the old frame. That is, the + * opposite of the coordinates of the new origin in the + * old frame. + * @return the newly created kinematic transform. + * @see #of(FieldAbsoluteDate, FieldPVCoordinates, FieldRotation, FieldVector3D) + */ + static > FieldKinematicTransform of(final FieldAbsoluteDate date, + final FieldPVCoordinates pvCoordinates) { + final Field field = date.getField(); + return of(date, pvCoordinates, FieldRotation.getIdentity(field), FieldVector3D.getZero(field)); + } + + /** + * Create a new kinematic transform from a non-Field version. + * + * @param type of the elements + * @param field field. + * @param kinematicTransform non-Field kinematic transform + * @return the newly created kinematic transform. + * @see #of(FieldAbsoluteDate, FieldPVCoordinates, FieldRotation, FieldVector3D) + */ + static > FieldKinematicTransform of(final Field field, + final KinematicTransform kinematicTransform) { + final FieldAbsoluteDate date = new FieldAbsoluteDate<>(field, kinematicTransform.getDate()); + final FieldPVCoordinates pvCoordinates = new FieldPVCoordinates<>(field, + new PVCoordinates(kinematicTransform.getTranslation(), kinematicTransform.getVelocity())); + final FieldRotation rotation = new FieldRotation<>(field, kinematicTransform.getRotation()); + final FieldVector3D rotationRate = new FieldVector3D<>(field, kinematicTransform.getRotationRate()); + return of(date, pvCoordinates, rotation, rotationRate); + } + + /** + * Create a new kinematic transform from a translation and rotation. + * + * @param type of the elements + * @param date of translation. + * @param pvCoordinates translation (with rate) to apply, expressed in the old frame. That is, the + * opposite of the coordinates of the new origin in the + * old frame. + * @param rotation to apply after the translation. That is after + * translating applying this rotation produces positions + * expressed in the new frame. + * @param rotationRate rate of rotation + * @return the newly created kinematic transform. + * @see #compose(FieldAbsoluteDate, FieldKinematicTransform, FieldKinematicTransform) + * @see #of(FieldAbsoluteDate, FieldPVCoordinates, FieldRotation, FieldVector3D) + * @see #of(FieldAbsoluteDate, FieldPVCoordinates, FieldRotation, FieldVector3D) + */ + static > FieldKinematicTransform of(final FieldAbsoluteDate date, + final FieldPVCoordinates pvCoordinates, + final FieldRotation rotation, + final FieldVector3D rotationRate) { + return new FieldKinematicTransform() { + + @Override + public FieldKinematicTransform getInverse() { + final FieldRotation r = getRotation(); + final FieldVector3D rp = r.applyTo(getTranslation()); + final FieldVector3D pInv = rp.negate(); + final FieldVector3D crossP = FieldVector3D.crossProduct(getRotationRate(), rp); + final FieldVector3D vInv = crossP.subtract(getRotation().applyTo(getVelocity())); + final FieldRotation rInv = r.revert(); + return FieldKinematicTransform.of(date, new FieldPVCoordinates<>(pInv, vInv), + rInv, rInv.applyTo(getRotationRate()).negate()); + } + + @Override + public AbsoluteDate getDate() { + return date.toAbsoluteDate(); + } + + @Override + public FieldAbsoluteDate getFieldDate() { + return date; + } + + @Override + public FieldVector3D getTranslation() { + return pvCoordinates.getPosition(); + } + + @Override + public FieldRotation getRotation() { + return rotation; + } + + @Override + public FieldVector3D getVelocity() { + return pvCoordinates.getVelocity(); + } + + @Override + public FieldVector3D getRotationRate() { + return rotationRate; + } + }; + } + +} diff --git a/src/main/java/org/orekit/frames/FieldStaticTransform.java b/src/main/java/org/orekit/frames/FieldStaticTransform.java index c1f800cf55..a2d7cb8cb8 100644 --- a/src/main/java/org/orekit/frames/FieldStaticTransform.java +++ b/src/main/java/org/orekit/frames/FieldStaticTransform.java @@ -114,6 +114,16 @@ default FieldLine transformLine(final FieldLine line) { return new FieldLine<>(transformedP0, transformedP1, line.getTolerance()); } + /** Get the Field date. + * This default implementation is there so that no API is broken by a minor release. + * It is overloaded by native inheritors and shall be removed in the next major release. + * @return Field date attached to the object + * @since 12.1 + */ + default FieldAbsoluteDate getFieldDate() { + return new FieldAbsoluteDate<>(getTranslation().getX().getField(), getDate()); + } + /** * Get the underlying elementary translation. *

    A transform can be uniquely represented as an elementary @@ -141,6 +151,20 @@ default FieldLine transformLine(final FieldLine line) { */ FieldStaticTransform getInverse(); + /** + * Get the inverse transform of the instance in static form (without rates). + * This enables to create a purely static inverse, as inheritors such as {@link FieldTransform} may + * have a relatively computationally-heavy #getInverse() method. + * + * @return inverse static transform of the instance + * @since 12.1 + */ + default FieldStaticTransform getStaticInverse() { + final FieldVector3D negatedTranslation = getTranslation().negate(); + final FieldRotation rotation = getRotation(); + return FieldStaticTransform.of(getFieldDate(), rotation.applyTo(negatedTranslation), rotation.revert()); + } + /** * Build a transform by combining two existing ones. *

    @@ -282,6 +306,11 @@ public AbsoluteDate getDate() { return date.toAbsoluteDate(); } + @Override + public FieldAbsoluteDate getFieldDate() { + return date; + } + @Override public FieldVector3D getTranslation() { return translation; diff --git a/src/main/java/org/orekit/frames/FieldTransform.java b/src/main/java/org/orekit/frames/FieldTransform.java index ab9ca4c116..35719499d8 100644 --- a/src/main/java/org/orekit/frames/FieldTransform.java +++ b/src/main/java/org/orekit/frames/FieldTransform.java @@ -101,7 +101,7 @@ * @since 9.0 */ public class FieldTransform> - implements FieldTimeShiftable, T>, FieldStaticTransform { + implements FieldTimeShiftable, T>, FieldKinematicTransform { /** Date of the transform. */ private final FieldAbsoluteDate date; @@ -163,9 +163,7 @@ public FieldTransform(final FieldAbsoluteDate date, final FieldVector3D tr public FieldTransform(final FieldAbsoluteDate date, final FieldRotation rotation) { this(date, date.toAbsoluteDate(), FieldPVCoordinates.getZero(date.getField()), - new FieldAngularCoordinates<>(rotation, - FieldVector3D.getZero(date.getField()), - FieldVector3D.getZero(date.getField()))); + new FieldAngularCoordinates<>(rotation, FieldVector3D.getZero(date.getField()))); } /** Build a translation transform, with its first time derivative. @@ -213,6 +211,22 @@ public FieldTransform(final FieldAbsoluteDate date, final FieldPVCoordinates< FieldAngularCoordinates.getIdentity(date.getField())); } + /** Build a combined translation and rotation transform. + * @param date date of the transform + * @param translation translation to apply (i.e. coordinates of + * the transformed origin, or coordinates of the origin of the + * old frame in the new frame) + * @param rotation rotation to apply ( i.e. rotation to apply to the + * coordinates of a vector expressed in the old frame to obtain the + * same vector expressed in the new frame ) + * @since 12.1 + */ + public FieldTransform(final FieldAbsoluteDate date, final FieldVector3D translation, + final FieldRotation rotation) { + this(date, date.toAbsoluteDate(), new FieldPVCoordinates<>(translation, FieldVector3D.getZero(date.getField())), + new FieldAngularCoordinates<>(rotation, FieldVector3D.getZero(date.getField()))); + } + /** Build a rotation transform. * @param date date of the transform * @param rotation rotation to apply ( i.e. rotation to apply to the @@ -379,6 +393,7 @@ public AbsoluteDate getDate() { /** Get the date. * @return date attached to the object */ + @Override public FieldAbsoluteDate getFieldDate() { return date; } diff --git a/src/main/java/org/orekit/frames/FixedTransformProvider.java b/src/main/java/org/orekit/frames/FixedTransformProvider.java index 95fcf196c2..7b21012657 100644 --- a/src/main/java/org/orekit/frames/FixedTransformProvider.java +++ b/src/main/java/org/orekit/frames/FixedTransformProvider.java @@ -59,11 +59,9 @@ public Transform getTransform(final AbsoluteDate date) { public > FieldTransform getTransform(final FieldAbsoluteDate date) { @SuppressWarnings("unchecked") - FieldTransform ft = (FieldTransform) cached.get(date.getField()); - if (ft == null) { - ft = new FieldTransform<>(date.getField(), transform); - cached.put(date.getField(), ft); - } + final FieldTransform ft = + (FieldTransform) cached.computeIfAbsent(date.getField(), + f -> new FieldTransform<>((Field) f, transform)); return ft; diff --git a/src/main/java/org/orekit/frames/Frame.java b/src/main/java/org/orekit/frames/Frame.java index 8d4541d348..de5ce25b39 100644 --- a/src/main/java/org/orekit/frames/Frame.java +++ b/src/main/java/org/orekit/frames/Frame.java @@ -267,6 +267,30 @@ public > FieldTransform getTransformTo(fina FieldTransform::getInverse); } + /** + * Get the kinematic portion of the transform from the instance to another + * frame. The returned transform is kinematic in the sense that it includes + * translations and rotations, with rates, but cannot transform an acceleration vector. + * + *

    This method is often more performant than {@link + * #getTransformTo(Frame, AbsoluteDate)} when accelerations are not needed. + * + * @param destination destination frame to which we want to transform + * vectors + * @param date the date (can be null if it is sure than no date + * dependent frame is used) + * @return kinematic transform from the instance to the destination frame + * @since 12.1 + */ + public KinematicTransform getKinematicTransformTo(final Frame destination, final AbsoluteDate date) { + return getTransformTo( + destination, + KinematicTransform.getIdentity(), + frame -> frame.getTransformProvider().getKinematicTransform(date), + (t1, t2) -> KinematicTransform.compose(date, t1, t2), + KinematicTransform::getInverse); + } + /** * Get the static portion of the transform from the instance to another * frame. The returned transform is static in the sense that it includes @@ -328,6 +352,38 @@ public > FieldStaticTransform getStaticTran } } + /** + * Get the kinematic portion of the transform from the instance to another + * frame. The returned transform is kinematic in the sense that it includes + * translations and rotations, with rates, but cannot transform an acceleration vector. + * + *

    This method is often more performant than {@link + * #getTransformTo(Frame, AbsoluteDate)} when accelerations are not needed. + * @param Type of transform returned. + * @param destination destination frame to which we want to transform + * vectors + * @param date the date (must be non-null, which is a more stringent condition + * * than in {@link #getKinematicTransformTo(Frame, AbsoluteDate)}) + * @return kinematic transform from the instance to the destination frame + * @since 12.1 + */ + public > FieldKinematicTransform getKinematicTransformTo(final Frame destination, + final FieldAbsoluteDate date) { + if (date.hasZeroField()) { + // If date field is Zero, then use the un-fielded version for performances + final KinematicTransform kinematicTransform = getKinematicTransformTo(destination, date.toAbsoluteDate()); + return FieldKinematicTransform.of(date.getField(), kinematicTransform); + + } else { + // Use classic fielded function + return getTransformTo(destination, + FieldKinematicTransform.getIdentity(date.getField()), + frame -> frame.getTransformProvider().getKinematicTransform(date), + (t1, t2) -> FieldKinematicTransform.compose(date, t1, t2), + FieldKinematicTransform::getInverse); + } + } + /** * Generic get transform method that builds the transform from {@code this} * to {@code destination}. diff --git a/src/main/java/org/orekit/frames/GTODProvider.java b/src/main/java/org/orekit/frames/GTODProvider.java index c88d843273..e66787b2ef 100644 --- a/src/main/java/org/orekit/frames/GTODProvider.java +++ b/src/main/java/org/orekit/frames/GTODProvider.java @@ -109,68 +109,91 @@ public GTODProvider getNonInterpolatingProvider() { /** {@inheritDoc} */ @Override public Transform getTransform(final AbsoluteDate date) { + return new Transform(date, getRotation(date), getRotationRate(date)); + } - // compute Greenwich apparent sidereal time, in radians - final double gast = gastFunction.value(date); - - // compute true angular rotation of Earth, in rad/s - final double lod = (eopHistory == null) ? 0.0 : eopHistory.getLOD(date); - final double omp = AVE * (1 - lod / Constants.JULIAN_DAY); - final Vector3D rotationRate = new Vector3D(omp, Vector3D.PLUS_K); - - // set up the transform from parent TOD - return new Transform(date, new Rotation(Vector3D.PLUS_K, gast, RotationConvention.FRAME_TRANSFORM), rotationRate); - + /** {@inheritDoc} */ + @Override + public KinematicTransform getKinematicTransform(final AbsoluteDate date) { + return KinematicTransform.of(date, getRotation(date), getRotationRate(date)); } /** {@inheritDoc} */ @Override public StaticTransform getStaticTransform(final AbsoluteDate date) { + return StaticTransform.of(date, getRotation(date)); + } + /** Form rotation to parent TOD. + * @param date transform date + * @return rotation to parent at date + * @since 12.1 + */ + private Rotation getRotation(final AbsoluteDate date) { // compute Greenwich apparent sidereal time, in radians final double gast = gastFunction.value(date); // set up the transform from parent TOD - return StaticTransform.of( - date, - new Rotation(Vector3D.PLUS_K, gast, RotationConvention.FRAME_TRANSFORM)); + return new Rotation(Vector3D.PLUS_K, gast, RotationConvention.FRAME_TRANSFORM); + } + /** Form rotation rate w.r.t. parent TOD. + * @param date transform date + * @return rotation rate at date + * @since 12.1 + */ + private Vector3D getRotationRate(final AbsoluteDate date) { + // compute true angular rotation of Earth, in rad/s + final double lod = (eopHistory == null) ? 0.0 : eopHistory.getLOD(date); + final double omp = AVE * (1 - lod / Constants.JULIAN_DAY); + return new Vector3D(omp, Vector3D.PLUS_K); } /** {@inheritDoc} */ @Override public > FieldTransform getTransform(final FieldAbsoluteDate date) { + return new FieldTransform<>(date, getRotation(date), getRotationRate(date)); + } - // compute Greenwich apparent sidereal time, in radians - final T gast = gastFunction.value(date); - - // compute true angular rotation of Earth, in rad/s - final T lod = (eopHistory == null) ? date.getField().getZero() : eopHistory.getLOD(date); - final T omp = lod.multiply(-1.0 / Constants.JULIAN_DAY).add(1).multiply(AVE); - final FieldVector3D rotationRate = new FieldVector3D<>(date.getField().getZero(), - date.getField().getZero(), - date.getField().getZero().add(omp)); - - // set up the transform from parent TOD - return new FieldTransform<>(date, - new FieldRotation<>(FieldVector3D.getPlusK(date.getField()), - gast, RotationConvention.FRAME_TRANSFORM), - rotationRate); - + /** {@inheritDoc} */ + @Override + public > FieldKinematicTransform getKinematicTransform(final FieldAbsoluteDate date) { + return FieldKinematicTransform.of(date, getRotation(date), getRotationRate(date)); } /** {@inheritDoc} */ @Override public > FieldStaticTransform getStaticTransform(final FieldAbsoluteDate date) { + return FieldStaticTransform.of(date, getRotation(date)); + } + /** Form rotation to parent TOD. + * @param type of the elements + * @param date transform date + * @return rotation to parent at date + * @since 12.1 + */ + private > FieldRotation getRotation(final FieldAbsoluteDate date) { // compute Greenwich apparent sidereal time, in radians final T gast = gastFunction.value(date); // set up the transform from parent TOD - return FieldStaticTransform.of( - date, - new FieldRotation<>(FieldVector3D.getPlusK(date.getField()), gast, RotationConvention.FRAME_TRANSFORM)); + return new FieldRotation<>(FieldVector3D.getPlusK(date.getField()), gast, RotationConvention.FRAME_TRANSFORM); + } + /** Form rotation rate w.r.t. parent TOD. + * @param type of the elements + * @param date transform date + * @return rotation rate at date + * @since 12.1 + */ + private > FieldVector3D getRotationRate(final FieldAbsoluteDate date) { + // compute true angular rotation of Earth, in rad/s + final T lod = (eopHistory == null) ? date.getField().getZero() : eopHistory.getLOD(date); + final T omp = lod.multiply(-1.0 / Constants.JULIAN_DAY).add(1).multiply(AVE); + return new FieldVector3D<>(date.getField().getZero(), + date.getField().getZero(), + date.getField().getZero().add(omp)); } /** Replace the instance with a data transfer object for serialization. diff --git a/src/main/java/org/orekit/frames/ITRFVersion.java b/src/main/java/org/orekit/frames/ITRFVersion.java index cdfc3d0c63..bee98eb411 100644 --- a/src/main/java/org/orekit/frames/ITRFVersion.java +++ b/src/main/java/org/orekit/frames/ITRFVersion.java @@ -115,7 +115,7 @@ public String getName() { */ public static ITRFVersion getITRFVersion(final int year) { - final int fixedYear = (year > 87 && year < 100) ? (year + 1900) : year; + final int fixedYear = (year < 100) ? (year + (year > 87 ? 1900 : 2000)) : year; // loop over all predefined frames versions for (final ITRFVersion version : values()) { @@ -287,6 +287,13 @@ public Transform getTransform(final AbsoluteDate date) { return provider.getTransform(date); } + /** {@inheritDoc} */ + @Override + public KinematicTransform getKinematicTransform(final AbsoluteDate date) { + return provider.getKinematicTransform(date); + } + + /** {@inheritDoc} */ @Override public StaticTransform getStaticTransform(final AbsoluteDate date) { return provider.getStaticTransform(date); @@ -298,6 +305,12 @@ public > FieldTransform getTransform(final return provider.getTransform(date); } + /** {@inheritDoc} */ + @Override + public > FieldKinematicTransform getKinematicTransform(final FieldAbsoluteDate date) { + return provider.getKinematicTransform(date); + } + /** {@inheritDoc} */ @Override public > FieldStaticTransform getStaticTransform(final FieldAbsoluteDate date) { diff --git a/src/main/java/org/orekit/frames/KinematicTransform.java b/src/main/java/org/orekit/frames/KinematicTransform.java new file mode 100644 index 0000000000..c23a1fc4ef --- /dev/null +++ b/src/main/java/org/orekit/frames/KinematicTransform.java @@ -0,0 +1,243 @@ +/* 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.frames; + +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.geometry.euclidean.threed.Rotation; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.PVCoordinates; +import org.orekit.utils.TimeStampedPVCoordinates; + +/** + * 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. + * + * @author Romain Serra + * @see StaticTransform + * @see Transform + * @since 12.1 + */ +public interface KinematicTransform extends StaticTransform { + + /** + * Get the identity kinematic transform. + * + * @return identity transform. + */ + static KinematicTransform getIdentity() { + return Transform.IDENTITY; + } + + /** Compute a composite velocity. + * @param first first applied transform + * @param second second applied transform + * @return velocity part of the composite transform + */ + static Vector3D compositeVelocity(final KinematicTransform first, final KinematicTransform second) { + + final Vector3D v1 = first.getVelocity(); + final Rotation r1 = first.getRotation(); + final Vector3D o1 = first.getRotationRate(); + final Vector3D p2 = second.getTranslation(); + final Vector3D v2 = second.getVelocity(); + + final Vector3D crossP = Vector3D.crossProduct(o1, p2); + + return v1.add(r1.applyInverseTo(v2.add(crossP))); + } + + /** Compute a composite rotation rate. + * @param first first applied transform + * @param second second applied transform + * @return rotation rate part of the composite transform + */ + static Vector3D compositeRotationRate(final KinematicTransform first, final KinematicTransform second) { + + final Vector3D o1 = first.getRotationRate(); + final Rotation r2 = second.getRotation(); + final Vector3D o2 = second.getRotationRate(); + + return o2.add(r2.applyTo(o1)); + } + + /** Transform {@link PVCoordinates}, without the acceleration vector. + * @param pv the position-velocity couple to transform. + * @return transformed position-velocity + */ + default PVCoordinates transformOnlyPV(final PVCoordinates pv) { + final Vector3D transformedP = transformPosition(pv.getPosition()); + final Vector3D crossP = Vector3D.crossProduct(getRotationRate(), transformedP); + final Vector3D transformedV = getRotation().applyTo(pv.getVelocity().add(getVelocity())).subtract(crossP); + return new PVCoordinates(transformedP, transformedV); + } + + /** Transform {@link TimeStampedPVCoordinates}, without the acceleration vector. + *

    + * In order to allow the user more flexibility, this method does not check for + * consistency between the transform {@link #getDate() date} and the time-stamped + * position-velocity {@link TimeStampedPVCoordinates#getDate() date}. The returned + * value will always have the same {@link TimeStampedPVCoordinates#getDate() date} as + * the input argument, regardless of the instance {@link #getDate() date}. + *

    + * @param pv the position-velocity couple to transform. + * @return transformed position-velocity + */ + default TimeStampedPVCoordinates transformOnlyPV(final TimeStampedPVCoordinates pv) { + final Vector3D transformedP = transformPosition(pv.getPosition()); + final Vector3D crossP = Vector3D.crossProduct(getRotationRate(), transformedP); + final Vector3D transformedV = getRotation().applyTo(pv.getVelocity().add(getVelocity())).subtract(crossP); + return new TimeStampedPVCoordinates(pv.getDate(), transformedP, transformedV); + } + + /** Get the first time derivative of the translation. + * @return first time derivative of the translation + * @see #getTranslation() + */ + Vector3D getVelocity(); + + /** Get the first time derivative of the rotation. + *

    The norm represents the angular rate.

    + * @return First time derivative of the rotation + * @see #getRotation() + */ + Vector3D getRotationRate(); + + /** + * Get the inverse transform of the instance. + * + * @return inverse transform of the instance + */ + KinematicTransform getInverse(); + + /** + * Build a transform by combining two existing ones. + *

    + * Note that the dates of the two existing transformed are ignored, + * and the combined transform date is set to the date supplied in this + * constructor without any attempt to shift the raw transforms. This is a + * design choice allowing user full control of the combination. + *

    + * + * @param date date of the transform + * @param first first transform applied + * @param second second transform applied + * @return the newly created kinematic transform that has the same effect as + * applying {@code first}, then {@code second}. + * @see #of(AbsoluteDate, PVCoordinates, Rotation, Vector3D) + */ + static KinematicTransform compose(final AbsoluteDate date, + final KinematicTransform first, + final KinematicTransform second) { + final Vector3D composedTranslation = StaticTransform.compositeTranslation(first, second); + final Vector3D composedTranslationRate = KinematicTransform.compositeVelocity(first, second); + return of(date, new PVCoordinates(composedTranslation, composedTranslationRate), + StaticTransform.compositeRotation(first, second), + KinematicTransform.compositeRotationRate(first, second)); + } + + /** + * Create a new kinematic transform from a rotation and zero, constant translation. + * + * @param date of translation. + * @param rotation to apply after the translation. That is after translating + * applying this rotation produces positions expressed in + * the new frame. + * @param rotationRate rate of rotation + * @return the newly created kinematic transform. + * @see #of(AbsoluteDate, PVCoordinates, Rotation, Vector3D) + */ + static KinematicTransform of(final AbsoluteDate date, + final Rotation rotation, + final Vector3D rotationRate) { + return of(date, PVCoordinates.ZERO, rotation, rotationRate); + } + + /** + * Create a new kinematic transform from a translation and its rate. + * + * @param date of translation. + * @param pvCoordinates translation (with rate) to apply, expressed in the old frame. That is, the + * opposite of the coordinates of the new origin in the + * old frame. + * @return the newly created kinematic transform. + * @see #of(AbsoluteDate, PVCoordinates, Rotation, Vector3D) + */ + static KinematicTransform of(final AbsoluteDate date, + final PVCoordinates pvCoordinates) { + return of(date, pvCoordinates, Rotation.IDENTITY, Vector3D.ZERO); + } + + /** + * Create a new kinematic transform from a translation and rotation. + * + * @param date of translation. + * @param pvCoordinates translation (with rate) to apply, expressed in the old frame. That is, the + * opposite of the coordinates of the new origin in the + * old frame. + * @param rotation to apply after the translation. That is after + * translating applying this rotation produces positions + * expressed in the new frame. + * @param rotationRate rate of rotation + * @return the newly created kinematic transform. + * @see #compose(AbsoluteDate, KinematicTransform, KinematicTransform) + * @see #of(AbsoluteDate, PVCoordinates, Rotation, Vector3D) + * @see #of(AbsoluteDate, PVCoordinates, Rotation, Vector3D) + */ + static KinematicTransform of(final AbsoluteDate date, final PVCoordinates pvCoordinates, + final Rotation rotation, final Vector3D rotationRate) { + return new KinematicTransform() { + + @Override + public KinematicTransform getInverse() { + final Rotation r = getRotation(); + final Vector3D rp = r.applyTo(getTranslation()); + final Vector3D pInv = rp.negate(); + final Vector3D crossP = Vector3D.crossProduct(getRotationRate(), rp); + final Vector3D vInv = crossP.subtract(getRotation().applyTo(getVelocity())); + final Rotation rInv = r.revert(); + return KinematicTransform.of(getDate(), new PVCoordinates(pInv, vInv), + rInv, rInv.applyTo(getRotationRate()).negate()); + } + + @Override + public AbsoluteDate getDate() { + return date; + } + + @Override + public Vector3D getTranslation() { + return pvCoordinates.getPosition(); + } + + @Override + public Rotation getRotation() { + return rotation; + } + + @Override + public Vector3D getVelocity() { + return pvCoordinates.getVelocity(); + } + + @Override + public Vector3D getRotationRate() { + return rotationRate; + } + }; + } + +} diff --git a/src/main/java/org/orekit/frames/L1TransformProvider.java b/src/main/java/org/orekit/frames/L1TransformProvider.java index 50019825eb..c56ad667a4 100644 --- a/src/main/java/org/orekit/frames/L1TransformProvider.java +++ b/src/main/java/org/orekit/frames/L1TransformProvider.java @@ -195,13 +195,13 @@ private Vector3D getL1(final Vector3D primaryToSecondary) { final T zero = primaryToSecondary.getX().getField().getZero(); final T[] searchInterval = UnivariateSolverUtils.bracket(l1Equation, baseR, zero, bigR.multiply(2), - bigR.multiply(0.01), zero.add(1), + bigR.multiply(0.01), zero.newInstance(1), MAX_EVALUATIONS); - final T relativeAccuracy = zero.add(RELATIVE_ACCURACY); - final T absoluteAccuracy = zero.add(ABSOLUTE_ACCURACY); - final T functionAccuracy = zero.add(FUNCTION_ACCURACY); + final T relativeAccuracy = zero.newInstance(RELATIVE_ACCURACY); + final T absoluteAccuracy = zero.newInstance(ABSOLUTE_ACCURACY); + final T functionAccuracy = zero.newInstance(FUNCTION_ACCURACY); final FieldBracketingNthOrderBrentSolver solver = new FieldBracketingNthOrderBrentSolver<>(relativeAccuracy, diff --git a/src/main/java/org/orekit/frames/L2TransformProvider.java b/src/main/java/org/orekit/frames/L2TransformProvider.java index da4e858807..fd17ff743e 100644 --- a/src/main/java/org/orekit/frames/L2TransformProvider.java +++ b/src/main/java/org/orekit/frames/L2TransformProvider.java @@ -195,13 +195,13 @@ private Vector3D getL2(final Vector3D primaryToSecondary) { final T zero = primaryToSecondary.getX().getField().getZero(); final T[] searchInterval = UnivariateSolverUtils.bracket(l2Equation, baseR, zero, bigR.multiply(2), - bigR.multiply(0.01), zero.add(1), + bigR.multiply(0.01), zero, MAX_EVALUATIONS); - final T relativeAccuracy = zero.add(RELATIVE_ACCURACY); - final T absoluteAccuracy = zero.add(ABSOLUTE_ACCURACY); - final T functionAccuracy = zero.add(FUNCTION_ACCURACY); + final T relativeAccuracy = zero.newInstance(RELATIVE_ACCURACY); + final T absoluteAccuracy = zero.newInstance(ABSOLUTE_ACCURACY); + final T functionAccuracy = zero.newInstance(FUNCTION_ACCURACY); final FieldBracketingNthOrderBrentSolver solver = new FieldBracketingNthOrderBrentSolver<>(relativeAccuracy, diff --git a/src/main/java/org/orekit/frames/LOF.java b/src/main/java/org/orekit/frames/LOF.java index dce24ad6dc..0b720d1533 100644 --- a/src/main/java/org/orekit/frames/LOF.java +++ b/src/main/java/org/orekit/frames/LOF.java @@ -25,6 +25,7 @@ import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.AngularCoordinates; import org.orekit.utils.FieldPVCoordinates; import org.orekit.utils.PVCoordinates; @@ -187,6 +188,10 @@ default > FieldTransform transformFromLOF(f */ default > FieldTransform transformFromInertial(final FieldAbsoluteDate date, final FieldPVCoordinates pv) { + if (isQuasiInertial()) { + final Field field = date.getField(); + return new FieldTransform<>(date, pv.getPosition().negate(), rotationFromInertial(field, date, pv)); + } // compute the translation part of the transform final FieldTransform translation = new FieldTransform<>(date, pv.negate()); @@ -199,11 +204,7 @@ default > FieldTransform transformFromInert new FieldVector3D<>(p.getNormSq().reciprocal(), r.applyTo(momentum))); - final FieldTransform transform = new FieldTransform<>(date, translation, rotation); - - // If LOF is considered pseudo-inertial, freeze transform - return isQuasiInertial() ? transform.freeze() : transform; - + return new FieldTransform<>(date, translation, rotation); } /** @@ -285,21 +286,17 @@ default Transform transformFromLOF(final LOF fromLOF, final AbsoluteDate date, f * @return transform from the frame where position-velocity are defined to local orbital frame */ default Transform transformFromInertial(final AbsoluteDate date, final PVCoordinates pv) { - - // compute the translation part of the transform - final Transform translation = new Transform(date, pv.negate()); + if (isQuasiInertial()) { + return new Transform(date, pv.getPosition().negate(), rotationFromInertial(date, pv)); + } // compute the rotation part of the transform final Rotation r = rotationFromInertial(date, pv); final Vector3D p = pv.getPosition(); final Vector3D momentum = pv.getMomentum(); - final Transform rotation = new Transform(date, r, new Vector3D(1.0 / p.getNormSq(), r.applyTo(momentum))); - - final Transform transform = new Transform(date, translation, rotation); - - // If LOF is considered pseudo-inertial, freeze transform - return isQuasiInertial() ? transform.freeze() : transform; + final AngularCoordinates angularCoordinates = new AngularCoordinates(r, new Vector3D(1.0 / p.getNormSq(), r.applyTo(momentum))); + return new Transform(date, pv.negate(), angularCoordinates); } /** diff --git a/src/main/java/org/orekit/frames/LOFType.java b/src/main/java/org/orekit/frames/LOFType.java index f35dfd4423..7e1d7f0970 100644 --- a/src/main/java/org/orekit/frames/LOFType.java +++ b/src/main/java/org/orekit/frames/LOFType.java @@ -577,7 +577,7 @@ public OrbitRelativeFrame toOrbitRelativeFrame() { public Rotation rotationFromInertial(final PVCoordinates pv) { final Vector3D m = pv.getMomentum(); return new Rotation(new Vector3D(-m.getY(), m.getX(), 0), m, - Vector3D.PLUS_I, Vector3D.PLUS_J); + Vector3D.PLUS_I, Vector3D.PLUS_K); } /** {@inheritDoc} */ @@ -588,7 +588,7 @@ public > FieldRotation rotationFromInertial return new FieldRotation<>(new FieldVector3D<>(m.getY().negate(), m.getX(), field.getZero()), m, new FieldVector3D<>(field, Vector3D.PLUS_I), - new FieldVector3D<>(field, Vector3D.PLUS_J)); + new FieldVector3D<>(field, Vector3D.PLUS_K)); } /** {@inheritDoc} */ @@ -690,7 +690,7 @@ public OrbitRelativeFrame toOrbitRelativeFrame() { /** {@inheritDoc} */ public String getName() { return this.name(); - }; + } /** * Get the rotation from input to output {@link LOFType local orbital frame}. diff --git a/src/main/java/org/orekit/frames/MODProvider.java b/src/main/java/org/orekit/frames/MODProvider.java index fd1bf29b63..1bb9655169 100644 --- a/src/main/java/org/orekit/frames/MODProvider.java +++ b/src/main/java/org/orekit/frames/MODProvider.java @@ -101,11 +101,9 @@ public > FieldTransform getTransform(final final T[] angles = precessionFunction.value(date); @SuppressWarnings("unchecked") - FieldRotation fR4 = (FieldRotation) fieldR4.get(date.getField()); - if (fR4 == null) { - fR4 = new FieldRotation<>(date.getField(), r4); - fieldR4.put(date.getField(), fR4); - } + final FieldRotation 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 a268bc6cff..eb00eb5112 100644 --- a/src/main/java/org/orekit/frames/StaticTransform.java +++ b/src/main/java/org/orekit/frames/StaticTransform.java @@ -128,6 +128,19 @@ default Line transformLine(final Line line) { */ StaticTransform getInverse(); + /** + * Get the inverse transform of the instance in static form (without rates). + * This enables to create a purely static inverse, as inheritors such as {@link Transform} may + * have a relatively computationally-heavy #getInverse() method. + * + * @return inverse static transform of the instance + * @since 12.1 + */ + default StaticTransform getStaticInverse() { + final Rotation rotation = getRotation(); + return StaticTransform.of(getDate(), rotation.applyTo(getTranslation()).negate(), rotation.revert()); + } + /** * Build a transform by combining two existing ones. *

    diff --git a/src/main/java/org/orekit/frames/TIRFProvider.java b/src/main/java/org/orekit/frames/TIRFProvider.java index a6c1e798b2..82312e7b59 100644 --- a/src/main/java/org/orekit/frames/TIRFProvider.java +++ b/src/main/java/org/orekit/frames/TIRFProvider.java @@ -85,65 +85,91 @@ public TIRFProvider getNonInterpolatingProvider() { /** {@inheritDoc} */ @Override public Transform getTransform(final AbsoluteDate date) { + return new Transform(date, getRotation(date), getRotationRate(date)); + } - // compute proper rotation - final double correctedERA = era.value(date); - - // compute true angular rotation of Earth, in rad/s - final double lod = (eopHistory == null) ? 0.0 : eopHistory.getLOD(date); - final double omp = AVE * (1 - lod / Constants.JULIAN_DAY); - final Vector3D rotationRate = new Vector3D(omp, Vector3D.PLUS_K); - - // set up the transform from parent CIRF - final Rotation rotation = new Rotation(Vector3D.PLUS_K, correctedERA, RotationConvention.FRAME_TRANSFORM); - return new Transform(date, rotation, rotationRate); - + /** {@inheritDoc} */ + @Override + public KinematicTransform getKinematicTransform(final AbsoluteDate date) { + return KinematicTransform.of(date, getRotation(date), getRotationRate(date)); } /** {@inheritDoc} */ @Override public StaticTransform getStaticTransform(final AbsoluteDate date) { + return StaticTransform.of(date, getRotation(date)); + } + + /** Form rotation to parent CIRF. + * @param date transform date + * @return rotation to parent at date + * @since 12.1 + */ + private Rotation getRotation(final AbsoluteDate date) { // compute proper rotation final double correctedERA = era.value(date); // set up the transform from parent CIRF - final Rotation rotation = new Rotation( - Vector3D.PLUS_K, - correctedERA, - RotationConvention.FRAME_TRANSFORM); - return StaticTransform.of(date, rotation); + return new Rotation(Vector3D.PLUS_K, correctedERA, RotationConvention.FRAME_TRANSFORM); + } + + /** Form rotation rate w.r.t. parent CIRF. + * @param date transform date + * @return rotation rate at date + * @since 12.1 + */ + private Vector3D getRotationRate(final AbsoluteDate date) { + // compute true angular rotation of Earth, in rad/s + final double lod = (eopHistory == null) ? 0.0 : eopHistory.getLOD(date); + final double omp = AVE * (1 - lod / Constants.JULIAN_DAY); + return new Vector3D(omp, Vector3D.PLUS_K); } /** {@inheritDoc} */ @Override public > FieldTransform getTransform(final FieldAbsoluteDate date) { + return new FieldTransform<>(date, getRotation(date), getRotationRate(date)); + } - // compute proper rotation - final T correctedERA = era.value(date); - - // compute true angular rotation of Earth, in rad/s - final T lod = (eopHistory == null) ? date.getField().getZero() : eopHistory.getLOD(date); - final T omp = lod.divide(Constants.JULIAN_DAY).subtract(1).multiply(-AVE); - final FieldVector3D rotationRate = new FieldVector3D<>(omp, Vector3D.PLUS_K); - - // set up the transform from parent CIRF - final FieldRotation rotation = new FieldRotation<>(FieldVector3D.getPlusK(date.getField()), - correctedERA, - RotationConvention.FRAME_TRANSFORM); - return new FieldTransform<>(date, rotation, rotationRate); - + /** {@inheritDoc} */ + @Override + public > FieldKinematicTransform getKinematicTransform(final FieldAbsoluteDate date) { + return FieldKinematicTransform.of(date, getRotation(date), getRotationRate(date)); } /** {@inheritDoc} */ @Override public > FieldStaticTransform getStaticTransform(final FieldAbsoluteDate date) { + return FieldStaticTransform.of(date, getRotation(date)); + } + + /** Form rotation to parent CIRF. + * @param type of the elements + * @param date transform date + * @return rotation to parent at date + * @since 12.1 + */ + private > FieldRotation getRotation(final FieldAbsoluteDate date) { // compute proper rotation final T correctedERA = era.value(date); + // set up the transform from parent CIRF - final FieldRotation rotation = new FieldRotation<>( + return new FieldRotation<>( FieldVector3D.getPlusK(date.getField()), correctedERA, RotationConvention.FRAME_TRANSFORM); - return FieldStaticTransform.of(date, rotation); + } + + /** Form rotation rate w.r.t. parent CIRF. + * @param type of the elements + * @param date transform date + * @return rotation rate at date + * @since 12.1 + */ + private > FieldVector3D getRotationRate(final FieldAbsoluteDate date) { + // compute true angular rotation of Earth, in rad/s + final T lod = (eopHistory == null) ? date.getField().getZero() : eopHistory.getLOD(date); + final T omp = lod.divide(Constants.JULIAN_DAY).subtract(1).multiply(-AVE); + return new FieldVector3D<>(omp, Vector3D.PLUS_K); } /** Get the Earth Rotation Angle at the current date. diff --git a/src/main/java/org/orekit/frames/TopocentricFrame.java b/src/main/java/org/orekit/frames/TopocentricFrame.java index 8d1279e40d..67d6a97339 100644 --- a/src/main/java/org/orekit/frames/TopocentricFrame.java +++ b/src/main/java/org/orekit/frames/TopocentricFrame.java @@ -26,6 +26,7 @@ import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; +import org.hipparchus.util.FieldSinCos; import org.hipparchus.util.MathUtils; import org.hipparchus.util.SinCos; import org.orekit.bodies.BodyShape; @@ -129,9 +130,9 @@ public Vector3D getCartesianPoint() { */ public > FieldGeodeticPoint getPoint(final Field field) { final T zero = field.getZero(); - return new FieldGeodeticPoint<>(zero.add(point.getLatitude()), - zero.add(point.getLongitude()), - zero.add(point.getAltitude())); + return new FieldGeodeticPoint<>(zero.newInstance(point.getLatitude()), + zero.newInstance(point.getLongitude()), + zero.newInstance(point.getAltitude())); } /** Get the zenith direction of topocentric frame, expressed in parent shape frame. @@ -350,8 +351,8 @@ public double getRangeRate(final PVCoordinates extPV, final Frame frame, final AbsoluteDate date) { // Transform given point from given frame to topocentric frame - final Transform t = frame.getTransformTo(this, date); - final PVCoordinates extPVTopo = t.transformPVCoordinates(extPV); + final KinematicTransform t = frame.getKinematicTransformTo(this, date); + final PVCoordinates extPVTopo = t.transformOnlyPV(extPV); // Compute range rate (doppler) : relative rate along the line of sight return Vector3D.dotProduct(extPVTopo.getPosition(), extPVTopo.getVelocity()) / @@ -371,8 +372,8 @@ public > T getRangeRate(final FieldPVCoordinat final FieldAbsoluteDate date) { // Transform given point from given frame to topocentric frame - final FieldTransform t = frame.getTransformTo(this, date); - final FieldPVCoordinates extPVTopo = t.transformPVCoordinates(extPV); + final FieldKinematicTransform t = frame.getKinematicTransformTo(this, date); + final FieldPVCoordinates extPVTopo = t.transformOnlyPV(extPV); // Compute range rate (doppler) : relative rate along the line of sight return FieldVector3D.dotProduct(extPVTopo.getPosition(), extPVTopo.getVelocity()).divide( @@ -453,6 +454,58 @@ public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Vector3D.ZERO)); } + /** Get the topocentric position from {@link TrackingCoordinates}. + * @param coords The coordinates that are to be converted. + * @return The topocentric coordinates. + * @since 12.1 + */ + public static Vector3D getTopocentricPosition(final TrackingCoordinates coords) { + return getTopocentricPosition(coords.getAzimuth(), coords.getElevation(), coords.getRange()); + } + + /** Get the topocentric position from {@link FieldTrackingCoordinates}. + * @param coords The coordinates that are to be converted. + * @param Type of the field coordinates. + * @return The topocentric coordinates. + * @since 12.1 + */ + public static > FieldVector3D getTopocentricPosition(final FieldTrackingCoordinates coords) { + return getTopocentricPosition(coords.getAzimuth(), coords.getElevation(), coords.getRange()); + } + + /** + * Gets the topocentric position from a set of az/el/ra coordinates. + * @param azimuth the angle of rotation around the vertical axis, going East. + * @param elevation the elevation angle from the local horizon. + * @param range the distance from the goedetic position. + * @return the topocentric position. + * @since 12.1 + */ + private static Vector3D getTopocentricPosition(final double azimuth, final double elevation, final double range) { + final SinCos sinCosAz = FastMath.sinCos(azimuth); + final SinCos sinCosEL = FastMath.sinCos(elevation); + return new Vector3D(range * sinCosEL.cos() * sinCosAz.sin(), range * sinCosEL.cos() * sinCosAz.cos(), range * sinCosEL.sin()); + } + + /** + * Gets the topocentric position from a set of az/el/ra coordinates. + * @param azimuth the angle of rotation around the vertical axis, going East. + * @param elevation the elevation angle from the local horizon. + * @param range the distance from the geodetic position. + * @return the topocentric position. + * @param the type of the az/el/ra coordinates. + * @since 12.1 + */ + private static > FieldVector3D getTopocentricPosition(final T azimuth, final T elevation, final T range) { + final FieldSinCos sinCosAz = FastMath.sinCos(azimuth); + final FieldSinCos sinCosEl = FastMath.sinCos(elevation); + return new FieldVector3D<>( + range.multiply(sinCosEl.cos()).multiply(sinCosAz.sin()), + range.multiply(sinCosEl.cos()).multiply(sinCosAz.cos()), + range.multiply(sinCosEl.sin()) + ); + } + /** Transform point in topocentric frame. * @param extPoint point * @param date current date diff --git a/src/main/java/org/orekit/frames/Transform.java b/src/main/java/org/orekit/frames/Transform.java index b9833581de..5d7ca31253 100644 --- a/src/main/java/org/orekit/frames/Transform.java +++ b/src/main/java/org/orekit/frames/Transform.java @@ -101,7 +101,7 @@ public class Transform implements TimeShiftable, Serializable, - StaticTransform { + KinematicTransform { /** Identity transform. */ public static final Transform IDENTITY = new IdentityTransform(); @@ -153,6 +153,20 @@ public Transform(final AbsoluteDate date, final Rotation rotation) { new AngularCoordinates(rotation)); } + /** Build a combined translation and rotation transform. + * @param date date of the transform + * @param translation translation to apply (i.e. coordinates of + * the transformed origin, or coordinates of the origin of the + * old frame in the new frame) + * @param rotation rotation to apply ( i.e. rotation to apply to the + * coordinates of a vector expressed in the old frame to obtain the + * same vector expressed in the new frame ) + * @since 12.1 + */ + public Transform(final AbsoluteDate date, final Vector3D translation, final Rotation rotation) { + this(date, new PVCoordinates(translation), new AngularCoordinates(rotation)); + } + /** Build a translation transform, with its first time derivative. * @param date date of the transform * @param translation translation to apply (i.e. coordinates of @@ -251,32 +265,13 @@ public Transform(final AbsoluteDate date, final AngularCoordinates angular) { public Transform(final AbsoluteDate date, final Transform first, final Transform second) { this(date, new PVCoordinates(StaticTransform.compositeTranslation(first, second), - compositeVelocity(first, second), + KinematicTransform.compositeVelocity(first, second), compositeAcceleration(first, second)), new AngularCoordinates(StaticTransform.compositeRotation(first, second), - compositeRotationRate(first, second), + KinematicTransform.compositeRotationRate(first, second), compositeRotationAcceleration(first, second))); } - /** Compute a composite velocity. - * @param first first applied transform - * @param second second applied transform - * @return velocity part of the composite transform - */ - private static Vector3D compositeVelocity(final Transform first, final Transform second) { - - final Vector3D v1 = first.cartesian.getVelocity(); - final Rotation r1 = first.angular.getRotation(); - final Vector3D o1 = first.angular.getRotationRate(); - final Vector3D p2 = second.cartesian.getPosition(); - final Vector3D v2 = second.cartesian.getVelocity(); - - final Vector3D crossP = Vector3D.crossProduct(o1, p2); - - return v1.add(r1.applyInverseTo(v2.add(crossP))); - - } - /** Compute a composite acceleration. * @param first first applied transform * @param second second applied transform @@ -300,21 +295,6 @@ private static Vector3D compositeAcceleration(final Transform first, final Trans } - /** Compute a composite rotation rate. - * @param first first applied transform - * @param second second applied transform - * @return rotation rate part of the composite transform - */ - private static Vector3D compositeRotationRate(final Transform first, final Transform second) { - - final Vector3D o1 = first.angular.getRotationRate(); - final Rotation r2 = second.angular.getRotation(); - final Vector3D o2 = second.angular.getRotationRate(); - - return o2.add(r2.applyTo(o1)); - - } - /** Compute a composite rotation acceleration. * @param first first applied transform * @param second second applied transform diff --git a/src/main/java/org/orekit/frames/TransformProvider.java b/src/main/java/org/orekit/frames/TransformProvider.java index ec4e8a1dc5..528e5e398e 100644 --- a/src/main/java/org/orekit/frames/TransformProvider.java +++ b/src/main/java/org/orekit/frames/TransformProvider.java @@ -46,11 +46,40 @@ public interface TransformProvider extends Serializable { > FieldTransform getTransform(FieldAbsoluteDate date); /** - * Get a transform for only rotations and translations on the specified date. + * Get a transform for position and velocity, not acceleration. + * + *

    The default implementation returns {@link #getTransform(AbsoluteDate)} + * but implementations may override it for better performance. + * + * @param date current date. + * @return the kinematic transform. + * @since 12.1 + */ + default KinematicTransform getKinematicTransform(AbsoluteDate date) { + return getTransform(date); + } + + /** + * Get a transform for position and velocity, not acceleration. * *

    The default implementation returns {@link #getTransform(AbsoluteDate)} * but implementations may override it for better performance. * + * @param type of the elements + * @param date current date. + * @return the kinematic transform. + * @since 12.1 + */ + default > FieldKinematicTransform getKinematicTransform(FieldAbsoluteDate date) { + return getTransform(date); + } + + /** + * Get a transform for only rotations and translations on the specified date. + * + *

    The default implementation calls {@link #getTransform(AbsoluteDate)} + * but implementations may override it for better performance. + * * @param date current date. * @return the static transform. */ diff --git a/src/main/java/org/orekit/frames/TransformProviderUtils.java b/src/main/java/org/orekit/frames/TransformProviderUtils.java index 1513556c1e..e3477abc20 100644 --- a/src/main/java/org/orekit/frames/TransformProviderUtils.java +++ b/src/main/java/org/orekit/frames/TransformProviderUtils.java @@ -84,6 +84,11 @@ public Transform getTransform(final AbsoluteDate date) { return provider.getTransform(date).getInverse(); } + @Override + public KinematicTransform getKinematicTransform(final AbsoluteDate date) { + return provider.getKinematicTransform(date).getInverse(); + } + @Override public StaticTransform getStaticTransform(final AbsoluteDate date) { return provider.getStaticTransform(date).getInverse(); @@ -95,6 +100,12 @@ public > FieldTransform getTransform(final return provider.getTransform(date).getInverse(); } + /** {@inheritDoc} */ + @Override + public > FieldKinematicTransform getKinematicTransform(final FieldAbsoluteDate date) { + return provider.getKinematicTransform(date).getInverse(); + } + /** {@inheritDoc} */ @Override public > FieldStaticTransform getStaticTransform(final FieldAbsoluteDate date) { @@ -123,6 +134,7 @@ public Transform getTransform(final AbsoluteDate date) { return new Transform(date, first.getTransform(date), second.getTransform(date)); } + /** {@inheritDoc} */ @Override public StaticTransform getStaticTransform(final AbsoluteDate date) { return StaticTransform.compose( @@ -132,12 +144,32 @@ public StaticTransform getStaticTransform(final AbsoluteDate date) { ); } + /** {@inheritDoc} */ + @Override + public KinematicTransform getKinematicTransform(final AbsoluteDate date) { + return KinematicTransform.compose( + date, + first.getKinematicTransform(date), + second.getKinematicTransform(date) + ); + } + /** {@inheritDoc} */ @Override public > FieldTransform getTransform(final FieldAbsoluteDate date) { return new FieldTransform<>(date, first.getTransform(date), second.getTransform(date)); } + /** {@inheritDoc} */ + @Override + public > FieldKinematicTransform getKinematicTransform(final FieldAbsoluteDate date) { + return FieldKinematicTransform.compose( + date, + first.getKinematicTransform(date), + second.getKinematicTransform(date) + ); + } + /** {@inheritDoc} */ @Override public > FieldStaticTransform getStaticTransform(final FieldAbsoluteDate date) { diff --git a/src/main/java/org/orekit/frames/VEISProvider.java b/src/main/java/org/orekit/frames/VEISProvider.java index 1f7b2bb883..0d313979ff 100644 --- a/src/main/java/org/orekit/frames/VEISProvider.java +++ b/src/main/java/org/orekit/frames/VEISProvider.java @@ -108,7 +108,7 @@ public > FieldTransform getTransform(final final T vst = ttd.multiply(VST1).add(rdtt.multiply(MathUtils.TWO_PI)).add(VST0).remainder(MathUtils.TWO_PI); // compute angular rotation of Earth, in rad/s - final FieldVector3D rotationRate = new FieldVector3D<>(date.getField().getZero().add(-VSTD), + final FieldVector3D rotationRate = new FieldVector3D<>(date.getField().getZero().newInstance(-VSTD), Vector3D.PLUS_K); // set up the transform from parent GTOD diff --git a/src/main/java/org/orekit/frames/VersionedITRFProvider.java b/src/main/java/org/orekit/frames/VersionedITRFProvider.java index 90e197d1d9..ed795d5f7e 100644 --- a/src/main/java/org/orekit/frames/VersionedITRFProvider.java +++ b/src/main/java/org/orekit/frames/VersionedITRFProvider.java @@ -62,7 +62,7 @@ class VersionedITRFProvider implements EOPBasedTransformProvider { final TimeScale tt) { this.version = version; this.rawProvider = rawProvider; - this.converter = new AtomicReference(); + this.converter = new AtomicReference<>(); this.tt = tt; } @@ -102,6 +102,23 @@ public Transform getTransform(final AbsoluteDate date) { } + /** {@inheritDoc} */ + @Override + public KinematicTransform getKinematicTransform(final AbsoluteDate date) { + + // get the transform from the current EOP + final KinematicTransform rawTransform = rawProvider.getKinematicTransform(date); + + // add the conversion layer + final ITRFVersion.Converter converterForDate = getConverter(date); + if (converterForDate == null) { + return rawTransform; + } else { + return KinematicTransform.compose(date, rawTransform, converterForDate.getKinematicTransform(date)); + } + + } + /** {@inheritDoc} */ @Override public StaticTransform getStaticTransform(final AbsoluteDate date) { @@ -139,6 +156,23 @@ public > FieldTransform getTransform(final } + /** {@inheritDoc} */ + @Override + public > FieldKinematicTransform getKinematicTransform(final FieldAbsoluteDate date) { + + // get the transform from the current EOP + final FieldKinematicTransform rawTransform = rawProvider.getKinematicTransform(date); + + // add the conversion layer + final ITRFVersion.Converter converterForDate = getConverter(date.toAbsoluteDate()); + if (converterForDate == null) { + return rawTransform; + } else { + return FieldKinematicTransform.compose(date, rawTransform, converterForDate.getKinematicTransform(date)); + } + + } + /** {@inheritDoc} */ @Override public > FieldStaticTransform getStaticTransform(final FieldAbsoluteDate date) { diff --git a/src/main/java/org/orekit/geometry/fov/EllipticalFieldOfView.java b/src/main/java/org/orekit/geometry/fov/EllipticalFieldOfView.java index 30809ab721..15ae95ef86 100644 --- a/src/main/java/org/orekit/geometry/fov/EllipticalFieldOfView.java +++ b/src/main/java/org/orekit/geometry/fov/EllipticalFieldOfView.java @@ -400,7 +400,7 @@ private > FieldVector3D directionAt(final T final T cos2 = FastMath.cos(d2); final T a1 = cos1.subtract(cos2.multiply(dotF1F2)).multiply(d); final T a2 = cos2.subtract(cos1.multiply(dotF1F2)).multiply(d); - final T ac = FastMath.sqrt(a1.multiply(a1.add(a2.multiply(2 * dotF1F2))).add(a2.multiply(a2)).negate().add(1).multiply(d)); + final T ac = FastMath.sqrt(a1.multiply(a1.add(a2.multiply(2 * dotF1F2))).add(a2.square()).negate().add(1).multiply(d)); return new FieldVector3D<>(a1, focus1, a2, focus2, FastMath.copySign(ac, sign), crossF1F2); } diff --git a/src/main/java/org/orekit/geometry/fov/PolygonalFieldOfView.java b/src/main/java/org/orekit/geometry/fov/PolygonalFieldOfView.java index 578517ba34..2862565b3c 100644 --- a/src/main/java/org/orekit/geometry/fov/PolygonalFieldOfView.java +++ b/src/main/java/org/orekit/geometry/fov/PolygonalFieldOfView.java @@ -210,7 +210,7 @@ public List> getFootprint(final Transform fovToBody, // none of the Field Of View loops cross the body // either the body is outside of Field Of View, or it is fully contained // we check the center - final Vector3D bodyCenter = fovToBody.toStaticTransform().getInverse().transformPosition(Vector3D.ZERO); + final Vector3D bodyCenter = fovToBody.getStaticInverse().transformPosition(Vector3D.ZERO); if (zone.checkPoint(new S2Point(bodyCenter)) != Region.Location.OUTSIDE) { // the body is fully contained in the Field Of View // we use the full limb as the footprint diff --git a/src/main/java/org/orekit/geometry/fov/SmoothFieldOfView.java b/src/main/java/org/orekit/geometry/fov/SmoothFieldOfView.java index 20d697e388..d2509c2309 100644 --- a/src/main/java/org/orekit/geometry/fov/SmoothFieldOfView.java +++ b/src/main/java/org/orekit/geometry/fov/SmoothFieldOfView.java @@ -154,7 +154,7 @@ public List> getFootprint(final Transform fovToBody, // the Field Of View loop does not cross the body // either the body is outside of Field Of View, or it is fully contained // we check the center - final Vector3D bodyCenter = fovToBody.toStaticTransform().getInverse().transformPosition(Vector3D.ZERO); + final Vector3D bodyCenter = fovToBody.getStaticInverse().transformPosition(Vector3D.ZERO); if (offsetFromBoundary(bodyCenter, 0.0, VisibilityTrigger.VISIBLE_ONLY_WHEN_FULLY_IN_FOV) < 0.0) { // the body is fully contained in the Field Of View // the previous loop did compute the full limb as the footprint diff --git a/src/main/java/org/orekit/gnss/Frequency.java b/src/main/java/org/orekit/gnss/Frequency.java index 161c91506f..92d8f9a63e 100644 --- a/src/main/java/org/orekit/gnss/Frequency.java +++ b/src/main/java/org/orekit/gnss/Frequency.java @@ -16,15 +16,13 @@ */ package org.orekit.gnss; -import org.orekit.utils.Constants; - /** * Enumerate for GNSS frequencies. * * @author Luc Maisonobe * @since 9.2 */ -public enum Frequency { +public enum Frequency implements GnssSignal { // CHECKSTYLE: stop MultipleStringLiterals check /** GPS L1 (1575.42 MHz). */ @@ -140,8 +138,11 @@ public enum Frequency { S05(SatelliteSystem.SBAS, "L5", 115); // CHECKSTYLE: resume MultipleStringLiterals check - /** Common frequency F0 in MHz (10.23 MHz). */ - public static final double F0 = 10.23; + /** Common frequency F0 in MHz (10.23 MHz). + * @deprecated as of 12.1, replaced by {@link GnssSignal#F0} + */ + @Deprecated + public static final double F0 = GnssSignal.F0 * 1.0e-6; /** Satellite system. */ private final SatelliteSystem satelliteSystem; @@ -149,13 +150,13 @@ public enum Frequency { /** RINEX name for the frequency. */ private final String name; - /** Ratio f/f0, where {@link #F0 f0} is the common frequency. */ + /** Ratio f/f0, where {@link GnssSignal#F0 f0} is the common frequency. */ private final double ratio; /** Simple constructor. * @param name for the frequency * @param satelliteSystem satellite system for which this frequency is defined - * @param ratio ratio f/f0, where {@link #F0 f0} is the common frequency + * @param ratio ratio f/f0, where {@link GnssSignal#F0 f0} is the common frequency */ Frequency(final SatelliteSystem satelliteSystem, final String name, final double ratio) { this.satelliteSystem = satelliteSystem; @@ -177,32 +178,25 @@ public SatelliteSystem getSatelliteSystem() { return satelliteSystem; } - /** Get the ratio f/f0, where {@link #F0 f0} is the common frequency. - * @return ratio f/f0, where {@link #F0 f0} is the common frequency - * @see #F0 - * @see #getMHzFrequency() - */ + /** {@inheritDoc} */ + @Override public double getRatio() { return ratio; } /** Get the value of the frequency in MHz. * @return value of the frequency in MHz - * @see #F0 - * @see #getRatio() - * @see #getWavelength() + * @deprecated as of 12.1 replaced by {@link #getFrequency()} */ + @Deprecated public double getMHzFrequency() { - return ratio * F0; + return getFrequency() * 1.0e-6; } - /** Get the wavelength in meters. - * @return wavelength in meters - * @see #getMHzFrequency() - * @since 10.1 - */ - public double getWavelength() { - return Constants.SPEED_OF_LIGHT / (1.0e6 * getMHzFrequency()); + /** {@inheritDoc} */ + @Override + public double getFrequency() { + return ratio * GnssSignal.F0; } } diff --git a/src/main/java/org/orekit/gnss/GnssSignal.java b/src/main/java/org/orekit/gnss/GnssSignal.java new file mode 100644 index 0000000000..1c5648f261 --- /dev/null +++ b/src/main/java/org/orekit/gnss/GnssSignal.java @@ -0,0 +1,36 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.gnss; + +/** Intermediate level interface for radio waves related to GNSS common frequency. + * @author Luc Maisonobe + * @since 12.1 + * + */ +public interface GnssSignal extends RadioWave { + + /** Common frequency F0 in Hz (10.23 MHz). */ + double F0 = 10230000.0; + + /** Get the ratio f/f0, where {@link #F0 f0} is the common frequency. + * @return ratio f/f0, where {@link #F0 f0} is the common frequency + * @see #F0 + * @see #getFrequency() + */ + double getRatio(); + +} diff --git a/src/main/java/org/orekit/gnss/IGSUtils.java b/src/main/java/org/orekit/gnss/IGSUtils.java new file mode 100644 index 0000000000..77c05f5b04 --- /dev/null +++ b/src/main/java/org/orekit/gnss/IGSUtils.java @@ -0,0 +1,164 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.gnss; + +import org.orekit.annotation.DefaultDataContext; +import org.orekit.data.DataContext; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.frames.Frame; +import org.orekit.frames.Frames; +import org.orekit.frames.ITRFVersion; +import org.orekit.frames.Predefined; +import org.orekit.frames.VersionedITRF; +import org.orekit.utils.IERSConventions; + +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** Utility for IGS files. + * @author Luc Maisonobe + * @since 12.1 + * + */ +public class IGSUtils { + + /** Pattern for frame names with year. + * @since 12.1 + */ + private static final Pattern EARTH_FRAME_WITH_YEAR = Pattern.compile("(?:ITR|ITRF|IGS|SLR)([0-9]{2})"); + + /** Pattern for GCRF inertial frame. + * @since 12.1 + */ + private static final Pattern GCRF_FRAME = Pattern.compile(" *GCRF *"); + + /** Pattern for EME2000 inertial frame. + * @since 12.1 + */ + private static final Pattern EME2000_FRAME = Pattern.compile("EME(?:00|2K)"); + + /** Private constructor for a utility class. + */ + private IGSUtils() { + // nothing to do + } + + /** Default string to {@link Frame} conversion for {@link org.orekit.files.sp3.SP3Parser} + * or {@link org.orekit.files.rinex.clock.RinexClockParser}. + * + *

    + * This method uses the {@link DataContext#getDefault() default data context}. + * If the frame names has a form like IGS##, or ITR##, or ITRF##, or SLR##, + * where ## is a two digits number, then this number will be used to build the + * appropriate {@link ITRFVersion}. Otherwise (for example if name is + * UNDEF or WGS84), then a default {@link + * org.orekit.frames.Frames#getITRF(IERSConventions, boolean) ITRF} + * will be created. + *

    + * + * @param name of the frame. + * @return ITRF based on 2010 conventions, + * with tidal effects considered during EOP interpolation + * @since 12.1 + */ + @DefaultDataContext + public static Frame guessFrame(final String name) { + return guessFrame(DataContext.getDefault().getFrames(), name); + } + + /** Default string to {@link Frame} conversion for {@link org.orekit.files.sp3.SP3Parser} + * or {@link org.orekit.files.rinex.clock.RinexClockParser}. + * + *

    + * Various frame names are supported: + *

    + *
      + *
    • IGS##, or ITR##, or ITRF##, or SLR##, + * where ## is a two digits number, then this number will be used to build the + * appropriate {@link ITRFVersion}
    • + *
    • GCRF (left or right justified) for GCRF inertial frame
    • + *
    • EME00 or EME2K for EME2000 inertial frame
    • + *
    • for all other names (for example if name is UNDEF or WGS84), + * then a default {@link org.orekit.frames.Frames#getITRF(IERSConventions, boolean) ITRF} + * frame will be selected
    • + *
    + *

    + * Note that using inertial frames in classical products like SP3 files is non-standard, + * it is supported by Orekit, but may not be supported by other programs, so they should + * be used with caution when writing files. + *

    + * + * @param frames frames factory + * @param name of the frame. + * @return guessed frame + * @since 12.1 + */ + public static Frame guessFrame(final Frames frames, final String name) { + final Matcher earthMatcher = EARTH_FRAME_WITH_YEAR.matcher(name); + if (earthMatcher.matches()) { + // this is a frame of the form IGS14, or ITR20, or SLR08, or similar + final int yy = Integer.parseInt(earthMatcher.group(1)); + final ITRFVersion itrfVersion = ITRFVersion.getITRFVersion(yy); + final IERSConventions conventions = + itrfVersion.getYear() < 2003 ? + IERSConventions.IERS_1996 : + (itrfVersion.getYear() < 2010 ? IERSConventions.IERS_2003 : IERSConventions.IERS_2010); + return frames.getITRF(itrfVersion, conventions, false); + } else { + final Matcher gcrfMatcher = GCRF_FRAME.matcher(name); + if (gcrfMatcher.matches()) { + // inertial GCRF frame + return frames.getGCRF(); + } else { + final Matcher eme2000Matcher = EME2000_FRAME.matcher(name); + if (eme2000Matcher.matches()) { + // inertial EME2000 frame + return frames.getEME2000(); + } else { + // unknown frame 'maybe UNDEF or WGS84 + // we use a default ITRF + return frames.getITRF(IERSConventions.IERS_2010, false); + } + } + } + } + + /** Guess a frame name. + *

    + * If the frame is not compatible with {@link #guessFrame(Frames, String)}, + * an exception will be triggered + *

    + * @param frame frame from which we want the name + * @return name compatible with {@link #guessFrame(Frames, String)} + * @since 12.1 + */ + public static String frameName(final Frame frame) { + if (frame instanceof VersionedITRF) { + final int yy = ((VersionedITRF) frame).getITRFVersion().getYear() % 100; + return String.format(Locale.US, "IGS%02d", yy); + } else if (Predefined.GCRF.getName().equals(frame.getName())) { + return "GCRF"; + } else if (Predefined.EME2000.getName().equals(frame.getName())) { + return "EME2K"; + } else { + throw new OrekitException(OrekitMessages.FRAME_NOT_ALLOWED, frame.getName()); + } + } + +} diff --git a/src/main/java/org/orekit/gnss/RadioWave.java b/src/main/java/org/orekit/gnss/RadioWave.java new file mode 100644 index 0000000000..63d99e853b --- /dev/null +++ b/src/main/java/org/orekit/gnss/RadioWave.java @@ -0,0 +1,42 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.gnss; + +import org.orekit.utils.Constants; + +/** Top level interface for radio waves. + * @author Luc Maisonobe + * @since 12.1 + * + */ +public interface RadioWave { + + /** Get the value of the frequency in Hz. + * @return value of the frequency in Hz + * @see #getWavelength() + */ + double getFrequency(); + + /** Get the wavelength in meters. + * @return wavelength in meters + * @see #getFrequency() + */ + default double getWavelength() { + return Constants.SPEED_OF_LIGHT / getFrequency(); + } + +} diff --git a/src/main/java/org/orekit/gnss/TimeSystem.java b/src/main/java/org/orekit/gnss/TimeSystem.java index 5bc132e02b..3ede2749ac 100644 --- a/src/main/java/org/orekit/gnss/TimeSystem.java +++ b/src/main/java/org/orekit/gnss/TimeSystem.java @@ -35,41 +35,41 @@ public enum TimeSystem { /** Global Positioning System. */ - GPS("GPS", "GP", "G", ts -> ts.getGPS()), + GPS("GPS", "GP", "G", TimeScales::getGPS), /** GLONASS. */ - GLONASS("GLO", "GL", "R", ts -> ts.getGLONASS()), + GLONASS("GLO", "GL", "R", TimeScales::getGLONASS), /** GALILEO. */ - GALILEO("GAL", "GA", "E", ts -> ts.getGST()), + GALILEO("GAL", "GA", "E", TimeScales::getGST), /** International Atomic Time. */ - TAI("TAI", null, null, ts -> ts.getTAI()), + TAI("TAI", null, null, TimeScales::getTAI), /** Coordinated Universal Time. */ - UTC("UTC", "UT", null, ts -> ts.getUTC()), + UTC("UTC", "UT", null, TimeScales::getUTC), /** Quasi-Zenith System. */ - QZSS("QZS", "QZ", "J", ts -> ts.getQZSS()), + QZSS("QZS", "QZ", "J", TimeScales::getQZSS), /** Beidou. */ - BEIDOU("BDT", "BD", "C", ts -> ts.getBDT()), + BEIDOU("BDT", "BD", "C", TimeScales::getBDT), /** IRNSS. */ - IRNSS("IRN", "IR", "I", ts -> ts.getIRNSS()), + IRNSS("IRN", "IR", "I", TimeScales::getIRNSS), /** SBAS. * @since 12.0 */ - SBAS(null, "SB", "S", ts -> ts.getUTC()), + SBAS(null, "SB", "S", TimeScales::getUTC), /** GMT (should only by used in RUN BY / DATE entries). * @since 12.0 */ - GMT("GMT", null, null, ts -> ts.getUTC()), + GMT("GMT", null, null, TimeScales::getUTC), /** Unknown (should only by used in RUN BY / DATE entries). */ - UNKNOWN("LCL", null, null, ts -> ts.getGPS()); + UNKNOWN("LCL", null, null, TimeScales::getGPS); /** Parsing key map. */ private static final Map KEYS_MAP = new HashMap<>(); diff --git a/src/main/java/org/orekit/gnss/attitude/GPSBlockIIA.java b/src/main/java/org/orekit/gnss/attitude/GPSBlockIIA.java index 71b5f22602..88b03e357b 100644 --- a/src/main/java/org/orekit/gnss/attitude/GPSBlockIIA.java +++ b/src/main/java/org/orekit/gnss/attitude/GPSBlockIIA.java @@ -159,7 +159,7 @@ protected > TimeStampedFieldAngularCoordinates // noon beta angle limit from yaw rate final T aNoon = FastMath.atan(context.getMuRate().divide(yawRate)); - final T aNight = field.getZero().add(NIGHT_TURN_LIMIT); + final T aNight = field.getZero().newInstance(NIGHT_TURN_LIMIT); final double cNoon = FastMath.cos(aNoon.getReal()); final double cNight = FastMath.cos(aNight.getReal()); @@ -183,16 +183,16 @@ protected > TimeStampedFieldAngularCoordinates if (beta.getReal() > 0 && beta.getReal() < yawBias) { // noon turn problem for small positive beta in block IIA // rotation is in the wrong direction for these spacecrafts - phiDot = field.getZero().add(FastMath.copySign(yawRate, beta.getReal())); + phiDot = field.getZero().newInstance(FastMath.copySign(yawRate, beta.getReal())); linearPhi = phiStart.add(phiDot.multiply(dtStart)); } else { // regular noon turn - phiDot = field.getZero().add(-FastMath.copySign(yawRate, beta.getReal())); + phiDot = field.getZero().newInstance(-FastMath.copySign(yawRate, beta.getReal())); linearPhi = phiStart.add(phiDot.multiply(dtStart)); } } else { // midnight turn - phiDot = field.getZero().add(yawRate); + phiDot = field.getZero().newInstance(yawRate); linearPhi = phiStart.add(phiDot.multiply(dtStart)); } diff --git a/src/main/java/org/orekit/gnss/attitude/GPSBlockIIF.java b/src/main/java/org/orekit/gnss/attitude/GPSBlockIIF.java index 922d90d49c..ddeb35e849 100644 --- a/src/main/java/org/orekit/gnss/attitude/GPSBlockIIF.java +++ b/src/main/java/org/orekit/gnss/attitude/GPSBlockIIF.java @@ -140,7 +140,7 @@ protected > TimeStampedFieldAngularCoordinates // noon beta angle limit from yaw rate final T aNoon = FastMath.atan(context.getMuRate().divide(yawRate)); - final T aNight = field.getZero().add(NIGHT_TURN_LIMIT); + final T aNight = field.getZero().newInstance(NIGHT_TURN_LIMIT); final double cNoon = FastMath.cos(aNoon.getReal()); final double cNight = FastMath.cos(aNight.getReal()); @@ -164,11 +164,11 @@ protected > TimeStampedFieldAngularCoordinates if (beta.getReal() > yawBias && beta.getReal() < 0) { // noon turn problem for small negative beta in block IIF // rotation is in the wrong direction for these spacecrafts - phiDot = field.getZero().add(FastMath.copySign(yawRate, beta.getReal())); + phiDot = field.getZero().newInstance(FastMath.copySign(yawRate, beta.getReal())); linearPhi = phiStart.add(phiDot.multiply(dtStart)); } else { // regular noon turn - phiDot = field.getZero().add(-FastMath.copySign(yawRate, beta.getReal())); + phiDot = field.getZero().newInstance(-FastMath.copySign(yawRate, beta.getReal())); linearPhi = phiStart.add(phiDot.multiply(dtStart)); } } else { diff --git a/src/main/java/org/orekit/gnss/attitude/GPSBlockIIR.java b/src/main/java/org/orekit/gnss/attitude/GPSBlockIIR.java index fdd2fa7f7d..15f48c1448 100644 --- a/src/main/java/org/orekit/gnss/attitude/GPSBlockIIR.java +++ b/src/main/java/org/orekit/gnss/attitude/GPSBlockIIR.java @@ -135,11 +135,11 @@ protected > TimeStampedFieldAngularCoordinates if (context.inSunSide()) { // noon turn - phiDot = field.getZero().add(-FastMath.copySign(yawRate, beta.getReal())); + phiDot = field.getZero().newInstance(-FastMath.copySign(yawRate, beta.getReal())); linearPhi = phiStart.add(phiDot.multiply(dtStart)); } else { // midnight turn - phiDot = field.getZero().add(FastMath.copySign(yawRate, beta.getReal())); + phiDot = field.getZero().newInstance(FastMath.copySign(yawRate, beta.getReal())); linearPhi = phiStart.add(phiDot.multiply(dtStart)); } diff --git a/src/main/java/org/orekit/gnss/attitude/Galileo.java b/src/main/java/org/orekit/gnss/attitude/Galileo.java index 9c014e5783..a8aff7bd4f 100644 --- a/src/main/java/org/orekit/gnss/attitude/Galileo.java +++ b/src/main/java/org/orekit/gnss/attitude/Galileo.java @@ -82,7 +82,7 @@ protected TimeStampedAngularCoordinates correctedYaw(final GNSSAttitudeContext c // noon beta angle limit from yaw rate final double beta0 = FastMath.atan(context.getMuRate() / yawRate); - if (FastMath.abs(context.beta(context.getDate())) < beta0 && + if (FastMath.abs(context.betaD2().getValue()) < beta0 && context.setUpTurnRegion(COS_NIGHT, COS_NOON)) { context.setHalfSpan(context.inSunSide() ? @@ -127,7 +127,7 @@ protected > TimeStampedFieldAngularCoordinates context.setUpTurnRegion(COS_NIGHT, COS_NOON)) { final Field field = context.getDate().getField(); - final T betaX = field.getZero().add(BETA_X); + final T betaX = field.getZero().newInstance(BETA_X); context.setHalfSpan(context.inSunSide() ? betaX : context.inOrbitPlaneAbsoluteAngle(betaX), diff --git a/src/main/java/org/orekit/gnss/attitude/Glonass.java b/src/main/java/org/orekit/gnss/attitude/Glonass.java index 59859a08ac..9a0e8d918f 100644 --- a/src/main/java/org/orekit/gnss/attitude/Glonass.java +++ b/src/main/java/org/orekit/gnss/attitude/Glonass.java @@ -152,7 +152,7 @@ protected > TimeStampedFieldAngularCoordinates // noon beta angle limit from yaw rate final T realBeta = context.beta(context.getDate()); final T muRate = context.getMuRate(); - final T aNight = field.getZero().add(NIGHT_TURN_LIMIT); + final T aNight = field.getZero().newInstance(NIGHT_TURN_LIMIT); T aNoon = FastMath.atan(muRate.divide(yawRate)); if (FastMath.abs(realBeta).getReal() < aNoon.getReal()) { final CalculusFieldUnivariateFunction f = yawEnd -> { @@ -161,11 +161,11 @@ protected > TimeStampedFieldAngularCoordinates subtract(context.computePhi(realBeta, delta.negate()))). multiply(0.5)); }; - final T[] bracket = UnivariateSolverUtils.bracket(f, field.getZero().add(YAW_END_ZERO), + final T[] bracket = UnivariateSolverUtils.bracket(f, field.getZero().newInstance(YAW_END_ZERO), field.getZero(), field.getZero().getPi()); - final T yawEnd = new FieldBracketingNthOrderBrentSolver<>(field.getZero().add(1.0e-14), - field.getZero().add(1.0e-8), - field.getZero().add(1.0e-15), + final T yawEnd = new FieldBracketingNthOrderBrentSolver<>(field.getZero().newInstance(1.0e-14), + field.getZero().newInstance(1.0e-8), + field.getZero().newInstance(1.0e-15), 5). solve(50, f, bracket[0], bracket[1], AllowedSolution.ANY_SIDE); aNoon = muRate.multiply(yawEnd).divide(yawRate); @@ -192,11 +192,11 @@ protected > TimeStampedFieldAngularCoordinates final T phiEnd = context.getYawEnd(beta); if (context.inSunSide()) { // noon turn - phiDot = field.getZero().add(-FastMath.copySign(yawRate, beta.getReal())); + phiDot = field.getZero().newInstance(-FastMath.copySign(yawRate, beta.getReal())); linearPhi = phiStart.add(phiDot.multiply(dtStart)); } else { // midnight turn - phiDot = field.getZero().add(FastMath.copySign(yawRate, beta.getReal())); + phiDot = field.getZero().newInstance(FastMath.copySign(yawRate, beta.getReal())); linearPhi = phiStart.add(phiDot.multiply(dtStart)); // this turn limitation is only computed for midnight turns in Kouba model diff --git a/src/main/java/org/orekit/gnss/metric/ntrip/NavigationSystem.java b/src/main/java/org/orekit/gnss/metric/ntrip/NavigationSystem.java index ff9b217afd..7e25a5ba6b 100644 --- a/src/main/java/org/orekit/gnss/metric/ntrip/NavigationSystem.java +++ b/src/main/java/org/orekit/gnss/metric/ntrip/NavigationSystem.java @@ -46,6 +46,9 @@ public enum NavigationSystem { /** SBAS. */ SBAS("SBAS"), + /** IRNSS. */ + IRS("IRS", "IRNSS"), + /** No navigation system for this stream. */ EMPTY(""); diff --git a/src/main/java/org/orekit/gnss/metric/ntrip/StreamMonitor.java b/src/main/java/org/orekit/gnss/metric/ntrip/StreamMonitor.java index 5a6084a8da..775c5f78f3 100644 --- a/src/main/java/org/orekit/gnss/metric/ntrip/StreamMonitor.java +++ b/src/main/java/org/orekit/gnss/metric/ntrip/StreamMonitor.java @@ -180,13 +180,7 @@ public void addObserver(final int typeCode, final MessageObserver observer) { synchronized (observers) { // register the observer - List list = observers.get(typeCode); - if (list == null) { - // create a new list the first time we register an observer for a message - list = new ArrayList<>(); - observers.put(typeCode, list); - } - list.add(observer); + observers.computeIfAbsent(typeCode, tc -> new ArrayList<>()).add(observer); // if we already have a message of the proper type // immediately notify the new observer about it diff --git a/src/main/java/org/orekit/models/earth/Geoid.java b/src/main/java/org/orekit/models/earth/Geoid.java index bde307050a..0dacf98d11 100644 --- a/src/main/java/org/orekit/models/earth/Geoid.java +++ b/src/main/java/org/orekit/models/earth/Geoid.java @@ -520,9 +520,9 @@ public > FieldGeodeticPoint getIntersection } // solve line search problem to find the intersection final FieldBracketingNthOrderBrentSolver solver = - new FieldBracketingNthOrderBrentSolver<>(field.getZero().add(1.0e-14), - field.getZero().add(1.0e-6), - field.getZero().add(1.0e-15), + new FieldBracketingNthOrderBrentSolver<>(field.getZero().newInstance(1.0e-14), + field.getZero().newInstance(1.0e-6), + field.getZero().newInstance(1.0e-15), 5); try { final T abscissa = solver.solve(MAX_EVALUATIONS, heightFunction, lowPoint, highPoint, diff --git a/src/main/java/org/orekit/models/earth/atmosphere/Atmosphere.java b/src/main/java/org/orekit/models/earth/atmosphere/Atmosphere.java index 784513010d..f92c323c33 100644 --- a/src/main/java/org/orekit/models/earth/atmosphere/Atmosphere.java +++ b/src/main/java/org/orekit/models/earth/atmosphere/Atmosphere.java @@ -21,9 +21,9 @@ import org.hipparchus.CalculusFieldElement; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.orekit.frames.FieldTransform; +import org.orekit.frames.FieldKinematicTransform; import org.orekit.frames.Frame; -import org.orekit.frames.Transform; +import org.orekit.frames.KinematicTransform; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.FieldPVCoordinates; @@ -68,10 +68,10 @@ public interface Atmosphere extends Serializable { * @return velocity (m/s) (defined in the same frame as the position) */ default Vector3D getVelocity(AbsoluteDate date, Vector3D position, Frame frame) { - final Transform bodyToFrame = getFrame().getTransformTo(frame, date); - final Vector3D posInBody = bodyToFrame.toStaticTransform().getInverse().transformPosition(position); + final KinematicTransform bodyToFrame = getFrame().getKinematicTransformTo(frame, date); + final Vector3D posInBody = bodyToFrame.getStaticInverse().transformPosition(position); final PVCoordinates pvBody = new PVCoordinates(posInBody, Vector3D.ZERO); - final PVCoordinates pvFrame = bodyToFrame.transformPVCoordinates(pvBody); + final PVCoordinates pvFrame = bodyToFrame.transformOnlyPV(pvBody); return pvFrame.getVelocity(); } @@ -82,12 +82,14 @@ default Vector3D getVelocity(AbsoluteDate date, Vector3D position, Frame frame) * @param instance of CalculusFieldElement * @return velocity (m/s) (defined in the same frame as the position) */ - default > FieldVector3D getVelocity(FieldAbsoluteDate date, FieldVector3D position, Frame frame) { - final FieldTransform bodyToFrame = getFrame().getTransformTo(frame, date); - final FieldVector3D posInBody = bodyToFrame.toStaticTransform().getInverse().transformPosition(position); + default > FieldVector3D getVelocity(FieldAbsoluteDate date, + FieldVector3D position, + Frame frame) { + final FieldKinematicTransform bodyToFrame = getFrame().getKinematicTransformTo(frame, date); + final FieldVector3D posInBody = bodyToFrame.getStaticInverse().transformPosition(position); final FieldPVCoordinates pvBody = new FieldPVCoordinates<>(posInBody, FieldVector3D.getZero(date.getField())); - final FieldPVCoordinates pvFrame = bodyToFrame.transformPVCoordinates(pvBody); + final FieldPVCoordinates pvFrame = bodyToFrame.transformOnlyPV(pvBody); return pvFrame.getVelocity(); } diff --git a/src/main/java/org/orekit/models/earth/atmosphere/DTM2000.java b/src/main/java/org/orekit/models/earth/atmosphere/DTM2000.java index 1b5ec6f631..f5e153a474 100644 --- a/src/main/java/org/orekit/models/earth/atmosphere/DTM2000.java +++ b/src/main/java/org/orekit/models/earth/atmosphere/DTM2000.java @@ -16,13 +16,6 @@ */ package org.orekit.models.earth.atmosphere; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; - import org.hipparchus.CalculusFieldElement; import org.hipparchus.exception.DummyLocalizable; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; @@ -44,6 +37,13 @@ import org.orekit.time.TimeScale; import org.orekit.utils.PVCoordinatesProvider; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; + /** This atmosphere model is the realization of the DTM-2000 model. *

    * It is described in the paper:
    @@ -638,7 +638,7 @@ private static class Computation { for (int i = 1; i <= 6; i++) { final double gamma = MA[i] * glb; final double upapg = 1.0 + ALEFA[i] + gamma; - final double fzI = FastMath.pow(t120tz, upapg) * FastMath.exp(-sigzeta * gamma); + final double fzI = FastMath.exp(FastMath.log(t120tz) * upapg - sigzeta * gamma); // concentrations of H, He, O, N2, O2, N (particles/cm³) final double ccI = dbase[i] * fzI; // contribution of densities of H, He, O, N2, O2, N (g/cm³) @@ -973,8 +973,8 @@ private static class FieldComputation> { // compute Legendre polynomials wrt geographic pole final T c = scLat.sin(); - final T c2 = c.multiply(c); - final T c4 = c2.multiply(c2); + final T c2 = c.square(); + final T c4 = c2.square(); final T s = scLat.cos(); final T s2 = s.multiply(s); p10 = c; @@ -1102,7 +1102,7 @@ private static class FieldComputation> { for (int i = 1; i <= 6; i++) { final T gamma = glb.multiply(MA[i]); final T upapg = gamma.add(1.0 + ALEFA[i]); - final T fzI = t120tz.pow(upapg).multiply(sigzeta.negate().multiply(gamma).exp()); + final T fzI = (t120tz.log().multiply(upapg).subtract(sigzeta.multiply(gamma))).exp(); // concentrations of H, He, O, N2, O2, N (particles/cm³) final T ccI = dbase[i].multiply(fzI); // contribution of densities of H, He, O, N2, O2, N (g/cm³) @@ -1154,8 +1154,8 @@ private T gFunction(final double[] a, final T[] da, fmfb[2] = f[2] - fbar[2]; fbm150[1] = fbar[1] - 150.0; fbm150[2] = fbar[2]; - da[4] = zero.add(fmfb[1]); - da[6] = zero.add(fbm150[1]); + da[4] = zero.newInstance(fmfb[1]); + da[6] = zero.newInstance(fbm150[1]); da[4] = da[4].add(a[70] * fmfb[2]); da[6] = da[6].add(a[71] * fbm150[2]); da[70] = da[4].multiply(a[ 5]).multiply(2). @@ -1185,7 +1185,7 @@ private T gFunction(final double[] a, final T[] da, final T c2fi = p10mg.multiply(p10mg).negate().add(1); final T dkp = c2fi.multiply(a[ikp + 1]).add(a[ikp]).multiply(akp[2]).add(akp[1]); T dakp = p20mg.multiply(a[8]).add(p40mg.multiply(a[68])). - add(p20mg.multiply(a[61]).add(dkp.multiply(dkp).multiply(2 * a[75]).add(a[60])).multiply(dkp.multiply(2))). + add(p20mg.multiply(a[61]).add(dkp.square().multiply(2 * a[75]).add(a[60])).multiply(dkp.multiply(2))). add(a[7]); da[ikp] = dakp.multiply(akp[2]); da[ikp + 1] = da[ikp].multiply(c2fi); @@ -1197,15 +1197,15 @@ private T gFunction(final double[] a, final T[] da, da[7] = dkp; da[8] = p20mg.multiply(dkp); da[68] = p40mg.multiply(dkp); - da[60] = dkp.multiply(dkp); + da[60] = dkp.square(); da[61] = p20mg.multiply(da[60]); - da[75] = da[60].multiply(da[60]); - da[64] = zero.add(dkpm); + da[75] = da[60].square(); + da[64] = zero.newInstance(dkpm); da[65] = p20mg.multiply(dkpm); da[72] = p40mg.multiply(dkpm); - da[66] = zero.add(dkpm * dkpm); + da[66] = zero.newInstance(dkpm * dkpm); da[73] = p20mg.multiply(da[66]); - da[76] = da[66].multiply(da[66]); + da[76] = da[66].square(); // non-periodic g(l) function T f0 = da[4].multiply(a[4]). @@ -1240,10 +1240,10 @@ private T gFunction(final double[] a, final T[] da, add(da[78].multiply(a78)). add(da[79].multiply(a[79])); // termes annuels symetriques en latitude - da[9] = zero.add(FastMath.cos(ROT * (day - a[11]))); + da[9] = zero.newInstance(FastMath.cos(ROT * (day - a[11]))); da[10] = p20.multiply(da[9]); // termes semi-annuels symetriques en latitude - da[12] = zero.add(FastMath.cos(ROT2 * (day - a[14]))); + da[12] = zero.newInstance(FastMath.cos(ROT2 * (day - a[14]))); da[13] = p20.multiply(da[12]); // termes annuels non symetriques en latitude final double coste = FastMath.cos(ROT * (day - a[18])); diff --git a/src/main/java/org/orekit/models/earth/atmosphere/HarrisPriester.java b/src/main/java/org/orekit/models/earth/atmosphere/HarrisPriester.java index d94c671515..095ca13803 100644 --- a/src/main/java/org/orekit/models/earth/atmosphere/HarrisPriester.java +++ b/src/main/java/org/orekit/models/earth/atmosphere/HarrisPriester.java @@ -361,13 +361,13 @@ public > T getDensity(final Vector3D sunInEart final T dH = posAlt.negate().add(tabAltRho[ia][0]).divide(tabAltRho[ia][0] - tabAltRho[ia + 1][0]); // Min exponential density interpolation - final T rhoMin = zero.add(tabAltRho[ia + 1][1] / tabAltRho[ia][1]).pow(dH).multiply(tabAltRho[ia][1]); + final T rhoMin = zero.newInstance(tabAltRho[ia + 1][1] / tabAltRho[ia][1]).pow(dH).multiply(tabAltRho[ia][1]); if (Precision.equals(cosPow.getReal(), 0.)) { - return zero.add(rhoMin); + return rhoMin; } else { // Max exponential density interpolation - final T rhoMax = zero.add(tabAltRho[ia + 1][2] / tabAltRho[ia][2]).pow(dH).multiply(tabAltRho[ia][2]); + final T rhoMax = zero.newInstance(tabAltRho[ia + 1][2] / tabAltRho[ia][2]).pow(dH).multiply(tabAltRho[ia][2]); return rhoMin.add(rhoMax.subtract(rhoMin).multiply(cosPow)); } @@ -448,7 +448,7 @@ private > T getHeight(final FieldVector3D p final double e2 = f * (2. - f); final T r = position.getNorm(); final T sl = position.getZ().divide(r); - final T cl2 = sl.multiply(sl).negate().add(1.); + final T cl2 = sl.square().negate().add(1.); final T coef = cl2.multiply(-e2).add(1.).reciprocal().multiply(1. - e2).sqrt(); return r.subtract(coef.multiply(a)); 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 2bbd4b9376..c894024360 100644 --- a/src/main/java/org/orekit/models/earth/atmosphere/JB2008.java +++ b/src/main/java/org/orekit/models/earth/atmosphere/JB2008.java @@ -544,9 +544,9 @@ public > T getDensity(final T dateMJD, final T // Equation (17) final T cos = eta.cos(); - final T cosEta = cos.multiply(cos).multiply(cos.sqrt()); + final T cosEta = cos.square().multiply(cos.sqrt()); final T sin = theta.sin(); - final T sinTeta = sin.multiply(sin).multiply(sin.sqrt()); + final T sinTeta = sin.square().multiply(sin.sqrt()); final T cosTau = tau.multiply(0.5).cos().abs(); final T df = sinTeta.add(cosEta.subtract(sinTeta).multiply(cosTau).multiply(cosTau).multiply(cosTau)); final T tsubl = df.multiply(0.31).add(1).multiply(tsubc); @@ -576,7 +576,7 @@ public > T getDensity(final T dateMJD, final T tc[3] = gsubx.divide(tc[2]); // Equation (5) - final T z1 = field.getZero().add(90.); + final T z1 = field.getZero().newInstance(90.); final T z2 = min(105.0, altKm); T al = z2.divide(z1).log(); int n = 1 + (int) FastMath.floor(al.getReal() / R1); @@ -826,7 +826,7 @@ private static > T dTc(final double f10, final final T h = satAlt.subtract(200.0).divide(50.0); dTc = poly1CDTC(fs, st, cs).multiply(h).add(poly2CDTC(fs, st, cs)); } else if (satAlt.getReal() > 240.0 && satAlt.getReal() <= 300.0) { - final T h = solarTime.getField().getZero().add(0.8); + final T h = solarTime.getField().getZero().newInstance(0.8); final T bb = poly1CDTC(fs, st, cs); final T aa = bb.multiply(h).add(poly2CDTC(fs, st, cs)); final T p2BDT = poly2BDTC(st); @@ -998,7 +998,7 @@ private static double mBar(final double z) { */ private static > T mBar(final T z) { final T dz = z.subtract(100.); - T amb = z.getField().getZero().add(CMB[6]); + T amb = z.getField().getZero().newInstance(CMB[6]); for (int i = 5; i >= 0; --i) { amb = dz.multiply(amb).add(CMB[i]); } @@ -1182,7 +1182,7 @@ private static > T dayOfYear(final T dateMJD) * @return min value */ private > T min(final double d, final T f) { - return (f.getReal() > d) ? f.getField().getZero().add(d) : f; + return (f.getReal() > d) ? f.getField().getZero().newInstance(d) : f; } /** Compute max of two values, one double and one field element. @@ -1192,7 +1192,7 @@ private > T min(final double d, final T f) { * @return max value */ private > T max(final double d, final T f) { - return (f.getReal() <= d) ? f.getField().getZero().add(d) : f; + return (f.getReal() <= d) ? f.getField().getZero().newInstance(d) : f; } /** Get the local density. diff --git a/src/main/java/org/orekit/models/earth/atmosphere/NRLMSISE00.java b/src/main/java/org/orekit/models/earth/atmosphere/NRLMSISE00.java index ef83d95eae..bfaf9b11e9 100644 --- a/src/main/java/org/orekit/models/earth/atmosphere/NRLMSISE00.java +++ b/src/main/java/org/orekit/models/earth/atmosphere/NRLMSISE00.java @@ -16,10 +16,8 @@ */ package org.orekit.models.earth.atmosphere; -import java.util.Arrays; - -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.exception.LocalizedCoreFormats; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Vector3D; @@ -43,6 +41,8 @@ import org.orekit.utils.IERSConventions; import org.orekit.utils.PVCoordinatesProvider; +import java.util.Arrays; + /** This class implements the mathematical representation of the 2001 * Naval Research Laboratory Mass Spectrometer and Incoherent Scatter @@ -998,6 +998,9 @@ public class NRLMSISE00 implements Atmosphere { 2.23000e+02, 2.86760e+02, -2.93940e+00, 2.50000e+00, 0.00000e+00 }; + /** NRLMSISE-00 minimum temperature, used in many cases in density computation. */ + private static final double MIN_TEMP = 50.; + // Fields /** External data container. */ @@ -2484,7 +2487,7 @@ private double densm(final double alt, final double d0, final double xm) { /* Integrate temperature profile */ final double yi = splini(xs, ys, y2out, x); - final double expl = FastMath.min(50., gamm * yi); + final double expl = FastMath.min(MIN_TEMP, gamm * yi); /* Density at altitude */ densm *= (t1 / tz) * FastMath.exp(-expl); @@ -2530,7 +2533,7 @@ private double densm(final double alt, final double d0, final double xm) { /* Integrate temperature profile */ final double yi = splini(xs, ys, y2out, x); - final double expl = FastMath.min(50., gamm * yi); + final double expl = FastMath.min(MIN_TEMP, gamm * yi); /* Density at altitude */ densm *= (t1 / tz) * FastMath.exp(-expl); @@ -2608,15 +2611,24 @@ private double densu(final double alt, final double dlb, final double tinf, /* calculate density above za */ double glb = galt(zlb); double gamma = xm * glb / (R_GAS * s2 * tinf); - double expl = (tt <= 0) ? 50. : FastMath.min(50., FastMath.exp(-s2 * gamma * zg2)); - double densu = dlb * FastMath.pow(tlb / tt, 1.0 + alpha + gamma) * expl; + double expl = (tt <= 0) ? MIN_TEMP : FastMath.min(MIN_TEMP, FastMath.exp(-s2 * gamma * zg2)); + double densu = dlb * expl * FastMath.pow(tlb / tt, 1.0 + alpha + gamma); + + // Correction for issue 1365 - protection against "densu" being infinite + if (!Double.isFinite(densu)) { + if (expl < MIN_TEMP) { + densu = dlb * FastMath.exp(FastMath.log(tlb / tt) * (1.0 + alpha + gamma) - s2 * gamma * zg2); + } else { + throw new OrekitException( OrekitMessages.INFINITE_NRLMSISE00_DENSITY); + } + } /* calculate density below za */ if (alt < ZN1[0]) { glb = galt(ZN1[0]); gamma = xm * glb * zgdif / R_GAS; /* integrate spline temperatures */ - expl = (tz <= 0) ? 50.0 : FastMath.min(50., gamma * splini(xs, ys, y2out, x)); + expl = (tz <= 0) ? MIN_TEMP : FastMath.min(MIN_TEMP, gamma * splini(xs, ys, y2out, x)); /* correct density at altitude */ densu *= FastMath.pow(meso_tn1[0] / tz, 1.0 + alpha) * FastMath.exp(-expl); } @@ -2825,7 +2837,7 @@ public class FieldOutput> { temperatures = MathArrays.buildArray(field, 2); // Calculates latitude variable gravity and effective radius - final T xlat = (sw[2] == 0) ? zero.add(LAT_REF) : lat; + final T xlat = (sw[2] == 0) ? zero.newInstance(LAT_REF) : lat; final T c2 = xlat.multiply(2 * DEG_TO_RAD).cos(); glat = c2.multiply(-0.0026373).add(1).multiply(G_REF); rlat = glat.multiply(2).divide(c2.multiply(2.27e-9).add(3.085462e-6)).multiply(1.e-5); @@ -2921,7 +2933,7 @@ void gts7(final T alt) { final double xmm = PDM[2][4]; /**** Exospheric temperature ****/ - T tinf = zero.add(PTM[0] * PT[0]); + T tinf = zero.newInstance(PTM[0] * PT[0]); // Tinf variations not important below ZA or ZN[0] if (alt.getReal() > ZN1[0]) { tinf = tinf.multiply(globe7(PT).multiply(sw[16]).add(1)); @@ -2929,24 +2941,24 @@ void gts7(final T alt) { setTemperature(EXOSPHERIC, tinf); // Gradient variations not important below ZN[4] - T g0 = zero.add(PTM[3] * PS[0]); + T g0 = zero.newInstance(PTM[3] * PS[0]); if (alt.getReal() > ZN1[4]) { g0 = g0.multiply(globe7(PS).multiply(sw[19]).add(1)); } // Temperature at lower boundary - T tlb = zero.add(PTM[1] * PD[3][0]); + T tlb = zero.newInstance(PTM[1] * PD[3][0]); tlb = tlb.multiply(globe7(PD[3]).multiply(sw[17]).add(1)); // Slope final T s = g0.divide(tinf.subtract(tlb)); // Lower thermosphere temp variations not significant for density above 300 km - meso_tn1[1] = zero.add(PTM[6] * PTL[0][0]); - meso_tn1[2] = zero.add(PTM[2] * PTL[1][0]); - meso_tn1[3] = zero.add(PTM[7] * PTL[2][0]); - meso_tn1[4] = zero.add(PTM[4] * PTL[3][0]); - meso_tgn1[1] = zero.add(PTM[8] * PMA[8][0]); + meso_tn1[1] = zero.newInstance(PTM[6] * PTL[0][0]); + meso_tn1[2] = zero.newInstance(PTM[2] * PTL[1][0]); + meso_tn1[3] = zero.newInstance(PTM[7] * PTL[2][0]); + meso_tn1[4] = zero.newInstance(PTM[4] * PTL[3][0]); + meso_tgn1[1] = zero.newInstance(PTM[8] * PMA[8][0]); if (alt.getReal() < 300.0) { final double r = PTM[4] * PTL[3][0]; meso_tn1[1] = meso_tn1[1].divide(glob7s(PTL[0]).multiply(sw[18] ).negate().add(1)); @@ -2958,7 +2970,7 @@ void gts7(final T alt) { } /**** Temperature at altitude ****/ - setTemperature(ALTITUDE, densu(alt, zero.add(1.0), tinf, tlb, 0, 0, PTM[5], s)); + setTemperature(ALTITUDE, densu(alt, zero.newInstance(1.0), tinf, tlb, 0, 0, PTM[5], s)); /**** N2 density ****/ /* Density variation factor at Zlb */ @@ -2999,7 +3011,7 @@ void gts7(final T alt) { /* Turbopause */ final double zh04 = PDM[0][2]; /* Mixed density at Zlb */ - final T b04 = densu(zero.add(zh04), db04, tinf, tlb, HE_MASS - xmm, alpha[0] - 1., PTM[5], s); + final T b04 = densu(zero.newInstance(zh04), db04, tinf, tlb, HE_MASS - xmm, alpha[0] - 1., PTM[5], s); /* Mixed density at Alt */ final T dm04 = densu(alt, b04, tinf, tlb, xmm, 0., PTM[5], s); final double zhm04 = zhm28; @@ -3025,7 +3037,7 @@ void gts7(final T alt) { /* Turbopause */ final double zh16 = PDM[1][2]; /* Mixed density at Zlb */ - final T b16 = densu(zero.add(zh16), db16, tinf, tlb, O_MASS - xmm, alpha[1] - 1.0, PTM[5], s); + final T b16 = densu(zero.newInstance(zh16), db16, tinf, tlb, O_MASS - xmm, alpha[1] - 1.0, PTM[5], s); /* Mixed density at Alt */ final T dm16 = densu(alt, b16, tinf, tlb, xmm, 0., PTM[5], s); final double zhm16 = zhm28; @@ -3041,7 +3053,7 @@ void gts7(final T alt) { final double zcc16 = PDM[1][6] * PDL[1][12]; final double rc16 = PDM[1][3] * PDL[1][14]; /* Net density corrected at Alt */ - setDensity(ATOMIC_OXYGEN, diffusiveDensity.multiply(ccor(alt, zero.add(rc16), hcc16, zcc16))); + setDensity(ATOMIC_OXYGEN, diffusiveDensity.multiply(ccor(alt, zero.newInstance(rc16), hcc16, zcc16))); } /**** O2 density ****/ @@ -3057,7 +3069,7 @@ void gts7(final T alt) { /* Turbopause */ final double zh32 = PDM[3][2]; /* Mixed density at Zlb */ - final T b32 = densu(zero.add(zh32), db32, tinf, tlb, O2_MASS - xmm, alpha[3] - 1., PTM[5], s); + final T b32 = densu(zero.newInstance(zh32), db32, tinf, tlb, O2_MASS - xmm, alpha[3] - 1., PTM[5], s); /* Mixed density at Alt */ final T dm32 = densu(alt, b32, tinf, tlb, xmm, 0., PTM[5], s); final double zhm32 = zhm28; @@ -3090,7 +3102,7 @@ void gts7(final T alt) { /* Turbopause */ final double zh40 = PDM[4][2]; /* Mixed density at Zlb */ - final T b40 = densu(zero.add(zh40), db40, tinf, tlb, AR_MASS - xmm, alpha[4] - 1., PTM[5], s); + final T b40 = densu(zero.newInstance(zh40), db40, tinf, tlb, AR_MASS - xmm, alpha[4] - 1., PTM[5], s); /* Mixed density at Alt */ final T dm40 = densu(alt, b40, tinf, tlb, xmm, 0., PTM[5], s); final double zhm40 = zhm28; @@ -3116,7 +3128,7 @@ void gts7(final T alt) { /* Turbopause */ final double zh01 = PDM[5][2]; /* Mixed density at Zlb */ - final T b01 = densu(zero.add(zh01), db01, tinf, tlb, H_MASS - xmm, alpha[6] - 1., PTM[5], s); + final T b01 = densu(zero.newInstance(zh01), db01, tinf, tlb, H_MASS - xmm, alpha[6] - 1., PTM[5], s); /* Mixed density at Alt */ final T dm01 = densu(alt, b01, tinf, tlb, xmm, 0., PTM[5], s); final double zhm01 = zhm28; @@ -3132,7 +3144,7 @@ void gts7(final T alt) { final double zcc01 = PDM[5][6] * PDL[1][18]; final double rc01 = PDM[5][3] * PDL[1][20]; /* Net density corrected at Alt */ - setDensity(HYDROGEN, diffusiveDensity.multiply(ccor(alt, zero.add(rc01), hcc01, zcc01))); + setDensity(HYDROGEN, diffusiveDensity.multiply(ccor(alt, zero.newInstance(rc01), hcc01, zcc01))); } /**** N density ****/ @@ -3147,7 +3159,7 @@ void gts7(final T alt) { /* Turbopause */ final double zh14 = PDM[6][2]; /* Mixed density at Zlb */ - final T b14 = densu(zero.add(zh14), db14, tinf, tlb, N_MASS - xmm, alpha[7] - 1., PTM[5], s); + final T b14 = densu(zero.newInstance(zh14), db14, tinf, tlb, N_MASS - xmm, alpha[7] - 1., PTM[5], s); /* Mixed density at Alt */ final T dm14 = densu(alt, b14, tinf, tlb, xmm, 0., PTM[5], s); final double zhm14 = zhm28; @@ -3163,14 +3175,14 @@ void gts7(final T alt) { final double zcc14 = PDM[6][6] * PDL[0][3]; final double rc14 = PDM[6][3] * PDL[0][5]; /* Net density corrected at Alt */ - setDensity(ATOMIC_NITROGEN, diffusiveDensity.multiply(ccor(alt, zero.add(rc14), hcc14, zcc14))); + setDensity(ATOMIC_NITROGEN, diffusiveDensity.multiply(ccor(alt, zero.newInstance(rc14), hcc14, zcc14))); } /**** Anomalous O density ****/ final T g16h = globe7(PD[8]).multiply(sw[21]); final T db16h = g16h.exp().multiply(PDM[7][0] * PD[8][0]); final double tho = PDM[7][9] * PDL[0][6]; - diffusiveDensity = densu(alt, db16h, zero.add(tho), zero.add(tho), O_MASS, alpha[8], PTM[5], s); + diffusiveDensity = densu(alt, db16h, zero.newInstance(tho), zero.newInstance(tho), O_MASS, alpha[8], PTM[5], s); final double zsht = PDM[7][5]; final double zmho = PDM[7][4]; final T zsho = scalh(zmho, O_MASS, tho); @@ -3219,7 +3231,7 @@ void gts7(final T alt) { void gtd7(final T alt) { // Calculates for thermosphere/mesosphere (above ZN2[0]) - final T altt = (alt.getReal() > ZN2[0]) ? alt : zero.add(ZN2[0]); + final T altt = (alt.getReal() > ZN2[0]) ? alt : zero.newInstance(ZN2[0]); gts7(altt); if (alt.getReal() >= ZN2[0]) { return; @@ -3404,10 +3416,10 @@ private T globe7(final double[] p) { // F10.7 effect final double df = f107 - f107a; final double dfa = f107a - FLUX_REF; - t[0] = zero.add(p[19] * df * (1.0 + p[59] * dfa) + - p[20] * df * df + - p[21] * dfa + - p[29] * dfa * dfa); + t[0] = zero.newInstance(p[19] * df * (1.0 + p[59] * dfa) + + p[20] * df * df + + p[21] * dfa + + p[29] * dfa * dfa); final double f1 = 1.0 + (p[47] * dfa + p[19] * df + p[20] * df * df) * swc[1]; final double f2 = 1.0 + (p[49] * dfa + p[19] * df + p[20] * df * df) * swc[1]; @@ -3420,7 +3432,7 @@ private T globe7(final double[] p) { add(plg[0][1].multiply(p[26])); // Symmetrical annual - t[2] = zero.add(p[18] * cd32); + t[2] = zero.newInstance(p[18] * cd32); // Symmetrical semiannual t[3] = plg[0][2].multiply(p[16]).add(p[15]).multiply(cd18); @@ -3539,7 +3551,7 @@ private T globe7(final double[] p) { } // Sum all effects (params not used: 82, 89, 99, 139-149) - T tinf = zero.add(p[30]); + T tinf = zero.newInstance(p[30]); for (int i = 0; i < 14; i++) { tinf = tinf.add(t[i].multiply(FastMath.abs(sw[i + 1]))); } @@ -3562,7 +3574,7 @@ private T glob7s(final double[] p) { final double cd39 = FastMath.cos(2.0 * DAY_TO_RAD * (doy - p[38])); // F10.7 effect - t[0] = zero.add(p[21] * (f107a - FLUX_REF)); + t[0] = zero.newInstance(p[21] * (f107a - FLUX_REF)); // Time independent t[1] = plg[0][2].multiply(p[1]). @@ -3658,10 +3670,10 @@ private T sg0(final T ex, final double p24, final double p25) { final double g04 = g0(ap[4], p24, p25); final double g05 = g0(ap[5], p24, p25); final double g06 = g0(ap[6], p24, p25); - final T ex2 = ex.multiply(ex); + final T ex2 = ex.square(); final T ex3 = ex.multiply(ex2); - final T ex4 = ex2.multiply(ex2); - final T ex8 = ex4.multiply(ex4); + final T ex4 = ex2.square(); + final T ex8 = ex4.square(); final T ex12 = ex4.multiply(ex8); final T g234 = ex.multiply(g02).add(ex2.multiply(g03)).add(ex3.multiply(g04)); final T g56 = ex4.multiply(g05).add(ex12.multiply(g06)); @@ -3715,7 +3727,7 @@ private T ccor2(final T alt, final double r, final double h1, final double zh, f if (e1.getReal() > 70. || e2.getReal() > 70.) { return field.getOne(); } else if (e1.getReal() < -70. && e2.getReal() < -70.) { - return zero.add(FastMath.exp(r)); + return zero.newInstance(FastMath.exp(r)); } else { final T ex1 = e1.exp(); final T ex2 = e2.exp(); @@ -3732,7 +3744,7 @@ private T ccor2(final T alt, final double r, final double h1, final double zh, f private T scalh(final double alt, final double xm, final double temp) { // Gravity at altitude final T denom = rlat.reciprocal().multiply(alt).add(1); - final T galt = glat.divide(denom.multiply(denom)); + final T galt = glat.divide(denom.square()); return galt.reciprocal().multiply(R_GAS * temp / xm); } @@ -3790,11 +3802,11 @@ private T splini(final T[] xa, final T[] ya, final T[] y2a, final T x) { final T h = xa[khi].subtract(xa[klo]); final T a = xa[khi].subtract(xx).divide(h); final T b = xx.subtract(xa[klo]).divide(h); - final T a2 = a.multiply(a); - final T b2 = b.multiply(b); + final T a2 = a.square(); + final T b2 = b.square(); final T z = - a2.divide(2).subtract(a2.multiply(a2).add(1).divide(4)).multiply(y2a[klo]). + a2.divide(2).subtract(a2.square().add(1).divide(4)).multiply(y2a[klo]). add(b2.multiply(b2).divide(4).subtract(b2.divide(2)).multiply(y2a[khi])); yi = yi.add( a2.negate().add(1).multiply(ya[klo]).divide(2). add(b2.multiply(ya[khi]).divide(2)). @@ -3830,7 +3842,7 @@ private T splint(final T[] xa, final T[] ya, final T[] y2a, final T x) { final T a = xa[khi].subtract(x).divide(h); final T b = x.subtract(xa[klo]).divide(h); return a.multiply(ya[klo]).add(b.multiply(ya[khi])). - add(( a.multiply(a).multiply(a).subtract(a).multiply(y2a[klo]). + add(( a.square().multiply(a).subtract(a).multiply(y2a[klo]). add(b.multiply(b).multiply(b).subtract(b).multiply(y2a[khi])) ).multiply(h).multiply(h).divide(6)); } @@ -3849,7 +3861,7 @@ private T[] spline(final T[] x, final T[] y, final T yp1, final T ypn) { final T[] u = MathArrays.buildArray(field, n); if (yp1.getReal() < 1e+30) { - y2[0] = zero.add(-0.5); + y2[0] = zero.newInstance(-0.5); final T dx = x[1].subtract(x[0]); final T dy = y[1].subtract(y[0]); u[0] = dx.reciprocal().multiply(3.0).multiply(dy.divide(dx).subtract(yp1)); @@ -3896,25 +3908,25 @@ private T densm(final T alt, final T d0, final double xm) { // stratosphere/mesosphere temperature int mn = ZN2.length; - T z = (alt.getReal() > ZN2[mn - 1]) ? alt : zero.add(ZN2[mn - 1]); + T z = (alt.getReal() > ZN2[mn - 1]) ? alt : zero.newInstance(ZN2[mn - 1]); double z1 = ZN2[0]; double z2 = ZN2[mn - 1]; T t1 = meso_tn2[0]; T t2 = meso_tn2[mn - 1]; T zg = zeta(z, z1); - T zgdif = zeta(zero.add(z2), z1); + T zgdif = zeta(zero.newInstance(z2), z1); /* set up spline nodes */ T[] xs = MathArrays.buildArray(field, mn); T[] ys = MathArrays.buildArray(field, mn); for (int k = 0; k < mn; k++) { - xs[k] = zeta(zero.add(ZN2[k]), z1).divide(zgdif); + xs[k] = zeta(zero.newInstance(ZN2[k]), z1).divide(zgdif); ys[k] = meso_tn2[k].reciprocal(); } final T qSM = rlat.add(z2).divide(rlat.add(z1)); - T yd1 = meso_tgn2[0].negate().divide(t1.multiply(t1)).multiply(zgdif); - T yd2 = meso_tgn2[1].negate().divide(t2.multiply(t2)).multiply(zgdif).multiply(qSM).multiply(qSM); + T yd1 = meso_tgn2[0].negate().divide(t1.square()).multiply(zgdif); + T yd2 = meso_tgn2[1].negate().divide(t2.square()).multiply(zgdif).multiply(qSM.square()); /* calculate spline coefficients */ T[] y2out = spline(xs, ys, yd1, yd2); @@ -3926,12 +3938,12 @@ private T densm(final T alt, final T d0, final double xm) { if (xm != 0.0) { /* calculate stratosphere / mesospehere density */ - final T glb = galt(zero.add(z1)); + final T glb = galt(zero.newInstance(z1)); final T gamm = glb.multiply(zgdif).multiply(xm / R_GAS); /* Integrate temperature profile */ final T yi = splini(xs, ys, y2out, x); - final T expl = min(50., gamm.multiply(yi)); + final T expl = min(MIN_TEMP, gamm.multiply(yi)); /* Density at altitude */ densm = densm.multiply(t1.divide(tz).multiply(expl.negate().exp())); @@ -3949,13 +3961,13 @@ private T densm(final T alt, final T d0, final double xm) { t1 = meso_tn3[0]; t2 = meso_tn3[mn - 1]; zg = zeta(z, z1); - zgdif = zeta(zero.add(z2), z1); + zgdif = zeta(zero.newInstance(z2), z1); /* set up spline nodes */ xs = MathArrays.buildArray(field, mn); ys = MathArrays.buildArray(field, mn); for (int k = 0; k < mn; k++) { - xs[k] = zeta(zero.add(ZN3[k]), z1).divide(zgdif); + xs[k] = zeta(zero.newInstance(ZN3[k]), z1).divide(zgdif); ys[k] = meso_tn3[k].reciprocal(); } final T qTS = rlat.add(z2) .divide(rlat.add(z1)); @@ -3972,12 +3984,12 @@ private T densm(final T alt, final T d0, final double xm) { if (xm != 0.0) { /* calculate tropospheric / stratosphere density */ - final T glb = galt(zero.add(z1)); + final T glb = galt(zero.newInstance(z1)); final T gamm = glb.multiply(zgdif).multiply(xm / R_GAS); /* Integrate temperature profile */ final T yi = splini(xs, ys, y2out, x); - final T expl = min(50., gamm.multiply(yi)); + final T expl = min(MIN_TEMP, gamm.multiply(yi)); /* Density at altitude */ densm = densm.multiply(t1.divide(tz).multiply(expl.negate().exp())); @@ -4001,7 +4013,7 @@ private T densu(final T alt, final T dlb, final T tinf, final T tlb, final double xm, final double alpha, final double zlb, final T s2) { /* joining altitudes of Bates and spline */ - T z = (alt.getReal() > ZN1[0]) ? alt : zero.add(ZN1[0]); + T z = (alt.getReal() > ZN1[0]) ? alt : zero.newInstance(ZN1[0]); /* geopotential altitude difference from ZLB */ final T zg2 = zeta(z, zlb); @@ -4021,10 +4033,10 @@ private T densu(final T alt, final T dlb, final T tinf, /* calculate temperature below ZA * temperature gradient at ZA from Bates profile */ final T p = rlat.add(zlb).divide(rlat.add(ZN1[0])); - final T dta = tinf.subtract(ta).multiply(s2).multiply(p.multiply(p)); + final T dta = tinf.subtract(ta).multiply(s2).multiply(p.square()); meso_tgn1[0] = dta; meso_tn1[0] = ta; - final T tzn1mn1 = zero.add(ZN1[mn - 1]); + final T tzn1mn1 = zero.newInstance(ZN1[mn - 1]); z = (alt.getReal() > ZN1[mn - 1]) ? alt : tzn1mn1; final T t1 = meso_tn1[0]; @@ -4034,13 +4046,13 @@ private T densu(final T alt, final T dlb, final T tinf, zgdif = zeta(tzn1mn1, ZN1[0]); /* set up spline nodes */ for (int k = 0; k < mn; k++) { - xs[k] = zeta(zero.add(ZN1[k]), ZN1[0]).divide(zgdif); + xs[k] = zeta(zero.newInstance(ZN1[k]), ZN1[0]).divide(zgdif); ys[k] = meso_tn1[k].reciprocal(); } /* end node derivatives */ final T q = rlat.add(ZN1[mn - 1]).divide(rlat.add(ZN1[0])); - final T yd1 = meso_tgn1[0].negate().divide(t1.multiply(t1)).multiply(zgdif); - final T yd2 = meso_tgn1[1].negate().divide(t2.multiply(t2)).multiply(zgdif).multiply(q.multiply(q)); + final T yd1 = meso_tgn1[0].negate().divide(t1.square()).multiply(zgdif); + final T yd2 = meso_tgn1[1].negate().divide(t2.square()).multiply(zgdif).multiply(q.square()); /* calculate spline coefficients */ y2out = spline(xs, ys, yd1, yd2); x = zg.divide(zgdif); @@ -4054,21 +4066,31 @@ private T densu(final T alt, final T dlb, final T tinf, } /* calculate density above za */ - T glb = galt(zero.add(zlb)); + T glb = galt(zero.newInstance(zlb)); T gamma = glb.divide(s2.multiply(tinf)).multiply(xm / R_GAS); T expl = tt.getReal() <= 0 ? - zero.add(50) : - min(50.0, s2.negate().multiply(gamma).multiply(zg2).exp()); - T densu = dlb.multiply(tlb.divide(tt).pow(gamma.add(alpha + 1))).multiply(expl); + zero.newInstance(MIN_TEMP) : + min(MIN_TEMP, s2.negate().multiply(gamma).multiply(zg2).exp()); + T densu = dlb.multiply(expl).multiply(tlb.divide(tt).pow(gamma.add(alpha + 1))); + + // Correction for issue 1365 - protection against "densu" being infinite + if (!Double.isFinite(densu.getReal())) { + if (expl.getReal() < MIN_TEMP) { + densu = dlb.multiply(FastMath.exp((FastMath.log(tlb.divide(tt)).multiply(gamma.add(alpha + 1))). + subtract(s2.multiply(gamma).multiply(zg2)))); + } else { + throw new OrekitException(OrekitMessages.INFINITE_NRLMSISE00_DENSITY); + } + } /* calculate density below za */ if (alt.getReal() < ZN1[0]) { - glb = galt(zero.add(ZN1[0])); + glb = galt(zero.newInstance(ZN1[0])); gamma = glb.multiply(zgdif).multiply(xm / R_GAS); /* integrate spline temperatures */ expl = tz.getReal() <= 0 ? - zero.add(50.0) : - min(50.0, gamma.multiply(splini(xs, ys, y2out, x))); + zero.newInstance(MIN_TEMP) : + min(MIN_TEMP, gamma.multiply(splini(xs, ys, y2out, x))); /* correct density at altitude */ densu = densu.multiply(meso_tn1[0].divide(tz).pow(alpha + 1).multiply(expl.negate().exp())); } @@ -4083,7 +4105,7 @@ private T densu(final T alt, final T dlb, final T tinf, * @return min value */ private T min(final double d, final T f) { - return (f.getReal() > d) ? zero.add(d) : f; + return (f.getReal() > d) ? zero.newInstance(d) : f; } /** Calculate gravity at altitude. @@ -4092,7 +4114,7 @@ private T min(final double d, final T f) { */ private T galt(final T alt) { final T r = alt.divide(rlat).add(1); - return glat.divide(r.multiply(r)); + return glat.divide(r.square()); } /** Calculate zeta function. diff --git a/src/main/java/org/orekit/models/earth/displacement/OceanLoadingCoefficientsBLQFactory.java b/src/main/java/org/orekit/models/earth/displacement/OceanLoadingCoefficientsBLQFactory.java index 6d56af47e8..6ffa5f42d5 100644 --- a/src/main/java/org/orekit/models/earth/displacement/OceanLoadingCoefficientsBLQFactory.java +++ b/src/main/java/org/orekit/models/earth/displacement/OceanLoadingCoefficientsBLQFactory.java @@ -16,25 +16,18 @@ */ package org.orekit.models.earth.displacement; -import java.io.BufferedReader; -import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Optional; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import java.util.stream.Collectors; -import org.hipparchus.util.FastMath; import org.orekit.annotation.DefaultDataContext; -import org.orekit.bodies.GeodeticPoint; import org.orekit.data.AbstractSelfFeedingLoader; import org.orekit.data.DataContext; import org.orekit.data.DataLoader; import org.orekit.data.DataProvidersManager; +import org.orekit.data.DataSource; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; @@ -64,64 +57,6 @@ public class OceanLoadingCoefficientsBLQFactory extends AbstractSelfFeedingLoade /** Default supported files name pattern for Onsala Space Observatory files in BLQ format. */ public static final String DEFAULT_BLQ_SUPPORTED_NAMES = "^.+\\.blq$"; - /** Pattern for fields with real type. */ - private static final String REAL_TYPE_PATTERN = "[-+]?(?:(?:\\p{Digit}+(?:\\.\\p{Digit}*)?)|(?:\\.\\p{Digit}+))(?:[eE][-+]?\\p{Digit}+)?"; - - /** Pattern for extracted real fields. */ - private static final String REAL_FIELD_PATTERN = "\\p{Space}*(" + REAL_TYPE_PATTERN + ")"; - - /** Pattern for end of line. */ - private static final String END_OF_LINE_PATTERN = "\\p{Space}*$"; - - /** Pattern for site name and coordinates lines. */ - private static final String SITE_LINE_PATTERN = "^\\$\\$ *([^,]*),\\p{Space}*(?:RADI TANG)?\\p{Space}*lon/lat:" + - REAL_FIELD_PATTERN + - REAL_FIELD_PATTERN + - REAL_FIELD_PATTERN + - END_OF_LINE_PATTERN; - - /** Pattern for coefficients lines. */ - private static final String DATA_LINE_PATTERN = "^" + - REAL_FIELD_PATTERN + // M₂ tide - REAL_FIELD_PATTERN + // S₂ tide - REAL_FIELD_PATTERN + // N₂ tide - REAL_FIELD_PATTERN + // K₂ tide - REAL_FIELD_PATTERN + // K₁ tide - REAL_FIELD_PATTERN + // O₁ tide - REAL_FIELD_PATTERN + // P₁ tide - REAL_FIELD_PATTERN + // Q₁ tide - REAL_FIELD_PATTERN + // Mf tide - REAL_FIELD_PATTERN + // Mm tide - REAL_FIELD_PATTERN + // Ssa tide - END_OF_LINE_PATTERN; - - /** Pattern for site name and coordinates lines. */ - private static final Pattern SITE_PATTERN = Pattern.compile(SITE_LINE_PATTERN); - - /** Pattern for coefficients lines. */ - private static final Pattern DATA_PATTERN = Pattern.compile(DATA_LINE_PATTERN); - - /** Main tides. */ - private static final Tide[][] TIDES = { - { - Tide.SSA, Tide.MM, Tide.MF - }, { - Tide.Q1, Tide.O1, Tide.P1, Tide.K1 - }, { - Tide.N2, Tide.M2, Tide.S2, Tide.K2 - } - }; - - /** Species index for each column. */ - private static final int[] I = { - 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0 - }; - - /** Tides index for each column. */ - private static final int[] J = { - 1, 2, 0, 3, 3, 1, 2, 0, 2, 1, 0 - }; - /** Parsed coefficients. */ private final List coefficients; @@ -155,9 +90,8 @@ public OceanLoadingCoefficientsBLQFactory(final String supportedNames) { * @see #DEFAULT_BLQ_SUPPORTED_NAMES * @since 10.1 */ - public OceanLoadingCoefficientsBLQFactory( - final String supportedNames, - final DataProvidersManager dataProvidersManager) { + public OceanLoadingCoefficientsBLQFactory(final String supportedNames, + final DataProvidersManager dataProvidersManager) { super(supportedNames, dataProvidersManager); this.coefficients = new ArrayList<>(); @@ -168,7 +102,21 @@ public OceanLoadingCoefficientsBLQFactory( */ private void loadsIfNeeded() { if (coefficients.isEmpty()) { - feed(new BLQParser()); + feed(new DataLoader() { + + /** {@inheritDoc} */ + @Override + public boolean stillAcceptsData() { + return true; + } + + /** {@inheritDoc} */ + @Override + public void loadData(final InputStream input, final String name) { + final OceanLoadingCoefficientsBlqParser parser = new OceanLoadingCoefficientsBlqParser(); + coefficients.addAll(parser.parse(new DataSource(name, () -> input))); + } + }); } } @@ -180,14 +128,12 @@ public List getSites() { loadsIfNeeded(); // extract sites names from the map - final List sites = coefficients.stream() + return coefficients.stream() .map(OceanLoadingCoefficients::getSiteName) // sort to ensure we have a reproducible order .sorted(String::compareToIgnoreCase) .collect(Collectors.toList()); - return sites; - } /** Get the coefficients for a given site. @@ -211,162 +157,4 @@ public OceanLoadingCoefficients getCoefficients(final String site) { } - /** Local parser for the Onsala Space Observatory BLQ files. - *

    - * when completing the web site form, the email received as the following form: - *

    - *
    {@literal
    -     * $$ Ocean loading displacement
    -     * $$
    -     * $$ Calculated on holt using olfg/olmpp of H.-G. Scherneck
    -     * $$
    -     * $$ COLUMN ORDER:  M2  S2  N2  K2  K1  O1  P1  Q1  MF  MM SSA
    -     * $$
    -     * $$ ROW ORDER:
    -     * $$ AMPLITUDES (m)
    -     * $$   RADIAL
    -     * $$   TANGENTL    EW
    -     * $$   TANGENTL    NS
    -     * $$ PHASES (degrees)
    -     * $$   RADIAL
    -     * $$   TANGENTL    EW
    -     * $$   TANGENTL    NS
    -     * $$
    -     * $$ Displacement is defined positive in upwards, South and West direction.
    -     * $$ The phase lag is relative to Greenwich and lags positive. The
    -     * $$ Gutenberg-Bullen Greens function is used. In the ocean tide model the
    -     * $$ deficit of tidal water mass has been corrected by subtracting a uniform
    -     * $$ layer of water with a certain phase lag globally.
    -     * $$
    -     * $$ Complete  : No interpolation of ocean model was necessary
    -     * $$ _PP       : Ocean model has been interpolated near the station
    -     * $$                         (PP = Post-Processing)
    -     * $$
    -     * $$ Ocean tide model: CSR4.0, long-period tides from FES99
    -     * $$
    -     * $$ END HEADER
    -     * $$
    -     *   Goldstone
    -     * $$ Complete CSR4.0_f
    -     * $$ Computed by OLFG, H.-G. Scherneck, Onsala Space Observatory 2017-Sep-28
    -     * $$ Goldstone,                 RADI TANG  lon/lat:  243.1105   35.4259    0.000
    -     *   .00130 .00155 .00064 .00052 .01031 .00661 .00339 .00119 .00005 .00002 .00003
    -     *   .00136 .00020 .00024 .00004 .00322 .00202 .00106 .00036 .00007 .00003 .00001
    -     *   .00372 .00165 .00082 .00045 .00175 .00113 .00057 .00022 .00004 .00002 .00003
    -     *     -2.7 -106.3  -62.6 -106.8   41.6   27.3   40.4   24.0 -119.1 -123.2 -169.7
    -     *   -145.3  -88.4  178.5  -66.3 -130.5 -145.3 -131.7 -148.7  124.3  139.6   23.3
    -     *     90.7  111.1   74.1  111.3  176.9  165.3  175.8  164.0   48.9   25.3    4.5
    -     * $$
    -     *   ONSALA
    -     * $$ CSR4.0_f_PP ID: 2017-09-28 15:01:14
    -     * $$ Computed by OLMPP by H G Scherneck, Onsala Space Observatory, 2017
    -     * $$ Onsala,                    RADI TANG  lon/lat:   11.9264   57.3958    0.000
    -     *   .00344 .00121 .00078 .00031 .00189 .00116 .00064 .00004 .00090 .00048 .00041
    -     *   .00143 .00035 .00035 .00008 .00053 .00051 .00018 .00009 .00013 .00006 .00007
    -     *   .00086 .00023 .00023 .00006 .00029 .00025 .00010 .00008 .00003 .00001 .00000
    -     *    -64.6  -50.3  -95.0  -53.1  -58.8 -152.4  -65.5 -133.8    9.8    5.8    2.1
    -     *     85.4  115.2   56.7  114.7   99.5   15.9   94.2  -10.0 -166.3 -169.8 -177.7
    -     *    110.7  147.1   93.9  148.6   49.4  -56.5   34.8 -169.9  -35.3   -3.7   10.1
    -     * $$ END TABLE
    -     * Errors:
    -     * Warnings:
    -     * }
    - *

    - * We only parse blocks 7 lines blocks starting with the lines with the station names - * and their coordinates and the 6 data lines that follows. Several such blocks may - * appear in the file. - *

    - */ - private class BLQParser implements DataLoader { - - /** Simple constructor. - */ - BLQParser() { - // empty constructor - } - - /** {@inheritDoc} */ - @Override - public boolean stillAcceptsData() { - // as data for different stations may be in different files - // we always accept new data, even if we have already parsed - // some files - return true; - } - - /** {@inheritDoc} */ - @Override - public void loadData(final InputStream input, final String name) - throws IOException, OrekitException { - - // temporary holders for parsed data - String siteName = null; - GeodeticPoint siteLocation = null; - final double[][][] data = new double[6][3][]; - for (int i = 0; i < data.length; ++i) { - data[i][0] = new double[3]; - data[i][1] = new double[4]; - data[i][2] = new double[4]; - } - - try (BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8))) { - - int lineNumber = 0; - int dataLine = -1; - for (String line = reader.readLine(); line != null; line = reader.readLine()) { - ++lineNumber; - - if (dataLine < 0) { - // we are looking for a site line - final Matcher matcher = SITE_PATTERN.matcher(line); - if (matcher.matches()) { - // the current line is a site description line - siteName = matcher.group(1); - siteLocation = new GeodeticPoint(FastMath.toRadians(Double.parseDouble(matcher.group(3))), - FastMath.toRadians(Double.parseDouble(matcher.group(2))), - Double.parseDouble(matcher.group(4))); - // next line must be line 0 of the data - dataLine = 0; - } - } else { - // we are looking for a data line - final Matcher matcher = DATA_PATTERN.matcher(line); - if (!matcher.matches()) { - throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, - lineNumber, name, line); - } - for (int k = 0; k < I.length; ++k) { - if (dataLine < 3) { - // amplitude data - data[dataLine][I[k]][J[k]] = Double.parseDouble(matcher.group(k + 1)); - } else { - // phase data (reversed to be negative for lags) - data[dataLine][I[k]][J[k]] = -FastMath.toRadians(Double.parseDouble(matcher.group(k + 1))); - } - } - if (dataLine < data.length - 1) { - // we need more data - ++dataLine; - } else { - // it was the last data line - coefficients.add(new OceanLoadingCoefficients(siteName, siteLocation, TIDES, - data[0], data[3], - data[1], data[4], - data[2], data[5])); - dataLine = -1; - } - } - } - - if (dataLine >= 0) { - // we were looking for a line that did not appear - throw new OrekitException(OrekitMessages.UNEXPECTED_END_OF_FILE_AFTER_LINE, - name, lineNumber); - } - - } - } - - } - } diff --git a/src/main/java/org/orekit/models/earth/displacement/OceanLoadingCoefficientsBlqParser.java b/src/main/java/org/orekit/models/earth/displacement/OceanLoadingCoefficientsBlqParser.java new file mode 100644 index 0000000000..4038a2c643 --- /dev/null +++ b/src/main/java/org/orekit/models/earth/displacement/OceanLoadingCoefficientsBlqParser.java @@ -0,0 +1,268 @@ +/* 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.models.earth.displacement; + +import org.hipparchus.exception.LocalizedCoreFormats; +import org.hipparchus.util.FastMath; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.data.DataSource; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Parser for ocean loading coefficients, using Onsala Space Observatory files in BLQ format. + *

    + * Files in BLQ format can be generated using the form at the + * Bos-Scherneck web site, + * selecting BLQ as the output format. + *

    + *

    + * The sites names are extracted from the file content, not the file name, because the + * file can contain more than one station. As we expect existing files may have been + * stripped from headers and footers, we do not attempt to parse them. We only parse + * the series of 7 lines blocks starting with the lines with the station names and their + * coordinates and the 6 data lines that follows. Several such blocks may appear in the + * file. Copy-pasting the entire mail received from OSO after completing the web site + * form works, as intermediate lines between the 7 lines blocks are simply ignored. + *

    + * @see OceanLoadingCoefficients + * @see OceanLoading + * @since 9.1 + * @author Luc Maisonobe + */ +public class OceanLoadingCoefficientsBlqParser { + + /** Pattern for fields with real type. */ + private static final String REAL_TYPE_PATTERN = "[-+]?(?:\\p{Digit}+(?:\\.\\p{Digit}*)?|\\.\\p{Digit}+)(?:[eE][-+]?\\p{Digit}+)?"; + + /** Pattern for extracted real fields. */ + private static final String REAL_FIELD_PATTERN = "\\p{Space}*(" + REAL_TYPE_PATTERN + ")"; + + /** Pattern for end of line. */ + private static final String END_OF_LINE_PATTERN = "\\p{Space}*$"; + + /** Pattern for site name and coordinates lines. */ + private static final String SITE_LINE_PATTERN = "^\\$\\$ *([^,]*),\\p{Space}*(?:RADI TANG)?\\p{Space}*lon/lat:" + + REAL_FIELD_PATTERN + + REAL_FIELD_PATTERN + + REAL_FIELD_PATTERN + + END_OF_LINE_PATTERN; + + /** Pattern for coefficients lines. */ + private static final String DATA_LINE_PATTERN = "^" + + REAL_FIELD_PATTERN + // M₂ tide + REAL_FIELD_PATTERN + // S₂ tide + REAL_FIELD_PATTERN + // N₂ tide + REAL_FIELD_PATTERN + // K₂ tide + REAL_FIELD_PATTERN + // K₁ tide + REAL_FIELD_PATTERN + // O₁ tide + REAL_FIELD_PATTERN + // P₁ tide + REAL_FIELD_PATTERN + // Q₁ tide + REAL_FIELD_PATTERN + // Mf tide + REAL_FIELD_PATTERN + // Mm tide + REAL_FIELD_PATTERN + // Ssa tide + END_OF_LINE_PATTERN; + + /** Pattern for site name and coordinates lines. */ + private static final Pattern SITE_PATTERN = Pattern.compile(SITE_LINE_PATTERN); + + /** Pattern for coefficients lines. */ + private static final Pattern DATA_PATTERN = Pattern.compile(DATA_LINE_PATTERN); + + /** Main tides. */ + private static final Tide[][] TIDES = { + { + Tide.SSA, Tide.MM, Tide.MF + }, { + Tide.Q1, Tide.O1, Tide.P1, Tide.K1 + }, { + Tide.N2, Tide.M2, Tide.S2, Tide.K2 + } + }; + + /** Species index for each column. */ + private static final int[] I = { + 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0 + }; + + /** Tides index for each column. */ + private static final int[] J = { + 1, 2, 0, 3, 3, 1, 2, 0, 2, 1, 0 + }; + + /** Parse a BLQ file. + *

    + * Files in BLQ format can be generated using the form at the + * Bos-Scherneck web site, + * selecting BLQ as the output format. + *

    + *

    + * when completing the web site form, the email received as the following form: + *

    + *
    {@literal
    +     * $$ Ocean loading displacement
    +     * $$
    +     * $$ Calculated on holt using olfg/olmpp of H.-G. Scherneck
    +     * $$
    +     * $$ COLUMN ORDER:  M2  S2  N2  K2  K1  O1  P1  Q1  MF  MM SSA
    +     * $$
    +     * $$ ROW ORDER:
    +     * $$ AMPLITUDES (m)
    +     * $$   RADIAL
    +     * $$   TANGENTL    EW
    +     * $$   TANGENTL    NS
    +     * $$ PHASES (degrees)
    +     * $$   RADIAL
    +     * $$   TANGENTL    EW
    +     * $$   TANGENTL    NS
    +     * $$
    +     * $$ Displacement is defined positive in upwards, South and West direction.
    +     * $$ The phase lag is relative to Greenwich and lags positive. The
    +     * $$ Gutenberg-Bullen Greens function is used. In the ocean tide model the
    +     * $$ deficit of tidal water mass has been corrected by subtracting a uniform
    +     * $$ layer of water with a certain phase lag globally.
    +     * $$
    +     * $$ Complete  : No interpolation of ocean model was necessary
    +     * $$ _PP       : Ocean model has been interpolated near the station
    +     * $$                         (PP = Post-Processing)
    +     * $$
    +     * $$ Ocean tide model: CSR4.0, long-period tides from FES99
    +     * $$
    +     * $$ END HEADER
    +     * $$
    +     *   Goldstone
    +     * $$ Complete CSR4.0_f
    +     * $$ Computed by OLFG, H.-G. Scherneck, Onsala Space Observatory 2017-Sep-28
    +     * $$ Goldstone,                 RADI TANG  lon/lat:  243.1105   35.4259    0.000
    +     *   .00130 .00155 .00064 .00052 .01031 .00661 .00339 .00119 .00005 .00002 .00003
    +     *   .00136 .00020 .00024 .00004 .00322 .00202 .00106 .00036 .00007 .00003 .00001
    +     *   .00372 .00165 .00082 .00045 .00175 .00113 .00057 .00022 .00004 .00002 .00003
    +     *     -2.7 -106.3  -62.6 -106.8   41.6   27.3   40.4   24.0 -119.1 -123.2 -169.7
    +     *   -145.3  -88.4  178.5  -66.3 -130.5 -145.3 -131.7 -148.7  124.3  139.6   23.3
    +     *     90.7  111.1   74.1  111.3  176.9  165.3  175.8  164.0   48.9   25.3    4.5
    +     * $$
    +     *   ONSALA
    +     * $$ CSR4.0_f_PP ID: 2017-09-28 15:01:14
    +     * $$ Computed by OLMPP by H G Scherneck, Onsala Space Observatory, 2017
    +     * $$ Onsala,                    RADI TANG  lon/lat:   11.9264   57.3958    0.000
    +     *   .00344 .00121 .00078 .00031 .00189 .00116 .00064 .00004 .00090 .00048 .00041
    +     *   .00143 .00035 .00035 .00008 .00053 .00051 .00018 .00009 .00013 .00006 .00007
    +     *   .00086 .00023 .00023 .00006 .00029 .00025 .00010 .00008 .00003 .00001 .00000
    +     *    -64.6  -50.3  -95.0  -53.1  -58.8 -152.4  -65.5 -133.8    9.8    5.8    2.1
    +     *     85.4  115.2   56.7  114.7   99.5   15.9   94.2  -10.0 -166.3 -169.8 -177.7
    +     *    110.7  147.1   93.9  148.6   49.4  -56.5   34.8 -169.9  -35.3   -3.7   10.1
    +     * $$ END TABLE
    +     * Errors:
    +     * Warnings:
    +     * }
    + *

    + * We only parse blocks 7 lines blocks starting with the lines with the station names + * and their coordinates and the 6 data lines that follows. Several such blocks may + * appear in the file. + *

    + * @param source source for BLQ data + * @return parsed coefficients + */ + public List parse(final DataSource source) { + + final List coefficients = new ArrayList<>(); + + // temporary holders for parsed data + String siteName = null; + GeodeticPoint siteLocation = null; + final double[][][] data = new double[6][3][]; + for (int i = 0; i < data.length; ++i) { + data[i][0] = new double[3]; + data[i][1] = new double[4]; + data[i][2] = new double[4]; + } + + try (BufferedReader reader = new BufferedReader( + source.getOpener().openReaderOnce())) { + + int lineNumber = 0; + int dataLine = -1; + for (String line = reader.readLine(); line != null; line = reader.readLine()) { + ++lineNumber; + + if (dataLine < 0) { + // we are looking for a site line + final Matcher matcher = SITE_PATTERN.matcher(line); + if (matcher.matches()) { + // the current line is a site description line + siteName = matcher.group(1); + siteLocation = + new GeodeticPoint(FastMath.toRadians(Double.parseDouble(matcher.group(3))), + FastMath.toRadians(Double.parseDouble(matcher.group(2))), + Double.parseDouble(matcher.group(4))); + // next line must be line 0 of the data + dataLine = 0; + } + } else { + // we are looking for a data line + final Matcher matcher = DATA_PATTERN.matcher(line); + if (!matcher.matches()) { + throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, + lineNumber, source.getName(), line); + } + for (int k = 0; k < I.length; ++k) { + if (dataLine < 3) { + // amplitude data + data[dataLine][I[k]][J[k]] = Double.parseDouble(matcher.group(k + 1)); + } else { + // phase data (reversed to be negative for lags) + data[dataLine][I[k]][J[k]] = -FastMath.toRadians(Double.parseDouble(matcher.group(k + 1))); + } + } + if (dataLine < data.length - 1) { + // we need more data + ++dataLine; + } else { + // it was the last data line + coefficients.add(new OceanLoadingCoefficients(siteName, siteLocation, + TIDES, data[0], + data[3], data[1], + data[4], data[2], + data[5])); + dataLine = -1; + } + } + } + + if (dataLine >= 0) { + // we were looking for a line that did not appear + throw new OrekitException(OrekitMessages.UNEXPECTED_END_OF_FILE_AFTER_LINE, + source.getName(), lineNumber); + } + + } catch (IOException ioe) { + throw new OrekitException(ioe, LocalizedCoreFormats.SIMPLE_MESSAGE, + ioe.getLocalizedMessage()); + } + + return coefficients; + + } + +} diff --git a/src/main/java/org/orekit/models/earth/displacement/PostSeismicDeformation.java b/src/main/java/org/orekit/models/earth/displacement/PostSeismicDeformation.java new file mode 100644 index 0000000000..9131a9ca82 --- /dev/null +++ b/src/main/java/org/orekit/models/earth/displacement/PostSeismicDeformation.java @@ -0,0 +1,70 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.displacement; + +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.data.BodiesElements; +import org.orekit.frames.Frame; +import org.orekit.utils.TimeSpanMap; + +import java.util.List; + +/** Modeling of displacement of one reference point due to post-seismic effects. + * @see + * ITRF2020P: Equations of post-seismic deformation models + * @since 12.1 + * @author Luc Maisonobe + */ +public class PostSeismicDeformation implements StationDisplacement { + + /** Base point. */ + private final GeodeticPoint base; + + /** PSD corrections. */ + private final TimeSpanMap> corrections; + + /** Simple constructor. + * @param base base point + * @param corrections Post-Seismic Deformation corrections + */ + public PostSeismicDeformation(final GeodeticPoint base, + final TimeSpanMap> corrections) { + this.base = base; + this.corrections = corrections; + } + + /** {@inheritDoc} */ + @Override + public Vector3D displacement(final BodiesElements elements, final Frame earthFrame, final Vector3D referencePoint) { + + // initialize displacement with zero correction + Vector3D cumulativeCorrection = Vector3D.ZERO; + + // apply all relevant Post-Seismic Deformation corrections + final List correctionsAtDate = corrections.get(elements.getDate()); + if (correctionsAtDate != null) { + for (final PsdCorrection correction : correctionsAtDate) { + cumulativeCorrection = cumulativeCorrection.add(correction.displacement(elements.getDate(), base)); + } + } + + return cumulativeCorrection; + + } + +} diff --git a/src/main/java/org/orekit/models/earth/displacement/PsdCorrection.java b/src/main/java/org/orekit/models/earth/displacement/PsdCorrection.java new file mode 100644 index 0000000000..39fe3ff8f3 --- /dev/null +++ b/src/main/java/org/orekit/models/earth/displacement/PsdCorrection.java @@ -0,0 +1,165 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.displacement; + +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.util.FastMath; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.time.AbsoluteDate; + +/** Model for post-seismic deformation corrections. + * @since 12.1 + * @author Luc Maisonobe + */ +public class PsdCorrection { + + /** Correction axis. */ + private final Axis axis; + + /** Time evolution. */ + private final TimeEvolution evolution; + + /** Earthquake date. */ + private final AbsoluteDate earthquakeDate; + + /** Amplitude. */ + private final double amplitude; + + /** Relaxation time. */ + private final double relaxationTime; + + /** Simple constructor. + * @param axis correction axis + * @param evolution time evolution + * @param earthquakeDate earthquake date + * @param amplitude amplitude + * @param relaxationTime relaxation time + */ + public PsdCorrection(final Axis axis, + final TimeEvolution evolution, + final AbsoluteDate earthquakeDate, + final double amplitude, + final double relaxationTime) { + this.axis = axis; + this.evolution = evolution; + this.earthquakeDate = earthquakeDate; + this.amplitude = amplitude; + this.relaxationTime = relaxationTime; + } + + /** Get correction axis. + * @return correction axis + */ + public Axis getAxis() { + return axis; + } + + /** Get time evolution. + * @return time evolution + */ + public TimeEvolution getEvolution() { + return evolution; + } + + /** Get earthquake date. + * @return earthquake date + */ + public AbsoluteDate getEarthquakeDate() { + return earthquakeDate; + } + + /** Get amplitude. + * @return amplitude + */ + public double getAmplitude() { + return amplitude; + } + + /** Get relaxation time. + * @return relaxation time + */ + public double getRelaxationTime() { + return relaxationTime; + } + + /** Compute displacement. + * @param date date + * @param base base point + * @return displacement vector in Earth frame + */ + public Vector3D displacement(final AbsoluteDate date, final GeodeticPoint base) { + final double scaledTime = date.durationFrom(earthquakeDate) / relaxationTime; + final double timeFactor = evolution.timeFactor(scaledTime); + return new Vector3D(amplitude * timeFactor, axis.vector(base)); + } + + /** Enumerate for correction axis. */ + public enum Axis { + /** East axis. */ + EAST { + public Vector3D vector(final GeodeticPoint base) { + return base.getEast(); + } + }, + + /** North axis. */ + NORTH { + public Vector3D vector(final GeodeticPoint base) { + return base.getNorth(); + } + }, + + /** Up axis. */ + UP { + public Vector3D vector(final GeodeticPoint base) { + return base.getZenith(); + } + }; + + /** Get axis unit vector. + * @param base base point + * @return direction in Earth frame + */ + public abstract Vector3D vector(GeodeticPoint base); + } + + /** Enumerate for correction time evolution. */ + public enum TimeEvolution { + + /** Exponential evolution. */ + EXP { + public double timeFactor(final double scaledTime) { + return 1 - FastMath.exp(-scaledTime); + } + }, + + /** Logarithmic evolution. */ + LOG { + public double timeFactor(final double scaledTime) { + return FastMath.log(1 + scaledTime); + } + }; + + /** Evaluate correction time factor. + * @param scaledTime scaled time since earthquake + * @return correction time factor + */ + public abstract double timeFactor(double scaledTime); + + } + +} 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 9da71e5f41..061270c780 100644 --- a/src/main/java/org/orekit/models/earth/ionosphere/FieldNeQuickParameters.java +++ b/src/main/java/org/orekit/models/earth/ionosphere/FieldNeQuickParameters.java @@ -124,7 +124,7 @@ class FieldNeQuickParameters > { } // E layer maximum density height in km (Eq. 78) - this.hmE = field.getZero().add(120.0); + this.hmE = field.getZero().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) @@ -162,8 +162,8 @@ class FieldNeQuickParameters > { this.b2Bot = nmF2.divide(a).multiply(0.385); this.b1Top = hmF2.subtract(hmF1).multiply(0.3); this.b1Bot = hmF1.subtract(hmE).multiply(0.5); - this.beTop = FastMath.max(b1Bot, zero.add(7.0)); - this.beBot = zero.add(5.0); + this.beTop = FastMath.max(b1Bot, zero.newInstance(7.0)); + this.beBot = zero.newInstance(5.0); // Layer amplitude coefficients this.amplitudes = computeLayerAmplitudes(field, nmE, nmF1, foF1); @@ -344,14 +344,14 @@ private T computeAz(final T modip, final double[] alpha) { final T zero = field.getZero(); // Particular condition (Eq. 17) if (alpha[0] == 0.0 && alpha[1] == 0.0 && alpha[2] == 0.0) { - return zero.add(63.7); + return zero.newInstance(63.7); } // Az = a0 + modip * a1 + modip^2 * a2 (Eq. 18) T az = modip.multiply(alpha[2]).add(alpha[1]).multiply(modip).add(alpha[0]); // If Az < 0 -> Az = 0 az = FastMath.max(zero, az); // If Az > 400 -> Az = 400 - az = FastMath.min(zero.add(400.0), az); + az = FastMath.min(zero.newInstance(400.0), az); return az; } @@ -392,7 +392,8 @@ private T computeEffectiveSolarAngle(final int month, final T angle = FastMath.atan2(FastMath.sqrt(cZenith.multiply(cZenith).negate().add(1.0)), cZenith); final T x = FastMath.toDegrees(angle); // Effective solar zenith angle (Eq. 28) - final T xeff = join(clipExp(x.multiply(0.2).negate().add(20.0)).multiply(0.24).negate().add(90.0), x, zero.add(12.0), x.subtract(X0)); + final T xeff = join(clipExp(x.multiply(0.2).negate().add(20.0)).multiply(0.24).negate().add(90.0), x, + zero.newInstance(12.0), x.subtract(X0)); return FastMath.toRadians(xeff); } @@ -441,7 +442,7 @@ private T computehmF2(final Field field, final T foE, final T foF2, final T m final T zero = field.getZero(); // Ratio final T fo = foF2.divide(foE); - final T ratio = join(fo, zero.add(1.75), zero.add(20.0), fo.subtract(1.75)); + final T ratio = join(fo, zero.newInstance(1.75), zero.newInstance(20.0), fo.subtract(1.75)); // deltaM parameter T deltaM = zero.subtract(0.012); @@ -450,7 +451,7 @@ private T computehmF2(final Field field, final T foE, final T foF2, final T m } // hmF2 Eq. 80 - final T mF2Sq = mF2.multiply(mF2); + 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; @@ -624,9 +625,9 @@ private T computeMF2(final Field field, final T modip, final T[] cm3, */ private T computefoF1(final Field field, final T foE, final T foF2) { final T zero = field.getZero(); - final T temp = join(foE.multiply(1.4), zero, zero.add(1000.0), foE.subtract(2.0)); - final T temp2 = join(zero, temp, zero.add(1000.0), foE.subtract(temp)); - final T value = join(temp2, temp2.multiply(0.85), zero.add(60.0), foF2.multiply(0.85).subtract(temp2)); + 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)); if (value.getReal() < 1.0E-6) { return zero; } else { @@ -674,7 +675,7 @@ private T[] computeLayerAmplitudes(final Field field, final T nmE, final T nm a3a = nmE.subtract(epst(a2a, hmF1, b1Bot, hmE)).subtract(epst(a1, hmF2, b2Bot, hmE)).multiply(4.0); } amplitude[1] = a2a; - amplitude[2] = join(a3a, zero.add(0.05), zero.add(60.0), a3a.subtract(0.005)); + amplitude[2] = join(a3a, zero.newInstance(0.05), zero.newInstance(60.0), a3a.subtract(0.005)); } return amplitude; @@ -705,8 +706,8 @@ private T computeH0(final Field field, final int month, final T azr) { } // Auxiliary parameter kb (Eq. 101 and 102) - T kb = join(ka, one.multiply(2.0), one, ka.subtract(2.0)); - kb = join(one.multiply(8.0), kb, one, kb.subtract(8.0)); + T kb = join(ka, one.newInstance(2.0), one, ka.subtract(2.0)); + kb = join(one.newInstance(8.0), kb, one, kb.subtract(8.0)); // Auxiliary parameter Ha (Eq. 103) final T hA = kb.multiply(b2Bot); @@ -732,9 +733,9 @@ private T computeH0(final Field field, final int month, final T azr) { private T clipExp(final T power) { final T zero = power.getField().getZero(); if (power.getReal() > 80.0) { - return zero.add(5.5406E34); + return zero.newInstance(5.5406E34); } else if (power.getReal() < -80) { - return zero.add(1.8049E-35); + return zero.newInstance(1.8049E-35); } else { return FastMath.exp(power); } diff --git a/src/main/java/org/orekit/models/earth/ionosphere/KlobucharIonoModel.java b/src/main/java/org/orekit/models/earth/ionosphere/KlobucharIonoModel.java index edf292839b..0ba3a302d9 100644 --- a/src/main/java/org/orekit/models/earth/ionosphere/KlobucharIonoModel.java +++ b/src/main/java/org/orekit/models/earth/ionosphere/KlobucharIonoModel.java @@ -250,7 +250,7 @@ public > T pathDelay(final FieldAbsoluteDate> T pathDelay(final FieldAbsoluteDate> T bottomElectronDensity(final Field< // Compute the exponential argument for each layer (Eq. 111 to 113) // If h < 100km we use h = 100km as recommended in the reference document - final T hTemp = FastMath.max(zero.add(100.0), h); + final T hTemp = FastMath.max(zero.newInstance(100.0), h); final T exp = clipExp(field, FastMath.abs(hTemp.subtract(parameters.getHmF2())).add(1.0).divide(10.0).reciprocal()); final T[] arguments = MathArrays.buildArray(field, 3); arguments[0] = hTemp.subtract(parameters.getHmF2()).divide(bf2); @@ -655,9 +655,9 @@ private double clipExp(final double power) { private > T clipExp(final Field field, final T power) { final T zero = field.getZero(); if (power.getReal() > 80.0) { - return zero.add(5.5406E34); + return zero.newInstance(5.5406E34); } else if (power.getReal() < -80) { - return zero.add(1.8049E-35); + return zero.newInstance(1.8049E-35); } else { return FastMath.exp(power); } diff --git a/src/main/java/org/orekit/models/earth/tessellation/EllipsoidTessellator.java b/src/main/java/org/orekit/models/earth/tessellation/EllipsoidTessellator.java index c4f6961402..87357eddc7 100644 --- a/src/main/java/org/orekit/models/earth/tessellation/EllipsoidTessellator.java +++ b/src/main/java/org/orekit/models/earth/tessellation/EllipsoidTessellator.java @@ -166,8 +166,8 @@ public List> tessellate(final SphericalPolygonsSet zone, final double splitWidth = (fullWidth - widthOverlap) / quantization; final double splitLength = (fullLength - lengthOverlap) / quantization; - final Map> map = new IdentityHashMap>(); - final RegionFactory factory = new RegionFactory(); + final Map> map = new IdentityHashMap<>(); + final RegionFactory factory = new RegionFactory<>(); SphericalPolygonsSet remaining = (SphericalPolygonsSet) zone.copySelf(); S2Point inside = getInsidePoint(remaining); @@ -179,7 +179,7 @@ public List> tessellate(final SphericalPolygonsSet zone, } // find a mesh covering at least one connected part of the zone - final List mergingSeeds = new ArrayList(); + final List mergingSeeds = new ArrayList<>(); Mesh mesh = new Mesh(ellipsoid, zone, aiming, splitLength, splitWidth, inside); mergingSeeds.add(mesh.getNode(0, 0)); List tiles = null; @@ -218,7 +218,7 @@ public List> tessellate(final SphericalPolygonsSet zone, } // concatenate the lists from the independent meshes - final List> tilesLists = new ArrayList>(map.size()); + final List> tilesLists = new ArrayList<>(map.size()); for (final Map.Entry> entry : map.entrySet()) { tilesLists.add(entry.getValue()); } @@ -243,8 +243,8 @@ public List> sample(final SphericalPolygonsSet zone, final double splitWidth = width / quantization; final double splitLength = length / quantization; - final Map> map = new IdentityHashMap>(); - final RegionFactory factory = new RegionFactory(); + final Map> map = new IdentityHashMap<>(); + final RegionFactory factory = new RegionFactory<>(); SphericalPolygonsSet remaining = (SphericalPolygonsSet) zone.copySelf(); S2Point inside = getInsidePoint(remaining); @@ -256,7 +256,7 @@ public List> sample(final SphericalPolygonsSet zone, } // find a mesh covering at least one connected part of the zone - final List mergingSeeds = new ArrayList(); + final List mergingSeeds = new ArrayList<>(); Mesh mesh = new Mesh(ellipsoid, zone, aiming, splitLength, splitWidth, inside); mergingSeeds.add(mesh.getNode(0, 0)); List sample = null; @@ -268,7 +268,7 @@ public List> sample(final SphericalPolygonsSet zone, // extract the sample from the mesh // this further expands the mesh so sample cells dimensions are multiples of quantization, // hence it must be performed here before checking meshes independence - sample = extractSample(mesh, zone); + sample = extractSample(mesh); // check the mesh is independent from existing meshes mergingSeeds.clear(); @@ -295,7 +295,7 @@ public List> sample(final SphericalPolygonsSet zone, } // concatenate the lists from the independent meshes - final List> sampleLists = new ArrayList>(map.size()); + final List> sampleLists = new ArrayList<>(map.size()); for (final Map.Entry> entry : map.entrySet()) { sampleLists.add(entry.getValue()); } @@ -310,7 +310,7 @@ public List> sample(final SphericalPolygonsSet zone, */ private S2Point getInsidePoint(final SphericalPolygonsSet zone) { - final InsideFinder finder = new InsideFinder(zone); + final InsidePointFinder finder = new InsidePointFinder(zone); zone.getTree(false).visit(finder); return finder.getInsidePoint(); @@ -332,8 +332,7 @@ private void neighborExpandMesh(final Mesh mesh, final Collection see // mesh expansion loop boolean expanding = true; - final Queue newNodes = new LinkedList(); - newNodes.addAll(seeds); + final Queue newNodes = new LinkedList<>(seeds); int count = 0; while (expanding) { @@ -392,8 +391,8 @@ private List extractTiles(final Mesh mesh, final SphericalPolygonsSet zone final double lengthOverlap, final double widthOverlap, final boolean truncateLastWidth, final boolean truncateLastLength) { - final List tiles = new ArrayList(); - final List rangePairs = new ArrayList(); + final List tiles = new ArrayList<>(); + final List rangePairs = new ArrayList<>(); final int minAcross = mesh.getMinAcrossIndex(); final int maxAcross = mesh.getMaxAcrossIndex(); @@ -458,12 +457,13 @@ private List extractTiles(final Mesh mesh, final SphericalPolygonsSet zone } - /** Extract a sample of points from a mesh. + /** + * Extract a sample of points from a mesh. + * * @param mesh mesh from which grid should be extracted - * @param zone zone covered by the mesh * @return extracted grid */ - private List extractSample(final Mesh mesh, final SphericalPolygonsSet zone) { + private List extractSample(final Mesh mesh) { // find how to select sample points taking quantization into account // to have the largest possible number of points while still @@ -499,7 +499,7 @@ private List extractSample(final Mesh mesh, final SphericalPolygo } // extract the sample points - final List sample = new ArrayList(selectedCount); + final List sample = new ArrayList<>(selectedCount); for (int across = mesh.getMinAcrossIndex() + selectedAcrossModulus; across <= mesh.getMaxAcrossIndex(); across += quantization) { @@ -824,7 +824,7 @@ public Range next() { final int lower = nextLower; nextLower += quantization; - if (truncateLast && nextLower > maxIndex && lower < maxIndex) { + if (truncateLast && nextLower > maxIndex) { // truncate last tile nextLower = maxIndex; } diff --git a/src/main/java/org/orekit/models/earth/tessellation/InsidePointFinder.java b/src/main/java/org/orekit/models/earth/tessellation/InsidePointFinder.java index 3f1f497e8c..6f7e7323f8 100644 --- a/src/main/java/org/orekit/models/earth/tessellation/InsidePointFinder.java +++ b/src/main/java/org/orekit/models/earth/tessellation/InsidePointFinder.java @@ -36,7 +36,7 @@ *

    * @author Luc Maisonobe */ -class InsideFinder implements BSPTreeVisitor { +class InsidePointFinder implements BSPTreeVisitor { /** Zone of interest. */ private final SphericalPolygonsSet zone; @@ -50,7 +50,7 @@ class InsideFinder implements BSPTreeVisitor { /** Simple constructor. * @param zone zone of interest */ - InsideFinder(final SphericalPolygonsSet zone) { + InsidePointFinder(final SphericalPolygonsSet zone) { this.zone = zone; this.insidePointFirstChoice = null; this.insidePointSecondChoice = null; @@ -89,13 +89,28 @@ public void visitLeafNode(final BSPTree node) { final List boundary = convex.getBoundaryLoops(); final Vertex start = boundary.get(0); int n = 0; - Vector3D sumB = Vector3D.ZERO; + + // Initialize centroid coordinates + Vector3D centroid = Vector3D.ZERO; + + // Iterate through each edge in the boundary loop for (Edge e = start.getOutgoing(); n == 0 || e.getStart() != start; e = e.getEnd().getOutgoing()) { - sumB = new Vector3D(1, sumB, e.getLength(), e.getCircle().getPole()); + // Get the 3D coordinates of the start and end points of the edge + final Vector3D startPoint = e.getStart().getLocation().getVector(); + final Vector3D endPoint = e.getEnd().getLocation().getVector(); + + // Add the coordinates of the start and end points to the centroid + centroid = centroid.add(startPoint).add(endPoint); + + // Increment the counter n++; } - final S2Point candidate = new S2Point(sumB); + // Calculate the average centroid coordinates + centroid = centroid.scalarMultiply(1.0 / (2 * n)); + + // Project the centroid coordinates onto the sphere to get the candidate point + final S2Point candidate = new S2Point(centroid.normalize()); // check the candidate point is really considered inside // it may appear outside if the current leaf cell is very thin diff --git a/src/main/java/org/orekit/models/earth/troposphere/AbstractChaoMappingFunction.java b/src/main/java/org/orekit/models/earth/troposphere/AbstractChaoMappingFunction.java new file mode 100644 index 0000000000..1e5c659a70 --- /dev/null +++ b/src/main/java/org/orekit/models/earth/troposphere/AbstractChaoMappingFunction.java @@ -0,0 +1,92 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.troposphere; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.MathArrays; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; +import org.orekit.models.earth.weather.PressureTemperatureHumidity; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldTrackingCoordinates; +import org.orekit.utils.TrackingCoordinates; + +/** Chao mapping function for radio wavelengths. + * + * @see "C. C. Chao, A model for tropospheric calibration from delay surface and radiosonde ballon measurements, 1972" + * + * @author Luc Maisonobe + * @since 12.1 + */ +public class AbstractChaoMappingFunction implements TroposphereMappingFunction { + + /** First coefficient for hydrostatic (dry) component. */ + private final double ad; + + /** Second coefficient for hydrostatic (dry) component. */ + private final double bd; + + /** First coefficient for wet component. */ + private final double aw; + + /** Second coefficient for wet component. */ + private final double bw; + + /** Builds a new instance. + * @param ad first coefficient for hydrostatic (dry) component + * @param bd second coefficient for hydrostatic (dry) component + * @param aw first coefficient for wet component + * @param bw second coefficient for wet component + */ + protected AbstractChaoMappingFunction(final double ad, final double bd, final double aw, final double bw) { + this.ad = ad; + this.bd = bd; + this.aw = aw; + this.bw = bw; + } + + /** {@inheritDoc} */ + @Override + public double[] mappingFactors(final TrackingCoordinates trackingCoordinates, final GeodeticPoint point, + final PressureTemperatureHumidity weather, + final AbsoluteDate date) { + final double sinE = FastMath.sin(trackingCoordinates.getElevation()); + final double tanE = FastMath.tan(trackingCoordinates.getElevation()); + return new double[] { + 1 / (sinE + ad / (tanE + bd)), + 1 / (sinE + aw / (tanE + bw)) + }; + } + + /** {@inheritDoc} */ + @Override + public > T[] mappingFactors(final FieldTrackingCoordinates trackingCoordinates, + final FieldGeodeticPoint point, + final FieldPressureTemperatureHumidity weather, + final FieldAbsoluteDate date) { + final T sinE = FastMath.sin(trackingCoordinates.getElevation()); + final T tanE = FastMath.tan(trackingCoordinates.getElevation()); + final T[] mapping = MathArrays.buildArray(date.getField(), 2); + mapping[0] = sinE.add(tanE.add(bd).reciprocal().multiply(ad)).reciprocal(); + mapping[1] = sinE.add(tanE.add(bw).reciprocal().multiply(aw)).reciprocal(); + return mapping; + } + +} diff --git a/src/main/java/org/orekit/models/earth/troposphere/AbstractVienna.java b/src/main/java/org/orekit/models/earth/troposphere/AbstractVienna.java new file mode 100644 index 0000000000..195be5a55f --- /dev/null +++ b/src/main/java/org/orekit/models/earth/troposphere/AbstractVienna.java @@ -0,0 +1,182 @@ +/* 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.models.earth.troposphere; + +import java.util.Collections; +import java.util.List; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.FieldSinCos; +import org.hipparchus.util.SinCos; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; +import org.orekit.models.earth.weather.PressureTemperatureHumidity; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.TimeScale; +import org.orekit.utils.FieldTrackingCoordinates; +import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TrackingCoordinates; + +/** The Vienna tropospheric delay model for radio techniques. + * @since 12.1 + * @author Bryan Cazabonne + * @author Luc Maisonobe + */ +public abstract class AbstractVienna implements TroposphericModel, TroposphereMappingFunction { + + /** C coefficient from Chen and Herring gradient mapping function. + * @see Modeling tropospheric delays for space geodetic techniques, Daniel Landskron, 2017, section 2.2 + */ + private static final double C = 0.0032; + + /** Provider for ah and aw coefficients. */ + private final ViennaAProvider aProvider; + + /** Provider for {@link AzimuthalGradientCoefficients} and {@link FieldAzimuthalGradientCoefficients}. */ + private final AzimuthalGradientProvider gProvider; + + /** Provider for zenith delays. */ + private final TroposphericModel zenithDelayProvider; + + /** UTC time scale. */ + private final TimeScale utc; + + /** Build a new instance. + * @param aProvider provider for ah and aw coefficients + * @param gProvider provider for {@link AzimuthalGradientCoefficients} and {@link FieldAzimuthalGradientCoefficients} + * @param zenithDelayProvider provider for zenith delays + * @param utc UTC time scale + */ + protected AbstractVienna(final ViennaAProvider aProvider, + final AzimuthalGradientProvider gProvider, + final TroposphericModel zenithDelayProvider, + final TimeScale utc) { + this.aProvider = aProvider; + this.gProvider = gProvider; + this.zenithDelayProvider = zenithDelayProvider; + this.utc = utc; + } + + /** {@inheritDoc} */ + @Override + public TroposphericDelay pathDelay(final TrackingCoordinates trackingCoordinates, + final GeodeticPoint point, + final PressureTemperatureHumidity weather, + final double[] parameters, final AbsoluteDate date) { + // zenith delay + final TroposphericDelay delays = + zenithDelayProvider.pathDelay(trackingCoordinates, point, weather, parameters, date); + + // mapping function + final double[] mappingFunction = + mappingFactors(trackingCoordinates, point, weather, date); + + // horizontal gradient + final AzimuthalGradientCoefficients agc = gProvider.getGradientCoefficients(point, date); + final double gh; + final double gw; + if (agc != null) { + + // Chen and Herring gradient mapping function + final double sinE = FastMath.sin(trackingCoordinates.getElevation()); + final double tanE = FastMath.tan(trackingCoordinates.getElevation()); + final double mfh = 1.0 / (sinE * tanE + C); + + final SinCos sc = FastMath.sinCos(trackingCoordinates.getAzimuth()); + gh = mfh * (agc.getGnh() * sc.cos() + agc.getGeh() * sc.sin()); + gw = mfh * (agc.getGnw() * sc.cos() + agc.getGew() * sc.sin()); + + } else { + gh = 0; + gw = 0; + } + + // Tropospheric path delay + return new TroposphericDelay(delays.getZh(), + delays.getZw(), + delays.getZh() * mappingFunction[0] + gh, + delays.getZw() * mappingFunction[1] + gw); + + } + + /** {@inheritDoc} */ + @Override + public > FieldTroposphericDelay pathDelay(final FieldTrackingCoordinates trackingCoordinates, + final FieldGeodeticPoint point, + final FieldPressureTemperatureHumidity weather, + final T[] parameters, final FieldAbsoluteDate date) { + // zenith delay + final FieldTroposphericDelay delays = + zenithDelayProvider.pathDelay(trackingCoordinates, point, weather, parameters, date); + + // mapping function + final T[] mappingFunction = + mappingFactors(trackingCoordinates, point, weather, date); + + // horizontal gradient + final FieldAzimuthalGradientCoefficients agc = gProvider.getGradientCoefficients(point, date); + final T gh; + final T gw; + if (agc != null) { + + // Chen and Herring gradient mapping function + final T sinE = FastMath.sin(trackingCoordinates.getElevation()); + final T tanE = FastMath.tan(trackingCoordinates.getElevation()); + final T mfh = sinE.multiply(tanE).add(C).reciprocal(); + + final FieldSinCos sc = FastMath.sinCos(trackingCoordinates.getAzimuth()); + gh = mfh.multiply(agc.getGnh().multiply(sc.cos()).add(agc.getGeh().multiply(sc.sin()))); + gw = mfh.multiply(agc.getGnw().multiply(sc.cos()).add(agc.getGew().multiply(sc.sin()))); + + } else { + gh = date.getField().getZero(); + gw = date.getField().getZero(); + } + + // Tropospheric path delay + return new FieldTroposphericDelay<>(delays.getZh(), + delays.getZw(), + delays.getZh().multiply(mappingFunction[0]).add(gh), + delays.getZw().multiply(mappingFunction[1]).add(gw)); + + } + + /** {@inheritDoc} */ + @Override + public List getParametersDrivers() { + return Collections.emptyList(); + } + + /** Get provider for Vienna ah and aw coefficients. + * @return provider for Vienna ah and aw coefficients + */ + protected ViennaAProvider getAProvider() { + return aProvider; + } + + /** Get day of year. + * @param date date + * @return day of year + */ + protected int getDayOfYear(final AbsoluteDate date) { + return date.getComponents(utc).getDate().getDayOfYear(); + } + +} diff --git a/src/main/java/org/orekit/models/earth/troposphere/AskneNordiusModel.java b/src/main/java/org/orekit/models/earth/troposphere/AskneNordiusModel.java new file mode 100644 index 0000000000..b5f47e54df --- /dev/null +++ b/src/main/java/org/orekit/models/earth/troposphere/AskneNordiusModel.java @@ -0,0 +1,126 @@ +/* Copyright 2002-2024 Thales Alenia Space + * Licensed to CS Communication & Systèmes (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.models.earth.troposphere; + +import java.util.Collections; +import java.util.List; + +import org.hipparchus.CalculusFieldElement; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; +import org.orekit.models.earth.weather.PressureTemperatureHumidity; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.Constants; +import org.orekit.utils.FieldTrackingCoordinates; +import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TrackingCoordinates; + +/** The Askne Nordius model. + *

    + * The hydrostatic part is equivalent to Saastamoinen, whereas the wet part takes + * into account {@link PressureTemperatureHumidity#getTm() mean temperature weighted + * with water vapor pressure} and {@link PressureTemperatureHumidity#getLambda() water + * vapor decrease factor}. + *

    + * @author Luc Maisonobe + * @see "J. Askne and H. Nordius, Estimation of tropospheric delay for microwaves + * from surface weather data, Radio Science, volume 22, number 3, pages 379-386, + * May-June 1987" + * @see "Landskron D (2017) Modeling tropospheric delays for space geodetic + * techniques. Dissertation, Department of Geodesy and Geoinformation, TU Wien, Supervisor: J. Böhm. + * http://repositum.tuwien.ac.at/urn:nbn:at:at-ubtuw:1-100249" + * @since 12.1 + */ +public class AskneNordiusModel implements TroposphericModel { + + /** Lowest acceptable elevation angle [rad]. */ + public static final double LOW_ELEVATION_THRESHOLD = 0.05; + + /** Base delay coefficient (from Saastamoninen model). */ + private static final double L0 = 2.2768e-5; + + /** Askne-Nordius coefficient k'₂. */ + private static final double K_PRIME_2 = 16.5203; + + /** Askne-Nordius coefficient k₃. */ + private static final double K_3 = 377600; + + /** Gas constant for dry components. */ + private static final double RD = 287.0464; + + /** Unit consversion factor. */ + private static final double FACTOR = 1.0e-6; + + /** Mapping function. */ + private final TroposphereMappingFunction mappingFunction; + + /** Create a new Askne Nordius model. + * @param mappingFunction mapping function + */ + public AskneNordiusModel(final TroposphereMappingFunction mappingFunction) { + this.mappingFunction = mappingFunction; + } + + /** {@inheritDoc} */ + @Override + public TroposphericDelay pathDelay(final TrackingCoordinates trackingCoordinates, final GeodeticPoint point, + final PressureTemperatureHumidity weather, + final double[] parameters, final AbsoluteDate date) { + + final double[] mf = mappingFunction.mappingFactors(trackingCoordinates, point, weather, date); + + // calculate the path delay + final double zh = L0 * weather.getPressure(); + final double zw = FACTOR * (K_PRIME_2 + K_3 / weather.getTm()) * + RD * weather.getWaterVaporPressure() / + (Constants.G0_STANDARD_GRAVITY * (weather.getLambda() + 1.0)); + final double sh = zh * mf[0]; + final double sw = zw * mf[1]; + return new TroposphericDelay(zh, zw, sh, sw); + + } + + /** {@inheritDoc} */ + @Override + public > FieldTroposphericDelay pathDelay(final FieldTrackingCoordinates trackingCoordinates, + final FieldGeodeticPoint point, + final FieldPressureTemperatureHumidity weather, + final T[] parameters, final FieldAbsoluteDate date) { + + final T[] mf = mappingFunction.mappingFactors(trackingCoordinates, point, weather, date); + + // calculate the path delay in m + final T zh = weather.getPressure().multiply(L0); + final T zw = weather.getTm().reciprocal().multiply(K_3).add(K_PRIME_2). + multiply(weather.getWaterVaporPressure().multiply(RD)). + divide(weather.getLambda().add(1.0).multiply(Constants.G0_STANDARD_GRAVITY)). + multiply(FACTOR); + final T sh = zh.multiply(mf[0]); + final T sw = zw.multiply(mf[1]); + return new FieldTroposphericDelay<>(zh, zw, sh, sw); + + } + + /** {@inheritDoc} */ + @Override + public List getParametersDrivers() { + return Collections.emptyList(); + } + +} diff --git a/src/main/java/org/orekit/models/earth/troposphere/AzimuthalGradientCoefficients.java b/src/main/java/org/orekit/models/earth/troposphere/AzimuthalGradientCoefficients.java new file mode 100644 index 0000000000..7b540fb4c4 --- /dev/null +++ b/src/main/java/org/orekit/models/earth/troposphere/AzimuthalGradientCoefficients.java @@ -0,0 +1,79 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.troposphere; + +/** Container for the azimuthal gradient coefficients gnh, geh, gnw and gew. + * @author Luc Maisonobe + * @since 12.1 + */ +public class AzimuthalGradientCoefficients { + + /** North hydrostatic coefficient. */ + private final double gnh; + + /** East hydrostatic coefficient. */ + private final double geh; + + /** North wet coefficient. */ + private final double gnw; + + /** East wet coefficient. */ + private final double gew; + + /** Simple constructor. + * @param gnh North hydrostatic coefficient + * @param geh East hydrostatic coefficient + * @param gnw North wet coefficient + * @param gew East wet coefficient + */ + public AzimuthalGradientCoefficients(final double gnh, final double geh, + final double gnw, final double gew) { + this.gnh = gnh; + this.geh = geh; + this.gnw = gnw; + this.gew = gew; + } + + /** Get North hydrostatic coefficient. + * @return North hydrostatic coefficient + */ + public double getGnh() { + return gnh; + } + + /** Get East hydrostatic coefficient. + * @return East hydrostatic coefficient + */ + public double getGeh() { + return geh; + } + + /** Get North wet coefficient. + * @return North wet coefficient + */ + public double getGnw() { + return gnw; + } + + /** Get East wet coefficient. + * @return East wet coefficient + */ + public double getGew() { + return gew; + } + +} diff --git a/src/main/java/org/orekit/models/earth/troposphere/AzimuthalGradientProvider.java b/src/main/java/org/orekit/models/earth/troposphere/AzimuthalGradientProvider.java new file mode 100644 index 0000000000..b649be5723 --- /dev/null +++ b/src/main/java/org/orekit/models/earth/troposphere/AzimuthalGradientProvider.java @@ -0,0 +1,47 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.troposphere; + +import org.hipparchus.CalculusFieldElement; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; + +/** Provider for {@link AzimuthalGradientCoefficients} and {@link FieldAzimuthalGradientCoefficients}. + * @since 12.1 + * @author Luc Maisonobe + */ +public interface AzimuthalGradientProvider { + + /** Get azimuthal asymmetry gradients. + * @param location location at which parameters are requested + * @param date date at which parameters are requested + * @return azimuthal asymmetry gradients or null if no gradients are available + */ + AzimuthalGradientCoefficients getGradientCoefficients(GeodeticPoint location, AbsoluteDate date); + + /** Get azimuthal asymmetry gradients. + * @param type of the field elements + * @param location location at which parameters are requested + * @param date date at which parameters are requested + * @return azimuthal asymmetry gradients or null if no gradients are available + */ + > FieldAzimuthalGradientCoefficients getGradientCoefficients(FieldGeodeticPoint location, + FieldAbsoluteDate date); + +} diff --git a/src/main/java/org/orekit/models/earth/troposphere/CanonicalSaastamoinenModel.java b/src/main/java/org/orekit/models/earth/troposphere/CanonicalSaastamoinenModel.java new file mode 100644 index 0000000000..f8cac97acb --- /dev/null +++ b/src/main/java/org/orekit/models/earth/troposphere/CanonicalSaastamoinenModel.java @@ -0,0 +1,218 @@ +/* Copyright 2002-2024 Thales Alenia Space + * Licensed to CS Communication & Systèmes (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.models.earth.troposphere; + +import java.util.Collections; +import java.util.List; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.analysis.interpolation.LinearInterpolator; +import org.hipparchus.analysis.polynomials.PolynomialSplineFunction; +import org.hipparchus.util.FastMath; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; +import org.orekit.models.earth.weather.HeightDependentPressureTemperatureHumidityConverter; +import org.orekit.models.earth.weather.PressureTemperatureHumidity; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldTrackingCoordinates; +import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TrackingCoordinates; + +/** The canonical Saastamoinen model. + *

    + * Estimates the path delay imposed to + * electro-magnetic signals by the troposphere according to the formula: + * \[ + * \delta = \frac{0.002277}{\cos z (1 - 0.00266\cos 2\varphi - 0.00028 h})} + * \left[P+(\frac{1255}{T}+0.005)e - B(h) \tan^2 z\right] + * \] + * with the following input data provided to the model: + *

      + *
    • z: zenith angle
    • + *
    • P: atmospheric pressure
    • + *
    • T: temperature
    • + *
    • e: partial pressure of water vapor
    • + *
    + * @author Luc Maisonobe + * @see "J Saastamoinen, Atmospheric Correction for the Troposphere and Stratosphere in Radio + * Ranging of Satellites" + * @since 12.1 + */ +public class CanonicalSaastamoinenModel implements TroposphericModel { + + /** Default lowest acceptable elevation angle [rad]. */ + public static final double DEFAULT_LOW_ELEVATION_THRESHOLD = 0.05; + + /** Base delay coefficient. */ + private static final double L0 = 2.2768e-5; + + /** Temperature numerator. */ + private static final double T_NUM = 1255; + + /** Wet offset. */ + private static final double WET_OFFSET = 0.05; + + /** X values for the B function (table 1 in reference paper). */ + private static final double[] X_VALUES_FOR_B = { + 0.0, 200.0, 400.0, 600.0, 800.0, 1000.0, 1500.0, 2000.0, 2500.0, 3000.0, 4000.0, 5000.0, 6000.0 + }; + + /** Y values for the B function (table 1 in reference paper). + *

    + * The values have been scaled up by a factor 100.0 due to conversion from hPa to Pa. + *

    + */ + private static final double[] Y_VALUES_FOR_B = { + 116.0, 113.0, 110.0, 107.0, 104.0, 101.0, 94.0, 88.0, 82.0, 76.0, 66.0, 57.0, 49.0 + }; + + /** Interpolation function for the B correction term. */ + private static final PolynomialSplineFunction B_FUNCTION; + + static { + B_FUNCTION = new LinearInterpolator().interpolate(X_VALUES_FOR_B, Y_VALUES_FOR_B); + } + + /** Lowest acceptable elevation angle [rad]. */ + private double lowElevationThreshold; + + /** + * Create a new Saastamoinen model for the troposphere using the given environmental + * conditions and table from the reference book. + * + * @see HeightDependentPressureTemperatureHumidityConverter + */ + public CanonicalSaastamoinenModel() { + this.lowElevationThreshold = DEFAULT_LOW_ELEVATION_THRESHOLD; + } + + /** {@inheritDoc} + *

    + * The Saastamoinen model is not defined for altitudes below 0.0. for continuity + * reasons, we use the value for h = 0 when altitude is negative. + *

    + *

    + * There are also numerical issues for elevation angles close to zero. For continuity reasons, + * elevations lower than a threshold will use the value obtained + * for the threshold itself. + *

    + * @see #getLowElevationThreshold() + * @see #setLowElevationThreshold(double) + */ + @Override + public TroposphericDelay pathDelay(final TrackingCoordinates trackingCoordinates, final GeodeticPoint point, + final PressureTemperatureHumidity weather, + final double[] parameters, final AbsoluteDate date) { + + // there are no data in the model for negative altitudes and altitude bigger than 6000 m + // limit the height to a range of [0, 5000] m + final double fixedHeight = FastMath.min(FastMath.max(point.getAltitude(), X_VALUES_FOR_B[0]), + X_VALUES_FOR_B[X_VALUES_FOR_B.length - 1]); + + // interpolate the b correction term + final double B = B_FUNCTION.value(fixedHeight); + + // calculate the zenith angle from the elevation + final double z = FastMath.abs(0.5 * FastMath.PI - FastMath.max(trackingCoordinates.getElevation(), + lowElevationThreshold)); + + // calculate the path delay + final double invCos = 1.0 / FastMath.cos(z); + final double tan = FastMath.tan(z); + final double zh = L0 * weather.getPressure(); + final double zw = L0 * (T_NUM / weather.getTemperature() + WET_OFFSET) * weather.getWaterVaporPressure(); + final double sh = zh * invCos; + final double sw = (zw - L0 * B * tan * tan) * invCos; + return new TroposphericDelay(zh, zw, sh, sw); + + } + + /** {@inheritDoc} + *

    + * The Saastamoinen model is not defined for altitudes below 0.0. for continuity + * reasons, we use the value for h = 0 when altitude is negative. + *

    + *

    + * There are also numerical issues for elevation angles close to zero. For continuity reasons, + * elevations lower than a threshold will use the value obtained + * for the threshold itself. + *

    + * @see #getLowElevationThreshold() + * @see #setLowElevationThreshold(double) + */ + @Override + public > FieldTroposphericDelay pathDelay(final FieldTrackingCoordinates trackingCoordinates, + final FieldGeodeticPoint point, + final FieldPressureTemperatureHumidity weather, + final T[] parameters, final FieldAbsoluteDate date) { + + final Field field = date.getField(); + final T zero = field.getZero(); + + // there are no data in the model for negative altitudes and altitude bigger than 5000 m + // limit the height to a range of [0, 5000] m + final T fixedHeight = FastMath.min(FastMath.max(point.getAltitude(), X_VALUES_FOR_B[0]), + X_VALUES_FOR_B[X_VALUES_FOR_B.length - 1]); + + // interpolate the b correction term + final T B = B_FUNCTION.value(fixedHeight); + + // calculate the zenith angle from the elevation + final T z = FastMath.abs(zero.getPi().multiply(0.5). + subtract(FastMath.max(trackingCoordinates.getElevation(), lowElevationThreshold))); + + // calculate the path delay in m + final T invCos = FastMath.cos(z).reciprocal(); + final T tan = FastMath.tan(z); + final T zh = weather.getPressure().multiply(L0); + final T zw = weather.getTemperature().reciprocal().multiply(T_NUM).add(WET_OFFSET). + multiply(weather.getWaterVaporPressure()).multiply(L0); + final T sh = zh.multiply(invCos); + final T sw = zw.subtract(B.multiply(tan).multiply(tan).multiply(L0)).multiply(invCos); + return new FieldTroposphericDelay<>(zh, zw, sh, sw); + + } + + /** {@inheritDoc} */ + @Override + public List getParametersDrivers() { + return Collections.emptyList(); + } + + /** Get the low elevation threshold value for path delay computation. + * @return low elevation threshold, in rad. + * @see #pathDelay(TrackingCoordinates, GeodeticPoint, PressureTemperatureHumidity, double[], AbsoluteDate) + * @see #pathDelay(FieldTrackingCoordinates, FieldGeodeticPoint, FieldPressureTemperatureHumidity, CalculusFieldElement[], FieldAbsoluteDate) + */ + public double getLowElevationThreshold() { + return lowElevationThreshold; + } + + /** Set the low elevation threshold value for path delay computation. + * @param lowElevationThreshold The new value for the threshold [rad] + * @see #pathDelay(TrackingCoordinates, GeodeticPoint, PressureTemperatureHumidity, double[], AbsoluteDate) + * @see #pathDelay(FieldTrackingCoordinates, FieldGeodeticPoint, FieldPressureTemperatureHumidity, CalculusFieldElement[], FieldAbsoluteDate) + */ + public void setLowElevationThreshold(final double lowElevationThreshold) { + this.lowElevationThreshold = lowElevationThreshold; + } + +} + diff --git a/src/main/java/org/orekit/models/earth/troposphere/ChaoMappingFunction.java b/src/main/java/org/orekit/models/earth/troposphere/ChaoMappingFunction.java new file mode 100644 index 0000000000..ad00bfb1b0 --- /dev/null +++ b/src/main/java/org/orekit/models/earth/troposphere/ChaoMappingFunction.java @@ -0,0 +1,46 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.troposphere; + +/** Chao mapping function for radio wavelengths. + * + * @see "C. C. Chao, A model for tropospheric calibration from delay surface and radiosonde ballon measurements, 1972" + * + * @author Luc Maisonobe + * @since 12.1 + */ +public class ChaoMappingFunction extends AbstractChaoMappingFunction { + + /** First coefficient for hydrostatic (dry) component. */ + private static final double AD = 0.00143; + + /** Second coefficient for hydrostatic (dry) component. */ + private static final double BD = 0.0445; + + /** First coefficient for wet component. */ + private static final double AW = 0.00035; + + /** Second coefficient for wet component. */ + private static final double BW = 0.017; + + /** Builds a new instance. + */ + public ChaoMappingFunction() { + super(AD, BD, AW, BW); + } + +} diff --git a/src/main/java/org/orekit/models/earth/troposphere/ConstantAzimuthalGradientProvider.java b/src/main/java/org/orekit/models/earth/troposphere/ConstantAzimuthalGradientProvider.java new file mode 100644 index 0000000000..954317252d --- /dev/null +++ b/src/main/java/org/orekit/models/earth/troposphere/ConstantAzimuthalGradientProvider.java @@ -0,0 +1,61 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.troposphere; + +import org.hipparchus.CalculusFieldElement; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; + +/** Constant provider for {@link AzimuthalGradientCoefficients} and {@link FieldAzimuthalGradientCoefficients}. + * @since 12.1 + * @author Luc Maisonobe + */ +public class ConstantAzimuthalGradientProvider implements AzimuthalGradientProvider { + + /** Constant parameters. */ + private final AzimuthalGradientCoefficients a; + + /** Simple constructor. + * @param a constant parameters (may be null if no gradients are available) + */ + public ConstantAzimuthalGradientProvider(final AzimuthalGradientCoefficients a) { + this.a = a; + } + + /** {@inheritDoc} */ + @Override + public AzimuthalGradientCoefficients getGradientCoefficients(final GeodeticPoint location, + final AbsoluteDate date) { + return a; + } + + /** {@inheritDoc} */ + @Override + public > FieldAzimuthalGradientCoefficients getGradientCoefficients(final FieldGeodeticPoint location, + final FieldAbsoluteDate date) { + final T zero = date.getField().getZero(); + return a == null ? + null : + new FieldAzimuthalGradientCoefficients<>(zero.newInstance(a.getGnh()), + zero.newInstance(a.getGeh()), + zero.newInstance(a.getGnw()), + zero.newInstance(a.getGew())); + } + +} diff --git a/src/main/java/org/orekit/models/earth/troposphere/ConstantTroposphericModel.java b/src/main/java/org/orekit/models/earth/troposphere/ConstantTroposphericModel.java new file mode 100644 index 0000000000..a2f141399c --- /dev/null +++ b/src/main/java/org/orekit/models/earth/troposphere/ConstantTroposphericModel.java @@ -0,0 +1,79 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.troposphere; + +import java.util.Collections; +import java.util.List; + +import org.hipparchus.CalculusFieldElement; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; +import org.orekit.models.earth.weather.PressureTemperatureHumidity; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldTrackingCoordinates; +import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TrackingCoordinates; + +/** Defines a constant tropospheric model. + * @author Luc Maisonobe + * @since 12.1 + */ +public class ConstantTroposphericModel implements TroposphericModel { + + /** Constant delay. */ + private final TroposphericDelay delay; + + /** Simple constructor. + * @param delay constant delay + */ + public ConstantTroposphericModel(final TroposphericDelay delay) { + this.delay = delay; + } + + /** {@inheritDoc} */ + @Override + public TroposphericDelay pathDelay(final TrackingCoordinates trackingCoordinates, + final GeodeticPoint point, + final PressureTemperatureHumidity weather, + final double[] parameters, + final AbsoluteDate date) { + return delay; + } + + /** {@inheritDoc} */ + @Override + public > FieldTroposphericDelay pathDelay(final FieldTrackingCoordinates trackingCoordinates, + final FieldGeodeticPoint point, + final FieldPressureTemperatureHumidity weather, + final T[] parameters, + final FieldAbsoluteDate date) { + final T zero = date.getField().getZero(); + return new FieldTroposphericDelay<>(zero.newInstance(delay.getZh()), + zero.newInstance(delay.getZw()), + zero.newInstance(delay.getSh()), + zero.newInstance(delay.getSw())); + } + + /** {@inheritDoc} */ + @Override + public List getParametersDrivers() { + return Collections.emptyList(); + } + +} diff --git a/src/main/java/org/orekit/models/earth/troposphere/ConstantViennaAProvider.java b/src/main/java/org/orekit/models/earth/troposphere/ConstantViennaAProvider.java new file mode 100644 index 0000000000..51de200b89 --- /dev/null +++ b/src/main/java/org/orekit/models/earth/troposphere/ConstantViennaAProvider.java @@ -0,0 +1,55 @@ +/* Copyright 2002-2024 Thales Alenia Space + * Licensed to CS Communication & Systèmes (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.models.earth.troposphere; + +import org.hipparchus.CalculusFieldElement; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; + +/** Provider for constant Vienna A coefficients. + * @author Luc Maisonobe + * @since 12.1 + */ +public class ConstantViennaAProvider implements ViennaAProvider { + + /** Constant parameters. */ + private final ViennaACoefficients a; + + /** Simple constructor. + * @param a constant parameters + */ + public ConstantViennaAProvider(final ViennaACoefficients a) { + this.a = a; + } + + /** {@inheritDoc} */ + @Override + public ViennaACoefficients getA(final GeodeticPoint location, final AbsoluteDate date) { + return a; + } + + /** {@inheritDoc} */ + @Override + public > FieldViennaACoefficients getA(final FieldGeodeticPoint location, + final FieldAbsoluteDate date) { + return new FieldViennaACoefficients(date.getField().getZero().newInstance(a.getAh()), + date.getField().getZero().newInstance(a.getAw())); + } + +} diff --git a/src/main/java/org/orekit/models/earth/troposphere/DiscreteTroposphericModel.java b/src/main/java/org/orekit/models/earth/troposphere/DiscreteTroposphericModel.java index ceb121d4d8..1d6d2b34eb 100644 --- a/src/main/java/org/orekit/models/earth/troposphere/DiscreteTroposphericModel.java +++ b/src/main/java/org/orekit/models/earth/troposphere/DiscreteTroposphericModel.java @@ -37,7 +37,9 @@ *
  • δnh = non-hydrostatic (or wet) delay
  • * * @author Bryan Cazabonne + * @deprecated as of 12.1, replaced by {@link TroposphericModel} */ +@Deprecated public interface DiscreteTroposphericModel extends ParameterDriversProvider { /** Calculates the tropospheric path delay for the signal path from a ground @@ -63,4 +65,5 @@ public interface DiscreteTroposphericModel extends ParameterDriversProvider { */ > T pathDelay(T elevation, FieldGeodeticPoint point, T[] parameters, FieldAbsoluteDate date); + } diff --git a/src/main/java/org/orekit/models/earth/troposphere/DummyMappingFunction.java b/src/main/java/org/orekit/models/earth/troposphere/DummyMappingFunction.java new file mode 100644 index 0000000000..2ab2a3fea2 --- /dev/null +++ b/src/main/java/org/orekit/models/earth/troposphere/DummyMappingFunction.java @@ -0,0 +1,69 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.troposphere; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.util.MathArrays; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; +import org.orekit.models.earth.weather.PressureTemperatureHumidity; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldTrackingCoordinates; +import org.orekit.utils.TrackingCoordinates; + +/** Dummy mapping function. + *

    + * This mapping function just uses 1.0 as constant mapping factors, which + * implies the slanted tropospheric delays are equal to the zenith delays. + * This is mainly useful when only zenith delays are needed. + *

    + * @author Luc Maisonobe + * @since 12.1 + */ +public class DummyMappingFunction implements TroposphereMappingFunction { + + /** Builds a new instance. + */ + protected DummyMappingFunction() { + // nothing to do + } + + /** {@inheritDoc} */ + @Override + public double[] mappingFactors(final TrackingCoordinates trackingCoordinates, final GeodeticPoint point, + final PressureTemperatureHumidity weather, + final AbsoluteDate date) { + return new double[] { + 1.0, 1.0 + }; + } + + /** {@inheritDoc} */ + @Override + public > T[] mappingFactors(final FieldTrackingCoordinates trackingCoordinates, + final FieldGeodeticPoint point, + final FieldPressureTemperatureHumidity weather, + final FieldAbsoluteDate date) { + final T[] mapping = MathArrays.buildArray(date.getField(), 2); + mapping[0] = date.getField().getOne(); + mapping[1] = date.getField().getOne(); + return mapping; + } + +} diff --git a/src/main/java/org/orekit/models/earth/troposphere/EstimatedModel.java b/src/main/java/org/orekit/models/earth/troposphere/EstimatedModel.java new file mode 100644 index 0000000000..5348320ac2 --- /dev/null +++ b/src/main/java/org/orekit/models/earth/troposphere/EstimatedModel.java @@ -0,0 +1,175 @@ +/* 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.models.earth.troposphere; + +import java.util.Collections; +import java.util.List; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.util.FastMath; +import org.orekit.annotation.DefaultDataContext; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.models.earth.weather.ConstantPressureTemperatureHumidityProvider; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; +import org.orekit.models.earth.weather.PressureTemperatureHumidity; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldTrackingCoordinates; +import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TrackingCoordinates; + +/** An estimated tropospheric model. The tropospheric delay is computed according to the formula: + *

    + * δ = δh * mh + (δt - δh) * mw + *

    + * With: + *

      + *
    • δh: Tropospheric zenith hydro-static delay.
    • + *
    • δt: Tropospheric total zenith delay.
    • + *
    • mh: Hydro-static mapping function.
    • + *
    • mw: Wet mapping function.
    • + *
    + *

    + * The mapping functions mh(e) and mw(e) are + * computed thanks to a {@link #model} initialized by the user. + * The user has the possibility to use several mapping function models for the computations: + * the {@link GlobalMappingFunctionModel Global Mapping Function}, or + * the {@link NiellMappingFunctionModel Niell Mapping Function} + *

    + * The tropospheric zenith delay δh is computed empirically with a + * {@link DiscreteTroposphericModel 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 + */ +public class EstimatedModel implements TroposphericModel { + + /** Name of the parameter of this model: the total zenith delay. */ + public static final String TOTAL_ZENITH_DELAY = "total zenith delay"; + + /** Mapping Function model. */ + private final TroposphereMappingFunction model; + + /** Driver for the tropospheric zenith total delay.*/ + private final ParameterDriver totalZenithDelay; + + /** Model for hydrostatic component. */ + private final TroposphericModel hydrostatic; + + /** Build a new instance using the given environmental conditions. + *

    + * This constructor uses a {@link ModifiedSaastamoinenModel} for the hydrostatic contribution. + *

    + * @param h0 altitude of the station [m] + * @param t0 the temperature at the station [K] + * @param p0 the atmospheric pressure at the station [mbar] + * @param model mapping function model. + * @param totalDelay initial value for the tropospheric zenith total delay [m] + */ + @DefaultDataContext + public EstimatedModel(final double h0, final double t0, final double p0, + final TroposphereMappingFunction model, final double totalDelay) { + this(new ModifiedSaastamoinenModel(new ConstantPressureTemperatureHumidityProvider(new PressureTemperatureHumidity(h0, + TroposphericModelUtils.HECTO_PASCAL.toSI(p0), + t0, + 0.0, + Double.NaN, + Double.NaN))), + model, totalDelay); + } + + /** Build a new instance using the given environmental conditions. + * @param hydrostatic model for hydrostatic component + * @param model mapping function model. + * @param totalDelay initial value for the tropospheric zenith total delay [m] + * @since 12.1 + */ + public EstimatedModel(final TroposphericModel hydrostatic, + final TroposphereMappingFunction model, + final double totalDelay) { + + totalZenithDelay = new ParameterDriver(EstimatedModel.TOTAL_ZENITH_DELAY, + totalDelay, FastMath.scalb(1.0, 0), 0.0, Double.POSITIVE_INFINITY); + + this.hydrostatic = hydrostatic; + this.model = model; + } + + /** Build a new instance using a standard atmosphere model. + *
      + *
    • altitude: 0m + *
    • temperature: 18 degree Celsius + *
    • pressure: 1013.25 mbar + *
    + * @param model mapping function model. + * @param totalDelay initial value for the tropospheric zenith total delay [m] + */ + @DefaultDataContext + public EstimatedModel(final TroposphereMappingFunction model, final double totalDelay) { + this(0.0, 273.15 + 18.0, 1013.25, model, totalDelay); + } + + /** {@inheritDoc} */ + @Override + public List getParametersDrivers() { + return Collections.singletonList(totalZenithDelay); + } + + /** {@inheritDoc} */ + @Override + public TroposphericDelay pathDelay(final TrackingCoordinates trackingCoordinates, + final GeodeticPoint point, + final PressureTemperatureHumidity weather, + final double[] parameters, final AbsoluteDate date) { + + // zenith hydrostatic delay + final double zd = hydrostatic.pathDelay(trackingCoordinates, point, weather, parameters, date).getZh(); + + // zenith wet delay + final double wd = parameters[0] - zd; + + // mapping functions + final double[] mf = model.mappingFactors(trackingCoordinates, point, weather, date); + + // composite delay + return new TroposphericDelay(zd, wd, mf[0] * zd, mf[1] * wd); + + } + + /** {@inheritDoc} */ + @Override + public > FieldTroposphericDelay pathDelay(final FieldTrackingCoordinates trackingCoordinates, + final FieldGeodeticPoint point, + final FieldPressureTemperatureHumidity weather, + final T[] parameters, final FieldAbsoluteDate date) { + + // zenith hydrostatic delay + final T zd = hydrostatic.pathDelay(trackingCoordinates, point, weather, parameters, date).getZh(); + + // zenith wet delay + final T wd = parameters[0].subtract(zd); + + // mapping functions + final T[] mf = model.mappingFactors(trackingCoordinates, point, weather, date); + + // composite delay + return new FieldTroposphericDelay<>(zd, wd, mf[0].multiply(zd), mf[1].multiply(wd)); + + } + +} diff --git a/src/main/java/org/orekit/models/earth/troposphere/EstimatedTroposphericModel.java b/src/main/java/org/orekit/models/earth/troposphere/EstimatedTroposphericModel.java index 3003182d5d..b41c933b74 100644 --- a/src/main/java/org/orekit/models/earth/troposphere/EstimatedTroposphericModel.java +++ b/src/main/java/org/orekit/models/earth/troposphere/EstimatedTroposphericModel.java @@ -16,16 +16,16 @@ */ package org.orekit.models.earth.troposphere; -import java.util.Collections; -import java.util.List; - import org.hipparchus.CalculusFieldElement; -import org.hipparchus.util.FastMath; +import org.orekit.annotation.DefaultDataContext; import org.orekit.bodies.FieldGeodeticPoint; import org.orekit.bodies.GeodeticPoint; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldTrackingCoordinates; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TrackingCoordinates; /** An estimated tropospheric model. The tropospheric delay is computed according to the formula: *

    @@ -45,41 +45,45 @@ * the {@link GlobalMappingFunctionModel Global Mapping Function}, or * the {@link NiellMappingFunctionModel Niell Mapping Function} *

    - * The tropospheric zenith delay δh is computed empirically with a {@link ModifiedSaastamoinenModel} - * while the tropospheric total zenith delay δt is estimated as a {@link ParameterDriver} + * The tropospheric zenith delay δh is computed empirically with a + * {@link DiscreteTroposphericModel 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. + * @deprecated as of 12.1, replaced by {@link EstimatedModel} */ -public class EstimatedTroposphericModel implements DiscreteTroposphericModel { +@Deprecated +public class EstimatedTroposphericModel extends EstimatedModel implements DiscreteTroposphericModel { /** Name of the parameter of this model: the total zenith delay. */ public static final String TOTAL_ZENITH_DELAY = "total zenith delay"; - /** Mapping Function model. */ - private final MappingFunction model; - - /** Driver for the tropospheric zenith total delay.*/ - private final ParameterDriver totalZenithDelay; - - /** The temperature at the station [K]. */ - private double t0; - - /** The atmospheric pressure [mbar]. */ - private double p0; - /** Build a new instance using the given environmental conditions. + *

    + * This constructor uses a {@link ModifiedSaastamoinenModel} for the hydrostatic contribution. + *

    * @param t0 the temperature at the station [K] * @param p0 the atmospheric pressure at the station [mbar] * @param model mapping function model (NMF or GMF). * @param totalDelay initial value for the tropospheric zenith total delay [m] */ + @DefaultDataContext public EstimatedTroposphericModel(final double t0, final double p0, final MappingFunction model, final double totalDelay) { + super(0.0, t0, p0, new TroposphereMappingFunctionAdapter(model), totalDelay); + } - totalZenithDelay = new ParameterDriver(EstimatedTroposphericModel.TOTAL_ZENITH_DELAY, - totalDelay, FastMath.scalb(1.0, 0), 0.0, Double.POSITIVE_INFINITY); - - this.t0 = t0; - this.p0 = p0; - this.model = model; + /** Build a new instance using the given environmental conditions. + * @param hydrostatic model for hydrostatic component + * @param model mapping function model (NMF or GMF). + * @param totalDelay initial value for the tropospheric zenith total delay [m] + * @since 12.1 + */ + public EstimatedTroposphericModel(final DiscreteTroposphericModel hydrostatic, + final MappingFunction model, + final double totalDelay) { + super(new TroposphericModelAdapter(hydrostatic), + new TroposphereMappingFunctionAdapter(model), + totalDelay); } /** Build a new instance using a standard atmosphere model. @@ -90,44 +94,31 @@ public EstimatedTroposphericModel(final double t0, final double p0, * @param model mapping function model (NMF or GMF). * @param totalDelay initial value for the tropospheric zenith total delay [m] */ + @DefaultDataContext public EstimatedTroposphericModel(final MappingFunction model, final double totalDelay) { this(273.15 + 18.0, 1013.25, model, totalDelay); } /** {@inheritDoc} */ @Override - public List getParametersDrivers() { - return Collections.singletonList(totalZenithDelay); - } - - /** {@inheritDoc} */ - @Override + @Deprecated public double pathDelay(final double elevation, final GeodeticPoint point, final double[] parameters, final AbsoluteDate date) { - // Use an empirical model for tropospheric zenith hydro-static delay : Saastamoinen model - final ModifiedSaastamoinenModel saastamoinen = new ModifiedSaastamoinenModel(t0, p0, 0.0); - // Zenith delays. elevation = pi/2 because we compute the delay in the zenith direction - final double zhd = saastamoinen.pathDelay(0.5 * FastMath.PI, point, parameters, date); - final double ztd = parameters[0]; - // Mapping functions - final double[] mf = model.mappingFactors(elevation, point, date); - // Total delay - return mf[0] * zhd + mf[1] * (ztd - zhd); + return pathDelay(new TrackingCoordinates(0.0, elevation, 0.0), point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, parameters, date).getDelay(); } /** {@inheritDoc} */ @Override - public > T pathDelay(final T elevation, final FieldGeodeticPoint point, - final T[] parameters, final FieldAbsoluteDate date) { - // Use an empirical model for tropospheric zenith hydro-static delay : Saastamoinen model - final ModifiedSaastamoinenModel saastamoinen = new ModifiedSaastamoinenModel(t0, p0, 0.0); - // Zenith delays. elevation = pi/2 because we compute the delay in the zenith direction - final T zhd = saastamoinen.pathDelay(elevation.getPi().multiply(0.5), point, parameters, date); - final T ztd = parameters[0]; - // Mapping functions - final T[] mf = model.mappingFactors(elevation, point, date); - // Total delay - return mf[0].multiply(zhd).add(mf[1].multiply(ztd.subtract(zhd))); + @Deprecated + public > T pathDelay(final T elevation, + final FieldGeodeticPoint point, + final T[] parameters, + final FieldAbsoluteDate date) { + return pathDelay(new FieldTrackingCoordinates<>(date.getField().getZero(), elevation, date.getField().getZero()), + point, + new FieldPressureTemperatureHumidity<>(date.getField(), TroposphericModelUtils.STANDARD_ATMOSPHERE), + parameters, date).getDelay(); } } diff --git a/src/main/java/org/orekit/models/earth/troposphere/FieldAzimuthalGradientCoefficients.java b/src/main/java/org/orekit/models/earth/troposphere/FieldAzimuthalGradientCoefficients.java new file mode 100644 index 0000000000..35f3d9bfb8 --- /dev/null +++ b/src/main/java/org/orekit/models/earth/troposphere/FieldAzimuthalGradientCoefficients.java @@ -0,0 +1,82 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.troposphere; + +import org.hipparchus.CalculusFieldElement; + +/** Container for the azimuthal gradient coefficients gnh, geh, gnw and gew. + * @param type of the field elements + * @author Luc Maisonobe + * @since 12.1 + */ +public class FieldAzimuthalGradientCoefficients> { + + /** North hydrostatic coefficient. */ + private final T gnh; + + /** East hydrostatic coefficient. */ + private final T geh; + + /** North wet coefficient. */ + private final T gnw; + + /** East wet coefficient. */ + private final T gew; + + /** Simple constructor. + * @param gnh North hydrostatic coefficient + * @param geh East hydrostatic coefficient + * @param gnw North wet coefficient + * @param gew East wet coefficient + */ + public FieldAzimuthalGradientCoefficients(final T gnh, final T geh, + final T gnw, final T gew) { + this.gnh = gnh; + this.geh = geh; + this.gnw = gnw; + this.gew = gew; + } + + /** Get North hydrostatic coefficient. + * @return North hydrostatic coefficient + */ + public T getGnh() { + return gnh; + } + + /** Get East hydrostatic coefficient. + * @return East hydrostatic coefficient + */ + public T getGeh() { + return geh; + } + + /** Get North wet coefficient. + * @return North wet coefficient + */ + public T getGnw() { + return gnw; + } + + /** Get East wet coefficient. + * @return East wet coefficient + */ + public T getGew() { + return gew; + } + +} diff --git a/src/main/java/org/orekit/models/earth/troposphere/FieldTroposphericDelay.java b/src/main/java/org/orekit/models/earth/troposphere/FieldTroposphericDelay.java new file mode 100644 index 0000000000..bf45363e0c --- /dev/null +++ b/src/main/java/org/orekit/models/earth/troposphere/FieldTroposphericDelay.java @@ -0,0 +1,88 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.troposphere; + +import org.hipparchus.CalculusFieldElement; + +/** Container for tropospheric delay. + * @param type of the field elements + * @author Luc Maisonobe + * @since 12.1 + */ +public class FieldTroposphericDelay> { + + /** Hydrostatic zenith delay (m). */ + private final T zh; + + /** Wet zenith delay (m). */ + private final T zw; + + /** Hydrostatic slanted delay (m). */ + private final T sh; + + /** Wet slanted delay (m). */ + private final T sw; + + /** Simple constructor. + * @param zh hydrostatic zenith delay (m) + * @param zw wet zenith delay (m) + * @param sh hydrostatic slanted delay (m) + * @param sw wet slanted delay (m) + */ + public FieldTroposphericDelay(final T zh, final T zw, final T sh, final T sw) { + this.zh = zh; + this.zw = zw; + this.sh = sh; + this.sw = sw; + } + + /** Get hydrostatic zenith delay (m). + * @return hydrostatic zenith delay (m) + */ + public T getZh() { + return zh; + } + + /** Get wet zenith delay (m). + * @return wet zenith delay (m) + */ + public T getZw() { + return zw; + } + + /** Get slanted delay (m). + * @return slanted delay (m) + */ + public T getSh() { + return sh; + } + + /** Get wet slanted delay (m). + * @return wet slanted delay (m) + */ + public T getSw() { + return sw; + } + + /** Get the total slanted delay (m). + * @return total slanted delay (m) + */ + public T getDelay() { + return sh.add(sw); + } + +} diff --git a/src/main/java/org/orekit/models/earth/troposphere/FieldViennaACoefficients.java b/src/main/java/org/orekit/models/earth/troposphere/FieldViennaACoefficients.java new file mode 100644 index 0000000000..34a02f5e21 --- /dev/null +++ b/src/main/java/org/orekit/models/earth/troposphere/FieldViennaACoefficients.java @@ -0,0 +1,57 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.troposphere; + +import org.hipparchus.CalculusFieldElement; + +/** Container for the {@link ViennaOne} and {@link ViennaThree} coefficients ah and aw. + * @param type of the field elements + * @author Luc Maisonobe + * @since 12.1 + */ +public class FieldViennaACoefficients> { + + /** Hydrostatic coefficient. */ + private final T ah; + + /** Wet coefficient. */ + private final T aw; + + /** Simple constructor. + * @param ah hydrostatic coefficient + * @param aw wet coefficient + */ + public FieldViennaACoefficients(final T ah, final T aw) { + this.ah = ah; + this.aw = aw; + } + + /** Get hydrostatic coefficient. + * @return hydrostatic coefficient + */ + public T getAh() { + return ah; + } + + /** Get wet coefficient. + * @return wet coefficient + */ + public T getAw() { + return aw; + } + +} diff --git a/src/main/java/org/orekit/models/earth/troposphere/FixedTroposphericDelay.java b/src/main/java/org/orekit/models/earth/troposphere/FixedTroposphericDelay.java index 12e2aee5bb..8c0c4e4b47 100644 --- a/src/main/java/org/orekit/models/earth/troposphere/FixedTroposphericDelay.java +++ b/src/main/java/org/orekit/models/earth/troposphere/FixedTroposphericDelay.java @@ -23,6 +23,7 @@ import org.hipparchus.analysis.interpolation.PiecewiseBicubicSplineInterpolatingFunction; import org.hipparchus.analysis.interpolation.PiecewiseBicubicSplineInterpolator; import org.hipparchus.util.FastMath; +import org.hipparchus.util.MathUtils; import org.orekit.annotation.DefaultDataContext; import org.orekit.bodies.FieldGeodeticPoint; import org.orekit.bodies.GeodeticPoint; @@ -30,17 +31,22 @@ import org.orekit.data.DataProvidersManager; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; +import org.orekit.models.earth.weather.PressureTemperatureHumidity; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldTrackingCoordinates; import org.orekit.utils.InterpolationTableLoader; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TrackingCoordinates; /** A static tropospheric model that interpolates the actual tropospheric delay * based on values read from a configuration file (tropospheric-delay.txt) via * the {@link DataProvidersManager}. * @author Thomas Neidhart */ -public class FixedTroposphericDelay implements DiscreteTroposphericModel { +@SuppressWarnings("deprecation") +public class FixedTroposphericDelay implements DiscreteTroposphericModel, TroposphericModel { /** Singleton object for the default model. */ private static FixedTroposphericDelay defaultModel; @@ -128,32 +134,72 @@ public static FixedTroposphericDelay getDefaultModel() { /** {@inheritDoc} */ @Override + @Deprecated public double pathDelay(final double elevation, final GeodeticPoint point, final double[] parameters, final AbsoluteDate date) { + return pathDelay(new TrackingCoordinates(0.0, elevation, 0.0), point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, parameters, date).getDelay(); + } + + /** {@inheritDoc} + *

    + * All delays are affected to {@link TroposphericDelay#getZh() hydrostatic zenith} + * and {@link TroposphericDelay#getSh() hydrostatic slanted} delays, the wet delays + * are arbitrarily set to 0. + *

    + */ + @Override + public TroposphericDelay pathDelay(final TrackingCoordinates trackingCoordinates, final GeodeticPoint point, + final PressureTemperatureHumidity weather, + final double[] parameters, final AbsoluteDate date) { // limit the height to 5000 m final double h = FastMath.min(FastMath.max(0, point.getAltitude()), 5000); // limit the elevation to 0 - π - final double ele = FastMath.min(FastMath.PI, FastMath.max(0d, elevation)); + final double ele = FastMath.min(FastMath.PI, FastMath.max(0d, trackingCoordinates.getElevation())); // mirror elevation at the right angle of π/2 final double e = ele > 0.5 * FastMath.PI ? FastMath.PI - ele : ele; - return delayFunction.value(h, e); + return new TroposphericDelay(delayFunction.value(h, MathUtils.SEMI_PI), 0.0, + delayFunction.value(h, e), 0.0); } /** {@inheritDoc} */ @Override + @Deprecated public > T pathDelay(final T elevation, final FieldGeodeticPoint point, - final T[] parameters, final FieldAbsoluteDate date) { + final T[] parameters, final FieldAbsoluteDate date) { + return pathDelay(new FieldTrackingCoordinates<>(date.getField().getZero(), elevation, date.getField().getZero()), + point, + new FieldPressureTemperatureHumidity<>(date.getField(), TroposphericModelUtils.STANDARD_ATMOSPHERE), + parameters, date).getDelay(); + } + + /** {@inheritDoc} + *

    + * All delays are affected to {@link FieldTroposphericDelay#getZh() hydrostatic zenith} + * and {@link FieldTroposphericDelay#getSh() hydrostatic slanted} delays, the wet delays + * are arbitrarily set to 0. + *

    + */ + @Override + public > FieldTroposphericDelay pathDelay(final FieldTrackingCoordinates trackingCoordinates, + final FieldGeodeticPoint point, + final FieldPressureTemperatureHumidity weather, + final T[] parameters, final FieldAbsoluteDate date) { final T zero = date.getField().getZero(); final T pi = zero.getPi(); // limit the height to 5000 m - final T h = FastMath.min(FastMath.max(zero, point.getAltitude()), zero.add(5000)); + final T h = FastMath.min(FastMath.max(zero, point.getAltitude()), zero.newInstance(5000)); // limit the elevation to 0 - π - final T ele = FastMath.min(pi, FastMath.max(zero, elevation)); + final T ele = FastMath.min(pi, FastMath.max(zero, trackingCoordinates.getElevation())); // mirror elevation at the right angle of π/2 final T e = ele.getReal() > pi.multiply(0.5).getReal() ? ele.negate().add(pi) : ele; - return delayFunction.value(h, e); + return new FieldTroposphericDelay<>(delayFunction.value(h, date.getField().getZero().newInstance(MathUtils.SEMI_PI)), + date.getField().getZero(), + delayFunction.value(h, e), + date.getField().getZero()); + } /** {@inheritDoc} */ diff --git a/src/main/java/org/orekit/models/earth/troposphere/GlobalMappingFunctionModel.java b/src/main/java/org/orekit/models/earth/troposphere/GlobalMappingFunctionModel.java index e0cc2c0e51..d358daf3e1 100644 --- a/src/main/java/org/orekit/models/earth/troposphere/GlobalMappingFunctionModel.java +++ b/src/main/java/org/orekit/models/earth/troposphere/GlobalMappingFunctionModel.java @@ -16,8 +16,8 @@ */ package org.orekit.models.earth.troposphere; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.util.FastMath; import org.hipparchus.util.FieldSinCos; import org.hipparchus.util.MathArrays; @@ -26,12 +26,16 @@ import org.orekit.bodies.FieldGeodeticPoint; import org.orekit.bodies.GeodeticPoint; import org.orekit.data.DataContext; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; +import org.orekit.models.earth.weather.PressureTemperatureHumidity; import org.orekit.time.AbsoluteDate; import org.orekit.time.DateTimeComponents; import org.orekit.time.FieldAbsoluteDate; import org.orekit.time.TimeScale; import org.orekit.utils.FieldLegendrePolynomials; +import org.orekit.utils.FieldTrackingCoordinates; import org.orekit.utils.LegendrePolynomials; +import org.orekit.utils.TrackingCoordinates; /** The Global Mapping Function model for radio techniques. * This model is an empirical mapping function. It only needs the @@ -54,7 +58,8 @@ * @author Bryan Cazabonne * */ -public class GlobalMappingFunctionModel implements MappingFunction { +@SuppressWarnings("deprecation") +public class GlobalMappingFunctionModel implements MappingFunction, TroposphereMappingFunction { /** Multiplication factor for mapping function coefficients. */ private static final double FACTOR = 1.0e-5; @@ -83,8 +88,20 @@ public GlobalMappingFunctionModel(final TimeScale utc) { /** {@inheritDoc} */ @Override + @Deprecated public double[] mappingFactors(final double elevation, final GeodeticPoint point, final AbsoluteDate date) { + return mappingFactors(new TrackingCoordinates(0.0, elevation, 0.0), point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + date); + } + + /** {@inheritDoc} */ + @Override + public double[] mappingFactors(final TrackingCoordinates trackingCoordinates, + final GeodeticPoint point, + final PressureTemperatureHumidity weather, + final AbsoluteDate date) { // Day of year computation final DateTimeComponents dtc = date.getComponents(utc); final int dofyear = dtc.getDate().getDayOfYear(); @@ -163,11 +180,14 @@ public double[] mappingFactors(final double elevation, final GeodeticPoint point final double aw = a0Wet + amplWet * FastMath.cos(coef - psi); final double[] function = new double[2]; - function[0] = TroposphericModelUtils.mappingFunction(ah, bh, ch, elevation); - function[1] = TroposphericModelUtils.mappingFunction(aw, bw, cw, elevation); + function[0] = TroposphericModelUtils.mappingFunction(ah, bh, ch, + trackingCoordinates.getElevation()); + function[1] = TroposphericModelUtils.mappingFunction(aw, bw, cw, + trackingCoordinates.getElevation()); // Apply height correction - final double correction = TroposphericModelUtils.computeHeightCorrection(elevation, point.getAltitude()); + final double correction = TroposphericModelUtils.computeHeightCorrection(trackingCoordinates.getElevation(), + point.getAltitude()); function[0] = function[0] + correction; return function; @@ -175,8 +195,22 @@ public double[] mappingFactors(final double elevation, final GeodeticPoint point /** {@inheritDoc} */ @Override + @Deprecated public > T[] mappingFactors(final T elevation, final FieldGeodeticPoint point, - final FieldAbsoluteDate date) { + final FieldAbsoluteDate date) { + return mappingFactors(new FieldTrackingCoordinates<>(date.getField().getZero(), elevation, date.getField().getZero()), + point, + new FieldPressureTemperatureHumidity<>(date.getField(), + TroposphericModelUtils.STANDARD_ATMOSPHERE), + date); + } + + /** {@inheritDoc} */ + @Override + public > T[] mappingFactors(final FieldTrackingCoordinates trackingCoordinates, + final FieldGeodeticPoint point, + final FieldPressureTemperatureHumidity weather, + final FieldAbsoluteDate date) { // Day of year computation final DateTimeComponents dtc = date.getComponents(utc); final int dofyear = dtc.getDate().getDayOfYear(); @@ -185,8 +219,8 @@ public > T[] mappingFactors(final T elevation, final T zero = field.getZero(); // bh and ch constants (Boehm, J et al, 2006) | HYDROSTATIC PART - final T bh = zero.add(0.0029); - final T c0h = zero.add(0.062); + final T bh = zero.newInstance(0.0029); + final T c0h = zero.newInstance(0.062); final T c10h; final T c11h; final T psi; @@ -197,12 +231,12 @@ public > T[] mappingFactors(final T elevation, // sin(latitude) > 0 -> northern hemisphere if (FastMath.sin(latitude.getReal()) > 0) { - c10h = zero.add(0.001); - c11h = zero.add(0.005); + c10h = zero.newInstance(0.001); + c11h = zero.newInstance(0.005); psi = zero; } else { - c10h = zero.add(0.002); - c11h = zero.add(0.007); + c10h = zero.newInstance(0.002); + c11h = zero.newInstance(0.007); psi = zero.getPi(); } @@ -215,8 +249,8 @@ public > T[] mappingFactors(final T elevation, final T ch = c11h.divide(2.0).multiply(FastMath.cos(coef).add(1.0)).add(c10h).multiply(FastMath.cos(latitude).negate().add(1.0)).add(c0h); // bw and cw constants (Boehm, J et al, 2006) | WET PART - final T bw = zero.add(0.00146); - final T cw = zero.add(0.04391); + final T bw = zero.newInstance(0.00146); + final T cw = zero.newInstance(0.04391); // Compute coefficients ah and aw with spherical harmonics Eq. 3 (Ref 1) @@ -262,11 +296,15 @@ public > T[] mappingFactors(final T elevation, final T aw = a0Wet.add(amplWet.multiply(FastMath.cos(coef.subtract(psi)))); final T[] function = MathArrays.buildArray(field, 2); - function[0] = TroposphericModelUtils.mappingFunction(ah, bh, ch, elevation); - function[1] = TroposphericModelUtils.mappingFunction(aw, bw, cw, elevation); + function[0] = TroposphericModelUtils.mappingFunction(ah, bh, ch, + trackingCoordinates.getElevation()); + function[1] = TroposphericModelUtils.mappingFunction(aw, bw, cw, + trackingCoordinates.getElevation()); // Apply height correction - final T correction = TroposphericModelUtils.computeHeightCorrection(elevation, point.getAltitude(), field); + final T correction = TroposphericModelUtils.computeHeightCorrection(trackingCoordinates.getElevation(), + point.getAltitude(), + field); function[0] = function[0].add(correction); return function; diff --git a/src/main/java/org/orekit/models/earth/troposphere/MappingFunction.java b/src/main/java/org/orekit/models/earth/troposphere/MappingFunction.java index eef1e7e4a0..49923535e2 100644 --- a/src/main/java/org/orekit/models/earth/troposphere/MappingFunction.java +++ b/src/main/java/org/orekit/models/earth/troposphere/MappingFunction.java @@ -24,7 +24,9 @@ /** Interface for mapping functions used in the tropospheric delay computation. * @author Bryan Cazabonne + * @deprecated as of 12.1, replaced by {@link TroposphereMappingFunction} */ +@Deprecated public interface MappingFunction { /** This method allows the computation of the hydrostatic and diff --git a/src/main/java/org/orekit/models/earth/troposphere/MariniMurray.java b/src/main/java/org/orekit/models/earth/troposphere/MariniMurray.java new file mode 100644 index 0000000000..db4f401d97 --- /dev/null +++ b/src/main/java/org/orekit/models/earth/troposphere/MariniMurray.java @@ -0,0 +1,171 @@ +/* Copyright 2011-2012 Space Applications Services + * Licensed to CS Communication & Systèmes (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.models.earth.troposphere; + +import java.util.Collections; +import java.util.List; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.util.FastMath; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; +import org.orekit.models.earth.weather.PressureTemperatureHumidity; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldTrackingCoordinates; +import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TrackingCoordinates; +import org.orekit.utils.units.Unit; +import org.orekit.utils.units.UnitsConverter; + +/** The Marini-Murray tropospheric delay model for laser ranging. + * + * @see "Marini, J.W., and C.W. Murray, correction of Laser Range Tracking Data for + * Atmospheric Refraction at Elevations Above 10 degrees, X-591-73-351, NASA GSFC, 1973" + * + * @author Joris Olympio + * @author Luc Maisonobe + * @since 12.1 + */ +public class MariniMurray implements TroposphericModel { + + /** Laser frequency parameter. */ + private final double fLambda; + + /** Create a new Marini-Murray model for the troposphere. + * @param lambda laser wavelength + * @param lambdaUnits units in which {@code lambda} is given + * @see TroposphericModelUtils#MICRO_M + * @see TroposphericModelUtils#NANO_M + * @since 12.1 + * */ + public MariniMurray(final double lambda, final Unit lambdaUnits) { + + // compute laser frequency parameter + final double lambdaMicrometer = new UnitsConverter(lambdaUnits, TroposphericModelUtils.MICRO_M).convert(lambda); + final double l2 = lambdaMicrometer * lambdaMicrometer; + fLambda = 0.9650 + (0.0164 + 0.000228 / l2) / l2; + + } + + /** {@inheritDoc} */ + @Override + public TroposphericDelay pathDelay(final TrackingCoordinates trackingCoordinates, final GeodeticPoint point, + final PressureTemperatureHumidity weather, + final double[] parameters, final AbsoluteDate date) { + + final double p = weather.getPressure(); + final double t = weather.getTemperature(); + final double e = weather.getWaterVaporPressure(); + + // beware since version 12.1 pressures are in Pa and not in hPa, hence the scaling has changed + final double Ah = 0.00002357 * p; + final double Aw = 0.00000141 * e; + final double K = 1.163 - 0.00968 * FastMath.cos(2 * point.getLatitude()) - 0.00104 * t + 0.0000001435 * p; + final double B = 1.084e-10 * p * t * K + 4.734e-12 * p * (p / t) * (2 * K) / (3 * K - 1); + final double flambda = getLaserFrequencyParameter(); + + final double fsite = getSiteFunctionValue(point); + + final double sinE = FastMath.sin(trackingCoordinates.getElevation()); + final double totalZenith = (flambda / fsite) * (Ah + Aw + B) / (1.0 + B / ((Ah + Aw + B) * (1.0 + 0.01))); + final double totalElev = (flambda / fsite) * (Ah + Aw + B) / (sinE + B / ((Ah + Aw + B) * (sinE + 0.01))); + final double hydrostaticZenith = (flambda / fsite) * (Ah + B) / (1.0 + B / ((Ah + B) * (1.0 + 0.01))); + final double hydrostaticElev = (flambda / fsite) * (Ah + B) / (sinE + B / ((Ah + B) * (sinE + 0.01))); + return new TroposphericDelay(hydrostaticZenith, totalZenith - hydrostaticZenith, + hydrostaticElev, totalElev - hydrostaticElev); + } + + /** {@inheritDoc} */ + @Override + public > FieldTroposphericDelay pathDelay(final FieldTrackingCoordinates trackingCoordinates, + final FieldGeodeticPoint point, + final FieldPressureTemperatureHumidity weather, + final T[] parameters, final FieldAbsoluteDate date) { + + final T p = weather.getPressure(); + final T t = weather.getTemperature(); + final T e = weather.getWaterVaporPressure(); + + // beware since version 12.1 pressures are in Pa and not in hPa, hence the scaling has changed + final T Ah = p.multiply(0.00002357); + final T Aw = e.multiply(0.00000141); + final T K = FastMath.cos(point.getLatitude().multiply(2.)).multiply(0.00968).negate(). + add(1.163). + subtract(t.multiply(0.00104)). + add(p.multiply(0.0000001435)); + final T B = K.multiply(t.multiply(p).multiply(1.084e-10 )). + add(K.multiply(2.).multiply(p.multiply(p).divide(t).multiply(4.734e-12)).divide(K.multiply(3.).subtract(1.))); + final double flambda = getLaserFrequencyParameter(); + + final T fsite = getSiteFunctionValue(point); + + final T sinE = FastMath.sin(trackingCoordinates.getElevation()); + final T one = date.getField().getOne(); + final T totalZenith = fsite.divide(flambda).reciprocal(). + multiply(B.add(Ah).add(Aw)). + divide(one.add(one.add(0.01).multiply(B.add(Ah).add(Aw)).divide(B).reciprocal())); + final T totalElev = fsite.divide(flambda).reciprocal(). + multiply(B.add(Ah).add(Aw)). + divide(sinE.add(sinE.add(0.01).multiply(B.add(Ah).add(Aw)).divide(B).reciprocal())); + final T hydrostaticZenith = fsite.divide(flambda).reciprocal(). + multiply(B.add(Ah)). + divide(one.add(one.add(0.01).multiply(B.add(Ah)).divide(B).reciprocal())); + final T hydrostaticElev = fsite.divide(flambda).reciprocal(). + multiply(B.add(Ah)). + divide(sinE.add(sinE.add(0.01).multiply(B.add(Ah)).divide(B).reciprocal())); + return new FieldTroposphericDelay<>(hydrostaticZenith, totalZenith.subtract(hydrostaticZenith), + hydrostaticElev, totalElev.subtract(hydrostaticElev)); + } + + /** {@inheritDoc} */ + @Override + public List getParametersDrivers() { + return Collections.emptyList(); + } + + /** Get the laser frequency parameter f(lambda). + * It is one for Ruby laser (lambda = 0.6943 micron) + * For infrared lasers, f(lambda) = 0.97966. + * + * @return the laser frequency parameter f(lambda). + */ + private double getLaserFrequencyParameter() { + return fLambda; + } + + /** Get the site parameter. + * + * @param point station location + * @return the site parameter. + */ + private double getSiteFunctionValue(final GeodeticPoint point) { + return 1. - 0.0026 * FastMath.cos(2 * point.getLatitude()) - 0.00031 * 0.001 * point.getAltitude(); + } + + /** Get the site parameter. + * + * @param type of the elements + * @param point station location + * @return the site parameter. + */ + private > T getSiteFunctionValue(final FieldGeodeticPoint point) { + return FastMath.cos(point.getLatitude().multiply(2)).multiply(0.0026).add(point.getAltitude().multiply(0.001).multiply(0.00031)).negate().add(1.); + } + +} diff --git a/src/main/java/org/orekit/models/earth/troposphere/MariniMurrayModel.java b/src/main/java/org/orekit/models/earth/troposphere/MariniMurrayModel.java index b24a2b8d3d..c6f41ad04e 100644 --- a/src/main/java/org/orekit/models/earth/troposphere/MariniMurrayModel.java +++ b/src/main/java/org/orekit/models/earth/troposphere/MariniMurrayModel.java @@ -16,16 +16,16 @@ */ package org.orekit.models.earth.troposphere; -import java.util.Collections; -import java.util.List; - import org.hipparchus.CalculusFieldElement; -import org.hipparchus.util.FastMath; import org.orekit.bodies.FieldGeodeticPoint; import org.orekit.bodies.GeodeticPoint; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; +import org.orekit.models.earth.weather.PressureTemperatureHumidity; +import org.orekit.models.earth.weather.water.CIPM2007; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; -import org.orekit.utils.ParameterDriver; +import org.orekit.utils.FieldTrackingCoordinates; +import org.orekit.utils.TrackingCoordinates; /** The Marini-Murray tropospheric delay model for laser ranging. * @@ -33,33 +33,32 @@ * Atmospheric Refraction at Elevations Above 10 degrees, X-591-73-351, NASA GSFC, 1973" * * @author Joris Olympio + * @deprecated as of 12.1, replaced by {@link MariniMurray} */ -public class MariniMurrayModel implements DiscreteTroposphericModel { - - /** The temperature at the station, K. */ - private double T0; - - /** The atmospheric pressure, mbar. */ - private double P0; - - /** water vapor pressure at the laser site, mbar. */ - private double e0; +@Deprecated +public class MariniMurrayModel extends MariniMurray implements DiscreteTroposphericModel { - /** Laser wavelength, micrometers. */ - private double lambda; + /** Constant pressure, temperature and humidity. */ + private final PressureTemperatureHumidity pth; /** Create a new Marini-Murray model for the troposphere using the given * environmental conditions. * @param t0 the temperature at the station, K * @param p0 the atmospheric pressure at the station, mbar - * @param rh the humidity at the station, percent (50% -> 0.5) + * @param rh the humidity at the station, as a ratio (50% → 0.5) * @param lambda laser wavelength (c/f), nm */ public MariniMurrayModel(final double t0, final double p0, final double rh, final double lambda) { - this.T0 = t0; - this.P0 = p0; - this.e0 = getWaterVapor(rh); - this.lambda = lambda * 1e-3; + super(lambda, TroposphericModelUtils.NANO_M); + this.pth = new PressureTemperatureHumidity(0, + TroposphericModelUtils.HECTO_PASCAL.toSI(p0), + t0, + new CIPM2007(). + waterVaporPressure(TroposphericModelUtils.HECTO_PASCAL.toSI(p0), + t0, + rh), + Double.NaN, + Double.NaN); } /** Create a new Marini-Murray model using a standard atmosphere model. @@ -75,98 +74,32 @@ public MariniMurrayModel(final double t0, final double p0, final double rh, fina * @return a Marini-Murray model with standard environmental values */ public static MariniMurrayModel getStandardModel(final double lambda) { - return new MariniMurrayModel(273.15 + 20, 1013.25, 0.5, lambda); + final double p = TroposphericModelUtils.HECTO_PASCAL.toSI(1013.25); + final double t = 273.15 + 20; + final double rh = 0.5; + return new MariniMurrayModel(t, p, rh, lambda); } /** {@inheritDoc} */ @Override public double pathDelay(final double elevation, final GeodeticPoint point, final double[] parameters, final AbsoluteDate date) { - final double A = 0.002357 * P0 + 0.000141 * e0; - final double K = 1.163 - 0.00968 * FastMath.cos(2 * point.getLatitude()) - 0.00104 * T0 + 0.00001435 * P0; - final double B = (1.084 * 1e-8) * P0 * T0 * K + (4.734 * 1e-8) * P0 * (P0 / T0) * (2 * K) / (3 * K - 1); - final double flambda = getLaserFrequencyParameter(); - - final double fsite = getSiteFunctionValue(point); - - final double sinE = FastMath.sin(elevation); - final double dR = (flambda / fsite) * (A + B) / (sinE + B / ((A + B) * (sinE + 0.01)) ); - return dR; + return pathDelay(new TrackingCoordinates(0.0, elevation, 0.0), point, + pth, parameters, date). + getDelay(); } /** {@inheritDoc} */ @Override - public > T pathDelay(final T elevation, final FieldGeodeticPoint point, - final T[] parameters, final FieldAbsoluteDate date) { - final double A = 0.002357 * P0 + 0.000141 * e0; - final T K = FastMath.cos(point.getLatitude().multiply(2.)).multiply(0.00968).negate().add(1.163).subtract(0.00104 * T0).add(0.00001435 * P0); - final T B = K.multiply((1.084 * 1e-8) * P0 * T0).add(K.multiply(2.).multiply((4.734 * 1e-8) * P0 * (P0 / T0)).divide(K.multiply(3.).subtract(1.))); - final double flambda = getLaserFrequencyParameter(); - - final T fsite = getSiteFunctionValue(point); - - final T sinE = FastMath.sin(elevation); - final T dR = fsite.divide(flambda).reciprocal().multiply(B.add(A)).divide(sinE.add(sinE.add(0.01).multiply(B.add(A)).divide(B).reciprocal())); - return dR; - } - - /** {@inheritDoc} */ - @Override - public List getParametersDrivers() { - return Collections.emptyList(); - } - - /** Get the laser frequency parameter f(lambda). - * It is one for Ruby laser (lambda = 0.6943 micron) - * For infrared lasers, f(lambda) = 0.97966. - * - * @return the laser frequency parameter f(lambda). - */ - private double getLaserFrequencyParameter() { - return 0.9650 + 0.0164 * FastMath.pow(lambda, -2) + 0.000228 * FastMath.pow(lambda, -4); - } - - /** Get the laser frequency parameter f(lambda). - * - * @param point station location - * @return the laser frequency parameter f(lambda). - */ - private double getSiteFunctionValue(final GeodeticPoint point) { - return 1. - 0.0026 * FastMath.cos(2 * point.getLatitude()) - 0.00031 * 0.001 * point.getAltitude(); - } - - /** Get the laser frequency parameter f(lambda). - * - * @param type of the elements - * @param point station location - * @return the laser frequency parameter f(lambda). - */ - private > T getSiteFunctionValue(final FieldGeodeticPoint point) { - return FastMath.cos(point.getLatitude().multiply(2)).multiply(0.0026).add(point.getAltitude().multiply(0.001).multiply(0.00031)).negate().add(1.); - } - - /** Get the water vapor. - * The water vapor model is the one of Giacomo and Davis as indicated in IERS TN 32, chap. 9. - * - * See: Giacomo, P., Equation for the dertermination of the density of moist air, Metrologia, V. 18, 1982 - * - * @param rh relative humidity, in percent (50% -> 0.5). - * @return the water vapor, in mbar (1 mbar = 100 Pa). - */ - private double getWaterVapor(final double rh) { - - // saturation water vapor, equation (3) of reference paper, in mbar - // with amended 1991 values (see reference paper) - final double es = 0.01 * FastMath.exp((1.2378847 * 1e-5) * T0 * T0 - - (1.9121316 * 1e-2) * T0 + - 33.93711047 - - (6.3431645 * 1e3) * 1. / T0); - - // enhancement factor, equation (4) of reference paper - final double fw = 1.00062 + (3.14 * 1e-6) * P0 + (5.6 * 1e-7) * FastMath.pow(T0 - 273.15, 2); - - final double e = rh * fw * es; - return e; + public > T pathDelay(final T elevation, + final FieldGeodeticPoint point, + final T[] parameters, + final FieldAbsoluteDate date) { + return pathDelay(new FieldTrackingCoordinates<>(date.getField().getZero(), elevation, date.getField().getZero()), + point, + new FieldPressureTemperatureHumidity<>(date.getField(), pth), + parameters, date). + getDelay(); } } diff --git a/src/main/java/org/orekit/models/earth/troposphere/MendesPavlisModel.java b/src/main/java/org/orekit/models/earth/troposphere/MendesPavlisModel.java index d579e0c982..1131516808 100644 --- a/src/main/java/org/orekit/models/earth/troposphere/MendesPavlisModel.java +++ b/src/main/java/org/orekit/models/earth/troposphere/MendesPavlisModel.java @@ -19,15 +19,24 @@ import java.util.Collections; import java.util.List; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.util.FastMath; import org.hipparchus.util.MathArrays; import org.orekit.bodies.FieldGeodeticPoint; import org.orekit.bodies.GeodeticPoint; +import org.orekit.models.earth.weather.ConstantPressureTemperatureHumidityProvider; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; +import org.orekit.models.earth.weather.PressureTemperatureHumidity; +import org.orekit.models.earth.weather.PressureTemperatureHumidityProvider; +import org.orekit.models.earth.weather.water.CIPM2007; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldTrackingCoordinates; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TrackingCoordinates; +import org.orekit.utils.units.Unit; +import org.orekit.utils.units.UnitsConverter; /** The Mendes - Pavlis tropospheric delay model for optical techniques. * It is valid for a wide range of wavelengths from 0.355µm to 1.064µm (Mendes and Pavlis, 2003) @@ -40,7 +49,9 @@ * * @author Bryan Cazabonne */ -public class MendesPavlisModel implements DiscreteTroposphericModel, MappingFunction { +@SuppressWarnings("deprecation") +public class MendesPavlisModel + implements DiscreteTroposphericModel, TroposphericModel, MappingFunction, TroposphereMappingFunction { /** Coefficients for the dispertion equation for the hydrostatic component [µm-2]. */ private static final double[] K_COEFFICIENTS = { @@ -62,72 +73,174 @@ public class MendesPavlisModel implements DiscreteTroposphericModel, MappingFunc /** Carbon dioxyde content (IAG recommendations). */ private static final double C02 = 0.99995995; - /** Laser wavelength [µm]. */ - private double lambda; - - /** The atmospheric pressure [hPa]. */ - private double P0; + /** Dispersion equation for the hydrostatic component. */ + private final double fLambdaH; - /** The temperature at the station [K]. */ - private double T0; + /** Dispersion equation for the non-hydrostatic component. */ + private final double fLambdaNH; - /** Water vapor pressure at the laser site [hPa]. */ - private double e0; + /** Provider for pressure, temperature and humidity. */ + private final PressureTemperatureHumidityProvider pthProvider; /** Create a new Mendes-Pavlis model for the troposphere. - * This initialisation will compute the water vapor pressure + * This initialization will compute the water vapor pressure * thanks to the values of the pressure, the temperature and the humidity * @param t0 the temperature at the station, K * @param p0 the atmospheric pressure at the station, hPa - * @param rh the humidity at the station, percent (50% → 0.5) + * @param rh the humidity at the station, as a ratio (50% → 0.5) * @param lambda laser wavelength, µm - * */ + * @deprecated as of 12.1, replaced by {@link #MendesPavlisModel(PressureTemperatureHumidityProvider, double, Unit)} + */ + @Deprecated public MendesPavlisModel(final double t0, final double p0, final double rh, final double lambda) { - this.P0 = p0; - this.T0 = t0; - this.e0 = getWaterVapor(rh); - this.lambda = lambda; + this(new ConstantPressureTemperatureHumidityProvider(new PressureTemperatureHumidity(0, + TroposphericModelUtils.HECTO_PASCAL.toSI(p0), + t0, + new CIPM2007(). + waterVaporPressure(TroposphericModelUtils.HECTO_PASCAL.toSI(p0), + t0, rh), + Double.NaN, + Double.NaN)), + lambda, TroposphericModelUtils.MICRO_M); + } + + /** Create a new Mendes-Pavlis model for the troposphere. + * @param pthProvider provider for atmospheric pressure, temperature and humidity at the station + * @param lambda laser wavelength + * @param lambdaUnits units in which {@code lambda} is given + * @see TroposphericModelUtils#MICRO_M + * @see TroposphericModelUtils#NANO_M + * @since 12.1 + * */ + public MendesPavlisModel(final PressureTemperatureHumidityProvider pthProvider, + final double lambda, final Unit lambdaUnits) { + this.pthProvider = pthProvider; + + // Dispersion equation for the hydrostatic component + final double lambdaMicrometer = new UnitsConverter(lambdaUnits, TroposphericModelUtils.MICRO_M).convert(lambda); + final double sigma = 1.0 / lambdaMicrometer; + final double sigma2 = sigma * sigma; + final double coef1 = K_COEFFICIENTS[0] + sigma2; + final double coef2 = K_COEFFICIENTS[0] - sigma2; + final double coef3 = K_COEFFICIENTS[2] + sigma2; + final double coef4 = K_COEFFICIENTS[2] - sigma2; + final double frac1 = coef1 / (coef2 * coef2); + final double frac2 = coef3 / (coef4 * coef4); + fLambdaH = 0.01 * (K_COEFFICIENTS[1] * frac1 + K_COEFFICIENTS[3] * frac2) * C02; + + // Dispersion equation for the non-hydrostatic component + final double sigma4 = sigma2 * sigma2; + final double sigma6 = sigma4 * sigma2; + final double w1s2 = 3 * W_COEFFICIENTS[1] * sigma2; + final double w2s4 = 5 * W_COEFFICIENTS[2] * sigma4; + final double w3s6 = 7 * W_COEFFICIENTS[3] * sigma6; + + fLambdaNH = 0.003101 * (W_COEFFICIENTS[0] + w1s2 + w2s4 + w3s6); + } /** Create a new Mendes-Pavlis model using a standard atmosphere model. * *
      - *
    • temperature: 18 degree Celsius - *
    • pressure: 1013.25 hPa - *
    • humidity: 50% + *
    • temperature: 18 degree Celsius
    • + *
    • pressure: 1013.25 hPa
    • + *
    • humidity: 50%
    • *
    * * @param lambda laser wavelength, µm * * @return a Mendes-Pavlis model with standard environmental values + * @deprecated as of 12.1, replaced by {@link #getStandardModel(double, Unit)} */ + @Deprecated public static MendesPavlisModel getStandardModel(final double lambda) { - return new MendesPavlisModel(273.15 + 18, 1013.25, 0.5, lambda); + return getStandardModel(lambda, TroposphericModelUtils.MICRO_M); + } + + /** Create a new Mendes-Pavlis model using a standard atmosphere model. + * + *
      + *
    • altitude: 0m
    • + *
    • temperature: 18 degree Celsius
    • + *
    • pressure: 1013.25 hPa
    • + *
    • humidity: 50%
    • + *
    + * + * @param lambda laser wavelength, µm + * @param lambdaUnits units in which {@code lambda} is given + * @return a Mendes-Pavlis model with standard environmental values + * @see TroposphericModelUtils#MICRO_M + * @see TroposphericModelUtils#NANO_M + * @since 12.1 + */ + public static MendesPavlisModel getStandardModel(final double lambda, final Unit lambdaUnits) { + final double h = 0; + final double p = TroposphericModelUtils.HECTO_PASCAL.toSI(1013.25); + final double t = 273.15 + 18; + final double rh = 0.5; + final PressureTemperatureHumidity pth = new PressureTemperatureHumidity(h, p, t, + new CIPM2007().waterVaporPressure(p, t, rh), + Double.NaN, + Double.NaN); + return new MendesPavlisModel(new ConstantPressureTemperatureHumidityProvider(pth), + lambda, lambdaUnits); } /** {@inheritDoc} */ @Override + @Deprecated public double pathDelay(final double elevation, final GeodeticPoint point, final double[] parameters, final AbsoluteDate date) { + return pathDelay(new TrackingCoordinates(0.0, elevation, 0.0), point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, parameters, date). + getDelay(); + } + + /** {@inheritDoc} */ + @Override + public TroposphericDelay pathDelay(final TrackingCoordinates trackingCoordinates, + final GeodeticPoint point, + final PressureTemperatureHumidity weather, + final double[] parameters, final AbsoluteDate date) { // Zenith delay final double[] zenithDelay = computeZenithDelay(point, parameters, date); // Mapping function - final double[] mappingFunction = mappingFactors(elevation, point, date); + final double[] mappingFunction = mappingFactors(trackingCoordinates, point, weather, date); // Tropospheric path delay - return zenithDelay[0] * mappingFunction[0] + zenithDelay[1] * mappingFunction[1]; + return new TroposphericDelay(zenithDelay[0], + zenithDelay[1], + zenithDelay[0] * mappingFunction[0], + zenithDelay[1] * mappingFunction[1]); } /** {@inheritDoc} */ @Override + @Deprecated public > T pathDelay(final T elevation, final FieldGeodeticPoint point, - final T[] parameters, final FieldAbsoluteDate date) { + final T[] parameters, final FieldAbsoluteDate date) { + return pathDelay(new FieldTrackingCoordinates<>(date.getField().getZero(), elevation, date.getField().getZero()), + point, + new FieldPressureTemperatureHumidity<>(date.getField(), TroposphericModelUtils.STANDARD_ATMOSPHERE), + parameters, date). + getDelay(); + } + + /** {@inheritDoc} */ + @Override + public > FieldTroposphericDelay pathDelay(final FieldTrackingCoordinates trackingCoordinates, + final FieldGeodeticPoint point, + final FieldPressureTemperatureHumidity weather, + final T[] parameters, final FieldAbsoluteDate date) { // Zenith delay - final T[] delays = computeZenithDelay(point, parameters, date); + final T[] zenithDelay = computeZenithDelay(point, parameters, date); // Mapping function - final T[] mappingFunction = mappingFactors(elevation, point, date); + final T[] mappingFunction = mappingFactors(trackingCoordinates, point, weather, date); // Tropospheric path delay - return delays[0].multiply(mappingFunction[0]).add(delays[1].multiply(mappingFunction[1])); + return new FieldTroposphericDelay<>(zenithDelay[0], + zenithDelay[1], + zenithDelay[0].multiply(mappingFunction[0]), + zenithDelay[1].multiply(mappingFunction[1])); } /** This method allows the computation of the zenith hydrostatic and @@ -142,38 +255,20 @@ public > T pathDelay(final T elevation, final * @return a two components array containing the zenith hydrostatic and wet delays. */ public double[] computeZenithDelay(final GeodeticPoint point, final double[] parameters, final AbsoluteDate date) { + + final PressureTemperatureHumidity pth = pthProvider.getWeatherParamerers(point, date); final double fsite = getSiteFunctionValue(point); // Array for zenith delay final double[] delay = new double[2]; - // Dispertion Equation for the Hydrostatic component - final double sigma = 1 / lambda; - final double sigma2 = sigma * sigma; - final double coef1 = K_COEFFICIENTS[0] + sigma2; - final double coef2 = K_COEFFICIENTS[0] - sigma2; - final double coef3 = K_COEFFICIENTS[2] + sigma2; - final double coef4 = K_COEFFICIENTS[2] - sigma2; - - final double frac1 = coef1 / (coef2 * coef2); - final double frac2 = coef3 / (coef4 * coef4); - - final double fLambdaH = 0.01 * (K_COEFFICIENTS[1] * frac1 + K_COEFFICIENTS[3] * frac2) * C02; - // Zenith delay for the hydrostatic component - delay[0] = 0.002416579 * (fLambdaH / fsite) * P0; - - // Dispertion Equation for the Non-Hydrostatic component - final double sigma4 = sigma2 * sigma2; - final double sigma6 = sigma4 * sigma2; - final double w1s2 = 3 * W_COEFFICIENTS[1] * sigma2; - final double w2s4 = 5 * W_COEFFICIENTS[2] * sigma4; - final double w3s6 = 7 * W_COEFFICIENTS[3] * sigma6; - - final double fLambdaNH = 0.003101 * (W_COEFFICIENTS[0] + w1s2 + w2s4 + w3s6); + // beware since version 12.1 pressure is in Pa and not in hPa, hence the scaling has changed + delay[0] = pth.getPressure() * 0.00002416579 * (fLambdaH / fsite); // Zenith delay for the non-hydrostatic component - delay[1] = 0.0001 * (5.316 * fLambdaNH - 3.759 * fLambdaH) * (e0 / fsite); + // beware since version 12.1 e0 is in Pa and not in hPa, hence the scaling has changed + delay[1] = 0.000001 * (5.316 * fLambdaNH - 3.759 * fLambdaH) * (pth.getWaterVaporPressure() / fsite); return delay; } @@ -190,45 +285,28 @@ public double[] computeZenithDelay(final GeodeticPoint point, final double[] par * @param date current date * @return a two components array containing the zenith hydrostatic and wet delays. */ - public > T[] computeZenithDelay(final FieldGeodeticPoint point, final T[] parameters, - final FieldAbsoluteDate date) { - final Field field = date.getField(); - final T zero = field.getZero(); + public > T[] computeZenithDelay(final FieldGeodeticPoint point, + final T[] parameters, + final FieldAbsoluteDate date) { + + final FieldPressureTemperatureHumidity pth = pthProvider.getWeatherParamerers(point, date); final T fsite = getSiteFunctionValue(point); // Array for zenith delay - final T[] delay = MathArrays.buildArray(field, 2); - - // Dispertion Equation for the Hydrostatic component - final T sigma = zero.add(1 / lambda); - final T sigma2 = sigma.multiply(sigma); - final T coef1 = sigma2.add(K_COEFFICIENTS[0]); - final T coef2 = sigma2.negate().add(K_COEFFICIENTS[0]); - final T coef3 = sigma2.add(K_COEFFICIENTS[2]); - final T coef4 = sigma2.negate().add(K_COEFFICIENTS[2]); - - final T frac1 = coef1.divide(coef2.multiply(coef2)); - final T frac2 = coef3.divide(coef4.multiply(coef4)); - - final T fLambdaH = frac1.multiply(K_COEFFICIENTS[1]).add(frac2.multiply(K_COEFFICIENTS[3])).multiply(0.01 * C02); + final T[] delay = MathArrays.buildArray(date.getField(), 2); // Zenith delay for the hydrostatic component - delay[0] = fLambdaH.divide(fsite).multiply(P0).multiply(0.002416579); - - // Dispertion Equation for the Non-Hydrostatic component - final T sigma4 = sigma2.multiply(sigma2); - final T sigma6 = sigma4.multiply(sigma2); - final T w1s2 = sigma2.multiply(3 * W_COEFFICIENTS[1]); - final T w2s4 = sigma4.multiply(5 * W_COEFFICIENTS[2]); - final T w3s6 = sigma6.multiply(7 * W_COEFFICIENTS[3]); - - final T fLambdaNH = w1s2.add(w2s4).add(w3s6).add(W_COEFFICIENTS[0]).multiply(0.003101); + // beware since version 12.1 pressure is in Pa and not in hPa, hence the scaling has changed + delay[0] = pth.getPressure().multiply(0.00002416579).multiply(fLambdaH).divide(fsite); // Zenith delay for the non-hydrostatic component - delay[1] = fLambdaNH.multiply(5.316).subtract(fLambdaH.multiply(3.759)).multiply(fsite.divide(e0).reciprocal()).multiply(0.0001); + // beware since version 12.1 e0 is in Pa and not in hPa, hence the scaling has changed + delay[1] = pth.getWaterVaporPressure().divide(fsite). + multiply(0.000001 * (5.316 * fLambdaNH - 3.759 * fLambdaH)); return delay; + } /** With the Mendes Pavlis tropospheric model, the mapping @@ -245,11 +323,36 @@ public > T[] computeZenithDelay(final FieldGeo * δ = (Dhz + Dwz) * m(e) = δz * m(e) */ @Override + @Deprecated public double[] mappingFactors(final double elevation, final GeodeticPoint point, final AbsoluteDate date) { - final double sinE = FastMath.sin(elevation); + return mappingFactors(new TrackingCoordinates(0.0, elevation, 0.0), point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + date); + } - final double T2degree = T0 - 273.15; + /** With the Mendes Pavlis tropospheric model, the mapping + * function is not split into hydrostatic and wet component. + *

    + * Therefore, the two components of the resulting array are equals. + *

      + *
    • double[0] = m(e) → total mapping function + *
    • double[1] = m(e) → total mapping function + *
    + *

    + * The total delay will thus be computed as:
    + * δ = Dhz * m(e) + Dwz * m(e)
    + * δ = (Dhz + Dwz) * m(e) = δz * m(e) + */ + @Override + public double[] mappingFactors(final TrackingCoordinates trackingCoordinates, + final GeodeticPoint point, + final PressureTemperatureHumidity weather, + final AbsoluteDate date) { + final double sinE = FastMath.sin(trackingCoordinates.getElevation()); + + final PressureTemperatureHumidity pth = pthProvider.getWeatherParamerers(point, date); + final double T2degree = pth.getTemperature() - 273.15; // Mapping function coefficients final double a1 = computeMFCoeffient(A_COEFFICIENTS[0][0], A_COEFFICIENTS[0][1], @@ -289,13 +392,41 @@ public double[] mappingFactors(final double elevation, final GeodeticPoint point * δ = (Dhz + Dwz) * m(e) = δz * m(e) */ @Override - public > T[] mappingFactors(final T elevation, final FieldGeodeticPoint point, - final FieldAbsoluteDate date) { + @Deprecated + public > T[] mappingFactors(final T elevation, + final FieldGeodeticPoint point, + final FieldAbsoluteDate date) { + return mappingFactors(new FieldTrackingCoordinates<>(date.getField().getZero(), elevation, date.getField().getZero()), + point, + new FieldPressureTemperatureHumidity<>(date.getField(), + TroposphericModelUtils.STANDARD_ATMOSPHERE), + date); + } + + /** With the Mendes Pavlis tropospheric model, the mapping + * function is not split into hydrostatic and wet component. + *

    + * Therefore, the two components of the resulting array are equals. + *

      + *
    • double[0] = m(e) → total mapping function + *
    • double[1] = m(e) → total mapping function + *
    + *

    + * The total delay will thus be computed as:
    + * δ = Dhz * m(e) + Dwz * m(e)
    + * δ = (Dhz + Dwz) * m(e) = δz * m(e) + */ + @Override + public > T[] mappingFactors(final FieldTrackingCoordinates trackingCoordinates, + final FieldGeodeticPoint point, + final FieldPressureTemperatureHumidity weather, + final FieldAbsoluteDate date) { final Field field = date.getField(); - final T sinE = FastMath.sin(elevation); + final T sinE = FastMath.sin(trackingCoordinates.getElevation()); - final double T2degree = T0 - 273.15; + final FieldPressureTemperatureHumidity pth = pthProvider.getWeatherParamerers(point, date); + final T T2degree = pth.getTemperature().subtract(273.15); // Mapping function coefficients final T a1 = computeMFCoeffient(A_COEFFICIENTS[0][0], A_COEFFICIENTS[0][1], @@ -328,77 +459,54 @@ public List getParametersDrivers() { return Collections.emptyList(); } - /** Get the laser frequency parameter f(lambda). - * - * @param point station location - * @return the laser frequency parameter f(lambda). - */ + /** Get the site parameter. + * + * @param point station location + * @return the site parameter. + */ private double getSiteFunctionValue(final GeodeticPoint point) { return 1. - 0.00266 * FastMath.cos(2. * point.getLatitude()) - 0.00000028 * point.getAltitude(); } - /** Get the laser frequency parameter f(lambda). - * - * @param type of the elements - * @param point station location - * @return the laser frequency parameter f(lambda). - */ + /** Get the site parameter. + * + * @param type of the elements + * @param point station location + * @return the site parameter. + */ private > T getSiteFunctionValue(final FieldGeodeticPoint point) { return FastMath.cos(point.getLatitude().multiply(2.)).multiply(0.00266).add(point.getAltitude().multiply(0.00000028)).negate().add(1.); } /** Compute the coefficients of the Mapping Function. - * - * @param T the temperature at the station site, °C - * @param a0 first coefficient - * @param a1 second coefficient - * @param a2 third coefficient - * @param a3 fourth coefficient - * @param point station location - * @return the value of the coefficient - */ + * + * @param t the temperature at the station site, °C + * @param a0 first coefficient + * @param a1 second coefficient + * @param a2 third coefficient + * @param a3 fourth coefficient + * @param point station location + * @return the value of the coefficient + */ private double computeMFCoeffient(final double a0, final double a1, final double a2, final double a3, - final double T, final GeodeticPoint point) { - return a0 + a1 * T + a2 * FastMath.cos(point.getLatitude()) + a3 * point.getAltitude(); + final double t, final GeodeticPoint point) { + return a0 + a1 * t + a2 * FastMath.cos(point.getLatitude()) + a3 * point.getAltitude(); } - /** Compute the coefficients of the Mapping Function. - * - * @param type of the elements - * @param T the temperature at the station site, °C - * @param a0 first coefficient - * @param a1 second coefficient - * @param a2 third coefficient - * @param a3 fourth coefficient - * @param point station location - * @return the value of the coefficient - */ - private > T computeMFCoeffient(final double a0, final double a1, final double a2, final double a3, - final double T, final FieldGeodeticPoint point) { - return point.getAltitude().multiply(a3).add(FastMath.cos(point.getLatitude()).multiply(a2)).add(a0 + a1 * T); - } - - /** Get the water vapor. - * The water vapor model is the one of Giacomo and Davis as indicated in IERS TN 32, chap. 9. - * - * See: Giacomo, P., Equation for the dertermination of the density of moist air, Metrologia, V. 18, 1982 + /** Compute the coefficients of the Mapping Function. * - * @param rh relative humidity, in percent (50% → 0.5). - * @return the water vapor, in mbar (1 mbar = 1 hPa). + * @param type of the elements + * @param t the temperature at the station site, °C + * @param a0 first coefficient + * @param a1 second coefficient + * @param a2 third coefficient + * @param a3 fourth coefficient + * @param point station location + * @return the value of the coefficient */ - private double getWaterVapor(final double rh) { - - // saturation water vapor, equation (3) of reference paper, in mbar - // with amended 1991 values (see reference paper) - final double es = 0.01 * FastMath.exp((1.2378847 * 1e-5) * T0 * T0 - - (1.9121316 * 1e-2) * T0 + - 33.93711047 - - (6.3431645 * 1e3) * 1. / T0); - - // enhancement factor, equation (4) of reference paper - final double fw = 1.00062 + (3.14 * 1e-6) * P0 + (5.6 * 1e-7) * FastMath.pow(T0 - 273.15, 2); - - final double e = rh * fw * es; - return e; + private > T computeMFCoeffient(final double a0, final double a1, final double a2, final double a3, + final T t, final FieldGeodeticPoint point) { + return point.getAltitude().multiply(a3).add(FastMath.cos(point.getLatitude()).multiply(a2)).add(t.multiply(a1).add(a0)); } + } diff --git a/src/main/java/org/orekit/models/earth/troposphere/ModifiedHopfieldModel.java b/src/main/java/org/orekit/models/earth/troposphere/ModifiedHopfieldModel.java new file mode 100644 index 0000000000..306e199f6d --- /dev/null +++ b/src/main/java/org/orekit/models/earth/troposphere/ModifiedHopfieldModel.java @@ -0,0 +1,242 @@ +/* Copyright 2002-2024 Thales Alenia Space + * Licensed to CS Communication & Systèmes (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.models.earth.troposphere; + +import java.util.Collections; +import java.util.List; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.FieldSinCos; +import org.hipparchus.util.MathUtils; +import org.hipparchus.util.SinCos; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; +import org.orekit.models.earth.weather.PressureTemperatureHumidity; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldTrackingCoordinates; +import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TrackingCoordinates; + +/** The modified Hopfield model. + *

    + * This model from Hopfield 1969, 1970, 1972 is described in equations + * 5.105, 5.106, 5.107 and 5.108 in Guochang Xu, GPS - Theory, Algorithms + * and Applications, Springer, 2007. + *

    + * @author Luc Maisonobe + * @see "Guochang Xu, GPS - Theory, Algorithms and Applications, Springer, 2007" + * @since 12.1 + */ +public class ModifiedHopfieldModel implements TroposphericModel { + + /** Constant for dry altitude effect. */ + private static final double HD0 = 40136.0; + + /** Slope for dry altitude effect. */ + private static final double HD1 = 148.72; + + /** Temperature reference. */ + private static final double T0 = 273.16; + + /** Constant for wet altitude effect. */ + private static final double HW0 = 11000.0; + + /** Dry delay factor. */ + private static final double ND = 77.64e-6; + + /** Wet delay factor, degree 1. */ + private static final double NW1 = -12.96e-6; + + /** Wet delay factor, degree 2. */ + private static final double NW2 = 0.371800; + + /** BAse radius. */ + private static final double RE = 6378137.0; + + /** Create a new Hopfield model. + */ + public ModifiedHopfieldModel() { + // nothing to do + } + + /** {@inheritDoc} */ + @Override + public TroposphericDelay pathDelay(final TrackingCoordinates trackingCoordinates, + final GeodeticPoint point, + final PressureTemperatureHumidity weather, + final double[] parameters, final AbsoluteDate date) { + + // zenith angle + final double zenithAngle = MathUtils.SEMI_PI - trackingCoordinates.getElevation(); + + // dry component + final double hd = HD0 + HD1 * (weather.getTemperature() - T0); + final double nd = ND * TroposphericModelUtils.HECTO_PASCAL.fromSI(weather.getPressure()) / + weather.getTemperature(); + + // wet component + final double hw = HW0; + final double nw = (NW1 + NW2 / weather.getTemperature()) / weather.getTemperature(); + + return new TroposphericDelay(delay(0.0, hd, nd), + delay(0.0, hw, nw), + delay(zenithAngle, hd, nd), + delay(zenithAngle, hw, nw)); + + } + + /** {@inheritDoc} + *

    + * The Saastamoinen model is not defined for altitudes below 0.0. for continuity + * reasons, we use the value for h = 0 when altitude is negative. + *

    + *

    + * There are also numerical issues for elevation angles close to zero. For continuity reasons, + * elevations lower than a threshold will use the value obtained + * for the threshold itself. + *

    + */ + @Override + public > FieldTroposphericDelay pathDelay(final FieldTrackingCoordinates trackingCoordinates, + final FieldGeodeticPoint point, + final FieldPressureTemperatureHumidity weather, + final T[] parameters, final FieldAbsoluteDate date) { + + // zenith angle + final T zenithAngle = trackingCoordinates.getElevation().negate().add(MathUtils.SEMI_PI); + + // dry component + final T hd = weather.getTemperature().subtract(T0).multiply(HD1).add(HD0); + final T nd = TroposphericModelUtils.HECTO_PASCAL.fromSI(weather.getPressure()). + multiply(ND). + divide(weather.getTemperature()); + + // wet component + final T hw = date.getField().getZero().newInstance(HW0); + final T nw = weather.getTemperature().reciprocal().multiply(NW2).add(NW1).divide(weather.getTemperature()); + + return new FieldTroposphericDelay<>(delay(date.getField().getZero(), hd, nd), + delay(date.getField().getZero(), hw, nw), + delay(zenithAngle, hd, nd), + delay(zenithAngle, hw, nw)); + + } + + /** {@inheritDoc} */ + @Override + public List getParametersDrivers() { + return Collections.emptyList(); + } + + /** Compute the 9 terms sum delay. + * @param zenithAngle zenith angle + * @param hi altitude effect + * @param ni delay factor + * @return 9 terms sum delay + */ + private double delay(final double zenithAngle, final double hi, final double ni) { + + // equation 5.107 + final SinCos scZ = FastMath.sinCos(zenithAngle); + final double rePhi = RE + hi; + final double reS = RE * scZ.sin(); + final double reC = RE * scZ.cos(); + final double ri = FastMath.sqrt(rePhi * rePhi - reS * reS) - reC; + + final double ai = -scZ.cos() / hi; + final double bi = -scZ.sin() * scZ.sin() / (2 * hi * RE); + final double ai2 = ai * ai; + final double bi2 = bi * bi; + + final double f1i = 1; + final double f2i = 4 * ai; + final double f3i = 6 * ai2 + 4 * bi; + final double f4i = 4 * ai * (ai2 + 3 * bi); + final double f5i = ai2 * ai2 + 12 * ai2 * bi + 6 * bi2; + final double f6i = 4 * ai * bi * (ai2 + 3 * bi); + final double f7i = bi2 * (6 * ai2 + 4 * bi); + final double f8i = 4 * ai * bi * bi2; + final double f9i = bi2 * bi2; + + return ni * (ri * (f1i + + ri * (f2i / 2 + + ri * (f3i / 3 + + ri * (f4i / 4 + + ri * (f5i / 5 + + ri * (f6i / 6 + + ri * (f7i / 7 + + ri * (f8i / 8 + + ri * f9i / 9))))))))); + + } + + /** Compute the 9 terms sum delay. + * @param type of the elements + * @param zenithAngle zenith angle + * @param hi altitude effect + * @param ni delay factor + * @return 9 terms sum delay + */ + private > T delay(final T zenithAngle, final T hi, final T ni) { + + // equation 5.107 + final FieldSinCos scZ = FastMath.sinCos(zenithAngle); + final T rePhi = hi.add(RE); + final T reS = scZ.sin().multiply(RE); + final T reC = scZ.cos().multiply(RE); + final T ri = FastMath.sqrt(rePhi.multiply(rePhi).subtract(reS.multiply(reS))).subtract(reC); + + final T ai = scZ.cos().negate().divide(hi); + final T bi = scZ.sin().multiply(scZ.sin()).negate().divide(hi.add(hi).multiply(RE)); + final T ai2 = ai.multiply(ai); + final T bi2 = bi.multiply(bi); + + final T f1i = ai.getField().getOne(); + final T f2i = ai.multiply(4); + final T f3i = ai2.multiply(6).add(bi.multiply(4)); + final T f4i = ai.multiply(4).multiply(ai2.add(bi.multiply(3))); + final T f5i = ai2.multiply(ai2).add(ai2.multiply(12).multiply(bi)).add(bi2.multiply(6)); + final T f6i = ai.multiply(4).multiply(bi).multiply(ai2.add(bi.multiply(3))); + final T f7i = bi2.multiply(ai2.multiply(6).add(bi.multiply(4))); + final T f8i = ai.multiply(4).multiply(bi).multiply(bi2); + final T f9i = bi2.multiply(bi2); + + return ni. + multiply(ri. + multiply(f1i. + add(ri. + multiply(f2i.divide(2). + add(ri. + multiply(f3i.divide(3). + add(ri. + multiply(f4i.divide(4). + add(ri. + multiply(f5i.divide(5). + add(ri. + multiply(f6i.divide(6). + add(ri. + multiply(f7i.divide(7). + add(ri. + multiply(f8i.divide(8). + add(ri.multiply(f9i.divide(9))))))))))))))))))); + + } + +} diff --git a/src/main/java/org/orekit/models/earth/troposphere/ModifiedSaastamoinenModel.java b/src/main/java/org/orekit/models/earth/troposphere/ModifiedSaastamoinenModel.java index ab03d5ac66..64f5139a56 100644 --- a/src/main/java/org/orekit/models/earth/troposphere/ModifiedSaastamoinenModel.java +++ b/src/main/java/org/orekit/models/earth/troposphere/ModifiedSaastamoinenModel.java @@ -24,7 +24,6 @@ import org.hipparchus.Field; import org.hipparchus.analysis.interpolation.BilinearInterpolatingFunction; import org.hipparchus.analysis.interpolation.LinearInterpolator; -import org.hipparchus.analysis.polynomials.PolynomialFunction; import org.hipparchus.analysis.polynomials.PolynomialSplineFunction; import org.hipparchus.util.FastMath; import org.orekit.annotation.DefaultDataContext; @@ -34,16 +33,23 @@ import org.orekit.data.DataProvidersManager; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; +import org.orekit.models.earth.weather.ConstantPressureTemperatureHumidityProvider; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; +import org.orekit.models.earth.weather.HeightDependentPressureTemperatureHumidityConverter; +import org.orekit.models.earth.weather.PressureTemperatureHumidity; +import org.orekit.models.earth.weather.PressureTemperatureHumidityProvider; +import org.orekit.models.earth.weather.water.Wang1988; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldTrackingCoordinates; import org.orekit.utils.InterpolationTableLoader; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TrackingCoordinates; /** The modified Saastamoinen model. Estimates the path delay imposed to * electro-magnetic signals by the troposphere according to the formula: *
    - * δ = 2.277e-3 / cos z * (P + (1255 / T + 0.05) * e - B * tan²
    - * z) + δR
    + * δ = 2.277e-3 / cos z * (P + (1255 / T + 0.05) * e - B * tan² z) + δR
      * 
    * with the following input data provided to the model: *
      @@ -60,8 +66,9 @@ *

      * @author Thomas Neidhart * @see "Guochang Xu, GPS - Theory, Algorithms and Applications, Springer, 2007" + * @since 12.1 */ -public class ModifiedSaastamoinenModel implements DiscreteTroposphericModel { +public class ModifiedSaastamoinenModel implements TroposphericModel { /** Default file name for δR correction term table. */ public static final String DELTA_R_FILE_NAME = "^saastamoinen-correction\\.txt$"; @@ -69,44 +76,49 @@ public class ModifiedSaastamoinenModel implements DiscreteTroposphericModel { /** Default lowest acceptable elevation angle [rad]. */ public static final double DEFAULT_LOW_ELEVATION_THRESHOLD = 0.05; + /** Provider for water pressure. */ + public static final Wang1988 WATER = new Wang1988(); + /** First pattern for δR correction term table. */ private static final Pattern FIRST_DELTA_R_PATTERN = Pattern.compile("^\\^"); /** Second pattern for δR correction term table. */ private static final Pattern SECOND_DELTA_R_PATTERN = Pattern.compile("\\$$"); + /** Base delay coefficient. */ + private static final double L0 = 2.277e-5; + + /** Temperature numerator. */ + private static final double T_NUM = 1255; + + /** Wet offset. */ + private static final double WET_OFFSET = 0.05; + /** X values for the B function. */ private static final double[] X_VALUES_FOR_B = { - 0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 4.0, 5.0 + 0.0, 500.0, 1000.0, 1500.0, 2000.0, 2500.0, 3000.0, 4000.0, 5000.0 }; - /** E values for the B function. */ + /** Y values for the B function. + *

      + * The values have been scaled up by a factor 100.0 due to conversion from hPa to Pa. + *

      + */ private static final double[] Y_VALUES_FOR_B = { - 1.156, 1.079, 1.006, 0.938, 0.874, 0.813, 0.757, 0.654, 0.563 - }; - - /** Coefficients for the partial pressure of water vapor polynomial. */ - private static final double[] E_COEFFICIENTS = { - -37.2465, 0.213166, -0.000256908 + 115.6, 107.9, 100.6, 93.8, 87.4, 81.3, 75.7, 65.4, 56.3 }; /** Interpolation function for the B correction term. */ - private final PolynomialSplineFunction bFunction; - - /** Polynomial function for the e term. */ - private final PolynomialFunction eFunction; + private static final PolynomialSplineFunction B_FUNCTION = new LinearInterpolator().interpolate(X_VALUES_FOR_B, Y_VALUES_FOR_B); /** Interpolation function for the delta R correction term. */ private final BilinearInterpolatingFunction deltaRFunction; - /** The temperature at the station [K]. */ - private double t0; - - /** The atmospheric pressure [mbar]. */ - private double p0; + /** Provider for atmospheric pressure, temperature and humidity at reference altitude. */ + private final PressureTemperatureHumidityProvider pth0Provider; - /** The humidity [percent]. */ - private double r0; + /** Height dependent converter for pressure, temperature and humidity. */ + private final HeightDependentPressureTemperatureHumidityConverter converter; /** Lowest acceptable elevation angle [rad]. */ private double lowElevationThreshold; @@ -115,55 +127,45 @@ public class ModifiedSaastamoinenModel implements DiscreteTroposphericModel { * Create a new Saastamoinen model for the troposphere using the given environmental * conditions and table from the reference book. * - * @param t0 the temperature at the station [K] - * @param p0 the atmospheric pressure at the station [mbar] - * @param r0 the humidity at the station [fraction] (50% -> 0.5) - * @see #ModifiedSaastamoinenModel(double, double, double, String, DataProvidersManager) - * @since 10.1 + * @param pth0Provider provider for atmospheric pressure, temperature and humidity at reference altitude + * @see #ModifiedSaastamoinenModel(PressureTemperatureHumidityProvider, String, DataProvidersManager) */ - public ModifiedSaastamoinenModel(final double t0, final double p0, final double r0) { - this(t0, p0, r0, defaultDeltaR()); + @DefaultDataContext + public ModifiedSaastamoinenModel(final PressureTemperatureHumidityProvider pth0Provider) { + this(pth0Provider, defaultDeltaR()); } /** Create a new Saastamoinen model for the troposphere using the given * environmental conditions. This constructor uses the {@link DataContext#getDefault() * default data context} if {@code deltaRFileName != null}. * - * @param t0 the temperature at the station [K] - * @param p0 the atmospheric pressure at the station [mbar] - * @param r0 the humidity at the station [fraction] (50% -> 0.5) + * @param pth0Provider provider for atmospheric pressure, temperature and humidity at reference altitude * @param deltaRFileName regular expression for filename containing δR * correction term table (typically {@link #DELTA_R_FILE_NAME}), if null * default values from the reference book are used - * @since 7.1 - * @see #ModifiedSaastamoinenModel(double, double, double, String, DataProvidersManager) + * @see #ModifiedSaastamoinenModel(PressureTemperatureHumidityProvider, String, DataProvidersManager) */ @DefaultDataContext - public ModifiedSaastamoinenModel(final double t0, final double p0, final double r0, - final String deltaRFileName) { - this(t0, p0, r0, deltaRFileName, - DataContext.getDefault().getDataProvidersManager()); + public ModifiedSaastamoinenModel(final PressureTemperatureHumidityProvider pth0Provider, + final String deltaRFileName) { + this(pth0Provider, deltaRFileName, + DataContext.getDefault().getDataProvidersManager()); } /** Create a new Saastamoinen model for the troposphere using the given * environmental conditions. This constructor allows the user to specify the source of * of the δR file. * - * @param t0 the temperature at the station [K] - * @param p0 the atmospheric pressure at the station [mbar] - * @param r0 the humidity at the station [fraction] (50% -> 0.5) + * @param pth0Provider provider for atmospheric pressure, temperature and humidity at reference altitude * @param deltaRFileName regular expression for filename containing δR * correction term table (typically {@link #DELTA_R_FILE_NAME}), if null * default values from the reference book are used * @param dataProvidersManager provides access to auxiliary data. - * @since 10.1 */ - public ModifiedSaastamoinenModel(final double t0, - final double p0, - final double r0, - final String deltaRFileName, - final DataProvidersManager dataProvidersManager) { - this(t0, p0, r0, + public ModifiedSaastamoinenModel(final PressureTemperatureHumidityProvider pth0Provider, + final String deltaRFileName, + final DataProvidersManager dataProvidersManager) { + this(pth0Provider, deltaRFileName == null ? defaultDeltaR() : loadDeltaR(deltaRFileName, dataProvidersManager)); @@ -171,58 +173,52 @@ public ModifiedSaastamoinenModel(final double t0, /** Create a new Saastamoinen model. * - * @param t0 the temperature at the station [K] - * @param p0 the atmospheric pressure at the station [mbar] - * @param r0 the humidity at the station [fraction] (50% -> 0.5) + * @param pth0Provider provider for atmospheric pressure, temperature and humidity at reference altitude * @param deltaR δR correction term function - * @since 7.1 */ - private ModifiedSaastamoinenModel(final double t0, final double p0, final double r0, - final BilinearInterpolatingFunction deltaR) { - checkParameterRangeInclusive("humidity", r0, 0.0, 1.0); - this.t0 = t0; - this.p0 = p0; - this.r0 = r0; - this.bFunction = new LinearInterpolator().interpolate(X_VALUES_FOR_B, Y_VALUES_FOR_B); - this.eFunction = new PolynomialFunction(E_COEFFICIENTS); - this.deltaRFunction = deltaR; + private ModifiedSaastamoinenModel(final PressureTemperatureHumidityProvider pth0Provider, + final BilinearInterpolatingFunction deltaR) { + this.pth0Provider = pth0Provider; + this.converter = new HeightDependentPressureTemperatureHumidityConverter(WATER); + this.deltaRFunction = deltaR; this.lowElevationThreshold = DEFAULT_LOW_ELEVATION_THRESHOLD; } /** Create a new Saastamoinen model using a standard atmosphere model. * *
        - *
      • temperature: 18 degree Celsius - *
      • pressure: 1013.25 mbar - *
      • humidity: 50% + *
      • altitude: 0m
      • + *
      • temperature: 18 degree Celsius
      • + *
      • pressure: 1013.25 mbar
      • + *
      • humidity: 50%
      • + *
      • @link {@link Wang1988 Wang 1988} model to compute water vapor pressure
      • *
      * * @return a Saastamoinen model with standard environmental values */ + @DefaultDataContext public static ModifiedSaastamoinenModel getStandardModel() { - return new ModifiedSaastamoinenModel(273.16 + 18, 1013.25, 0.5); + final double altitude = 0; + final double pressure = TroposphericModelUtils.HECTO_PASCAL.toSI(1013.25); + final double temperature = 273.15 + 18; + final double humidity = 0.5; + final PressureTemperatureHumidity pth = new PressureTemperatureHumidity(altitude, + pressure, + temperature, + WATER.waterVaporPressure(pressure, + temperature, + humidity), + Double.NaN, + Double.NaN); + final PressureTemperatureHumidityProvider pth0Provider = new ConstantPressureTemperatureHumidityProvider(pth); + return new ModifiedSaastamoinenModel(pth0Provider); } - /** Check if the given parameter is within an acceptable range. - * The bounds are inclusive: an exception is raised when either of those conditions are met: - *
        - *
      • The parameter is strictly greater than upperBound
      • - *
      • The parameter is strictly lower than lowerBound
      • - *
      - *

      - * In either of these cases, an OrekitException is raised. - *

      - * @param parameterName name of the parameter - * @param parameter value of the parameter - * @param lowerBound lower bound of the acceptable range (inclusive) - * @param upperBound upper bound of the acceptable range (inclusive) + /** Get provider for atmospheric pressure, temperature and humidity at reference altitude. + * @return provider for atmospheric pressure, temperature and humidity at reference altitude */ - private void checkParameterRangeInclusive(final String parameterName, final double parameter, - final double lowerBound, final double upperBound) { - if (parameter < lowerBound || parameter > upperBound) { - throw new OrekitException(OrekitMessages.INVALID_PARAMETER_RANGE, parameterName, - parameter, lowerBound, upperBound); - } + public PressureTemperatureHumidityProvider getPth0Provider() { + return pth0Provider; } /** {@inheritDoc} @@ -239,37 +235,37 @@ private void checkParameterRangeInclusive(final String parameterName, final doub * @see #setLowElevationThreshold(double) */ @Override - public double pathDelay(final double elevation, final GeodeticPoint point, - final double[] parameters, final AbsoluteDate date) { + public TroposphericDelay pathDelay(final TrackingCoordinates trackingCoordinates, + final GeodeticPoint point, + final PressureTemperatureHumidity weather, + final double[] parameters, final AbsoluteDate date) { - // there are no data in the model for negative altitudes and altitude bigger than 5000 m - // limit the height to a range of [0, 5000] m - final double fixedHeight = FastMath.min(FastMath.max(0, point.getAltitude()), 5000); + // limit the height to model range + final double fixedHeight = FastMath.min(FastMath.max(point.getAltitude(), X_VALUES_FOR_B[0]), + X_VALUES_FOR_B[X_VALUES_FOR_B.length - 1]); - // the corrected temperature using a temperature gradient of -6.5 K/km - final double T = t0 - 6.5e-3 * fixedHeight; - // the corrected pressure - final double P = p0 * FastMath.pow(1.0 - 2.26e-5 * fixedHeight, 5.225); - // the corrected humidity - final double R = r0 * FastMath.exp(-6.396e-4 * fixedHeight); + final PressureTemperatureHumidity pth = converter.convert(weather, fixedHeight); // interpolate the b correction term - final double B = bFunction.value(fixedHeight / 1e3); - // calculate e - final double e = R * FastMath.exp(eFunction.value(T)); + final double B = B_FUNCTION.value(fixedHeight); // calculate the zenith angle from the elevation - final double z = FastMath.abs(0.5 * FastMath.PI - FastMath.max(elevation, lowElevationThreshold)); + final double z = FastMath.abs(0.5 * FastMath.PI - + FastMath.max(trackingCoordinates.getElevation(), lowElevationThreshold)); // get correction factor final double deltaR = getDeltaR(fixedHeight, z); // calculate the path delay in m - final double tan = FastMath.tan(z); - final double delta = 2.277e-3 / FastMath.cos(z) * - (P + (1255d / T + 5e-2) * e - B * tan * tan) + deltaR; + // beware since version 12.1 pressures are in Pa and not in hPa, hence the scaling has changed + final double invCos = 1.0 / FastMath.cos(z); + final double tan = FastMath.tan(z); + final double zh = L0 * pth.getPressure(); + final double zw = L0 * (T_NUM / pth.getTemperature() + WET_OFFSET) * pth.getWaterVaporPressure(); + final double sh = zh * invCos; + final double sw = (zw - L0 * B * tan * tan) * invCos + deltaR; + return new TroposphericDelay(zh, zw, sh, sw); - return delta; } /** {@inheritDoc} @@ -286,39 +282,42 @@ public double pathDelay(final double elevation, final GeodeticPoint point, * @see #setLowElevationThreshold(double) */ @Override - public > T pathDelay(final T elevation, final FieldGeodeticPoint point, - final T[] parameters, final FieldAbsoluteDate date) { + public > FieldTroposphericDelay pathDelay(final FieldTrackingCoordinates trackingCoordinates, + final FieldGeodeticPoint point, + final FieldPressureTemperatureHumidity weather, + final T[] parameters, final FieldAbsoluteDate date) { + + // limit the height to model range + final T fixedHeight = FastMath.min(FastMath.max(point.getAltitude(), X_VALUES_FOR_B[0]), + X_VALUES_FOR_B[X_VALUES_FOR_B.length - 1]); + + final FieldPressureTemperatureHumidity pth = converter.convert(weather, fixedHeight); final Field field = date.getField(); final T zero = field.getZero(); - // there are no data in the model for negative altitudes and altitude bigger than 5000 m - // limit the height to a range of [0, 5000] m - final T fixedHeight = FastMath.min(FastMath.max(zero, point.getAltitude()), zero.add(5000)); - - // the corrected temperature using a temperature gradient of -6.5 K/km - final T T = fixedHeight.multiply(6.5e-3).negate().add(t0); - // the corrected pressure - final T P = fixedHeight.multiply(2.26e-5).negate().add(1.0).pow(5.225).multiply(p0); - // the corrected humidity - final T R = FastMath.exp(fixedHeight.multiply(-6.396e-4)).multiply(r0); // interpolate the b correction term - final T B = bFunction.value(fixedHeight.divide(1e3)); - // calculate e - final T e = R.multiply(FastMath.exp(eFunction.value(T))); + final T B = B_FUNCTION.value(fixedHeight); // calculate the zenith angle from the elevation - final T z = FastMath.abs(FastMath.max(elevation, zero.add(lowElevationThreshold)).negate().add(zero.getPi().multiply(0.5))); + final T z = FastMath.abs(FastMath.max(trackingCoordinates.getElevation(), + zero.newInstance(lowElevationThreshold)).negate(). + add(zero.getPi().multiply(0.5))); // get correction factor final T deltaR = getDeltaR(fixedHeight, z, field); // calculate the path delay in m - final T tan = FastMath.tan(z); - final T delta = FastMath.cos(z).divide(2.277e-3).reciprocal(). - multiply(P.add(T.divide(1255d).reciprocal().add(5e-2).multiply(e)).subtract(B.multiply(tan).multiply(tan))).add(deltaR); + // beware since version 12.1 pressures are in Pa and not in hPa, hence the scaling has changed + final T invCos = FastMath.cos(z).reciprocal(); + final T tan = FastMath.tan(z); + final T zh = pth.getPressure().multiply(L0); + final T zw = pth.getTemperature().reciprocal().multiply(T_NUM).add(WET_OFFSET). + multiply(pth.getWaterVaporPressure()).multiply(L0); + final T sh = zh.multiply(invCos); + final T sw = zw.subtract(B.multiply(tan).multiply(tan).multiply(L0)).multiply(invCos).add(deltaR); + return new FieldTroposphericDelay<>(zh, zw, sh, sw); - return delta; } /** Calculates the delta R correction term using linear interpolation. @@ -435,8 +434,8 @@ public List getParametersDrivers() { /** Get the low elevation threshold value for path delay computation. * @return low elevation threshold, in rad. - * @see #pathDelay(double, GeodeticPoint, double[], AbsoluteDate) - * @see #pathDelay(CalculusFieldElement, FieldGeodeticPoint, CalculusFieldElement[], FieldAbsoluteDate) + * @see #pathDelay(TrackingCoordinates, GeodeticPoint, PressureTemperatureHumidity, double[], AbsoluteDate) + * @see #pathDelay(FieldTrackingCoordinates, FieldGeodeticPoint, FieldPressureTemperatureHumidity, CalculusFieldElement[], FieldAbsoluteDate) * @since 10.2 */ public double getLowElevationThreshold() { @@ -445,8 +444,8 @@ public double getLowElevationThreshold() { /** Set the low elevation threshold value for path delay computation. * @param lowElevationThreshold The new value for the threshold [rad] - * @see #pathDelay(double, GeodeticPoint, double[], AbsoluteDate) - * @see #pathDelay(CalculusFieldElement, FieldGeodeticPoint, CalculusFieldElement[], FieldAbsoluteDate) + * @see #pathDelay(TrackingCoordinates, GeodeticPoint, PressureTemperatureHumidity, double[], AbsoluteDate) + * @see #pathDelay(FieldTrackingCoordinates, FieldGeodeticPoint, FieldPressureTemperatureHumidity, CalculusFieldElement[], FieldAbsoluteDate) * @since 10.2 */ public void setLowElevationThreshold(final double lowElevationThreshold) { diff --git a/src/main/java/org/orekit/models/earth/troposphere/NiellMappingFunctionModel.java b/src/main/java/org/orekit/models/earth/troposphere/NiellMappingFunctionModel.java index ce140c50db..14c1da3a14 100644 --- a/src/main/java/org/orekit/models/earth/troposphere/NiellMappingFunctionModel.java +++ b/src/main/java/org/orekit/models/earth/troposphere/NiellMappingFunctionModel.java @@ -26,10 +26,14 @@ import org.orekit.bodies.FieldGeodeticPoint; import org.orekit.bodies.GeodeticPoint; import org.orekit.data.DataContext; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; +import org.orekit.models.earth.weather.PressureTemperatureHumidity; import org.orekit.time.AbsoluteDate; import org.orekit.time.DateTimeComponents; import org.orekit.time.FieldAbsoluteDate; import org.orekit.time.TimeScale; +import org.orekit.utils.FieldTrackingCoordinates; +import org.orekit.utils.TrackingCoordinates; /** The Niell Mapping Function model for radio wavelengths. * This model is an empirical mapping function. It only needs the @@ -45,7 +49,8 @@ * @author Bryan Cazabonne * */ -public class NiellMappingFunctionModel implements MappingFunction { +@SuppressWarnings("deprecation") +public class NiellMappingFunctionModel implements MappingFunction, TroposphereMappingFunction { /** Values for the ah average function. */ private static final double[] VALUES_FOR_AH_AVERAGE = { @@ -160,8 +165,20 @@ public NiellMappingFunctionModel(final TimeScale utc) { /** {@inheritDoc} */ @Override + @Deprecated public double[] mappingFactors(final double elevation, final GeodeticPoint point, final AbsoluteDate date) { + return mappingFactors(new TrackingCoordinates(0.0, elevation, 0.0), + point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + date); + } + + /** {@inheritDoc} */ + @Override + public double[] mappingFactors(final TrackingCoordinates trackingCoordinates, final GeodeticPoint point, + final PressureTemperatureHumidity weather, + final AbsoluteDate date) { // Day of year computation final DateTimeComponents dtc = date.getComponents(utc); final int dofyear = dtc.getDate().getDayOfYear(); @@ -188,13 +205,17 @@ public double[] mappingFactors(final double elevation, final GeodeticPoint point final double[] function = new double[2]; // Hydrostatic mapping factor - function[0] = TroposphericModelUtils.mappingFunction(ah, bh, ch, elevation); + function[0] = TroposphericModelUtils.mappingFunction(ah, bh, ch, trackingCoordinates.getElevation()); // Wet mapping factor - function[1] = TroposphericModelUtils.mappingFunction(awFunction.value(absLatidude), bwFunction.value(absLatidude), cwFunction.value(absLatidude), elevation); + function[1] = TroposphericModelUtils.mappingFunction(awFunction.value(absLatidude), + bwFunction.value(absLatidude), + cwFunction.value(absLatidude), + trackingCoordinates.getElevation()); // Apply height correction - final double correction = TroposphericModelUtils.computeHeightCorrection(elevation, point.getAltitude()); + final double correction = TroposphericModelUtils.computeHeightCorrection(trackingCoordinates.getElevation(), + point.getAltitude()); function[0] = function[0] + correction; return function; @@ -202,8 +223,22 @@ public double[] mappingFactors(final double elevation, final GeodeticPoint point /** {@inheritDoc} */ @Override + @Deprecated public > T[] mappingFactors(final T elevation, final FieldGeodeticPoint point, - final FieldAbsoluteDate date) { + final FieldAbsoluteDate date) { + return mappingFactors(new FieldTrackingCoordinates<>(date.getField().getZero(), elevation, date.getField().getZero()), + point, + new FieldPressureTemperatureHumidity<>(date.getField(), + TroposphericModelUtils.STANDARD_ATMOSPHERE), + date); + } + + /** {@inheritDoc} */ + @Override + public > T[] mappingFactors(final FieldTrackingCoordinates trackingCoordinates, + final FieldGeodeticPoint point, + final FieldPressureTemperatureHumidity weather, + final FieldAbsoluteDate date) { final Field field = date.getField(); final T zero = field.getZero(); @@ -233,14 +268,17 @@ public > T[] mappingFactors(final T elevation, final T[] function = MathArrays.buildArray(field, 2); // Hydrostatic mapping factor - function[0] = TroposphericModelUtils.mappingFunction(ah, bh, ch, elevation); + function[0] = TroposphericModelUtils.mappingFunction(ah, bh, ch, + trackingCoordinates.getElevation()); // Wet mapping factor - function[1] = TroposphericModelUtils.mappingFunction(zero.add(awFunction.value(absLatidude)), zero.add(bwFunction.value(absLatidude)), - zero.add(cwFunction.value(absLatidude)), elevation); + function[1] = TroposphericModelUtils.mappingFunction(zero.newInstance(awFunction.value(absLatidude)), zero.newInstance(bwFunction.value(absLatidude)), + zero.newInstance(cwFunction.value(absLatidude)), trackingCoordinates.getElevation()); // Apply height correction - final T correction = TroposphericModelUtils.computeHeightCorrection(elevation, point.getAltitude(), field); + final T correction = TroposphericModelUtils.computeHeightCorrection(trackingCoordinates.getElevation(), + point.getAltitude(), + field); function[0] = function[0].add(correction); return function; diff --git a/src/main/java/org/orekit/models/earth/troposphere/RevisedChaoMappingFunction.java b/src/main/java/org/orekit/models/earth/troposphere/RevisedChaoMappingFunction.java new file mode 100644 index 0000000000..1f9c7c25b1 --- /dev/null +++ b/src/main/java/org/orekit/models/earth/troposphere/RevisedChaoMappingFunction.java @@ -0,0 +1,48 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.troposphere; + +/** Chao mapping function for radio wavelengths. + *

      + * The mapping function is described in A. Estefan, O. J. Sovers 1994 paper + * "A Comparative Survey of Current and Proposed Tropospheric Refraction-Delay + * Models for DSN Radio Metric Data Calibration" + *

      + * @author Luc Maisonobe + * @since 12.1 + */ +public class RevisedChaoMappingFunction extends AbstractChaoMappingFunction { + + /** First coefficient for hydrostatic (dry) component. */ + private static final double AD = 0.00147; + + /** Second coefficient for hydrostatic (dry) component. */ + private static final double BD = 0.0400; + + /** First coefficient for wet component. */ + private static final double AW = 0.00035; + + /** Second coefficient for wet component. */ + private static final double BW = 0.017; + + /** Builds a new instance. + */ + public RevisedChaoMappingFunction() { + super(AD, BD, AW, BW); + } + +} diff --git a/src/main/java/org/orekit/models/earth/troposphere/SaastamoinenModel.java b/src/main/java/org/orekit/models/earth/troposphere/SaastamoinenModel.java index 0a26a50172..18e61e236d 100644 --- a/src/main/java/org/orekit/models/earth/troposphere/SaastamoinenModel.java +++ b/src/main/java/org/orekit/models/earth/troposphere/SaastamoinenModel.java @@ -1,4 +1,4 @@ -/* Copyright 2023 Thales Alenia Space +/* Copyright 2002-2024 Thales Alenia Space * Licensed to CS Communication & Systèmes (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,16 +16,27 @@ */ package org.orekit.models.earth.troposphere; +import org.hipparchus.CalculusFieldElement; import org.orekit.annotation.DefaultDataContext; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.GeodeticPoint; import org.orekit.data.DataContext; import org.orekit.data.DataProvidersManager; +import org.orekit.models.earth.weather.ConstantPressureTemperatureHumidityProvider; +import org.orekit.models.earth.weather.PressureTemperatureHumidity; +import org.orekit.models.earth.weather.PressureTemperatureHumidityProvider; +import org.orekit.models.earth.weather.water.Wang1988; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldTrackingCoordinates; +import org.orekit.utils.TrackingCoordinates; /** The modified Saastamoinen model. * @author Luc Maisonobe * @deprecated as of 12.1, replaced by {@link ModifiedSaastamoinenModel} */ @Deprecated -public class SaastamoinenModel extends ModifiedSaastamoinenModel { +public class SaastamoinenModel extends ModifiedSaastamoinenModel implements DiscreteTroposphericModel { /** Default file name for δR correction term table. */ public static final String DELTA_R_FILE_NAME = ModifiedSaastamoinenModel.DELTA_R_FILE_NAME; @@ -39,12 +50,13 @@ public class SaastamoinenModel extends ModifiedSaastamoinenModel { * * @param t0 the temperature at the station [K] * @param p0 the atmospheric pressure at the station [mbar] - * @param r0 the humidity at the station [fraction] (50% -> 0.5) - * @see ModifiedSaastamoinenModel#ModifiedSaastamoinenModel(double, double, double, String, DataProvidersManager) + * @param r0 the humidity at the station [fraction] (50% → 0.5) + * @see ModifiedSaastamoinenModel#ModifiedSaastamoinenModel(PressureTemperatureHumidityProvider, String, DataProvidersManager) * @since 10.1 */ + @DefaultDataContext public SaastamoinenModel(final double t0, final double p0, final double r0) { - super(t0, p0, r0); + this(t0, p0, r0, DELTA_R_FILE_NAME); } /** Create a new Saastamoinen model for the troposphere using the given @@ -53,17 +65,17 @@ public SaastamoinenModel(final double t0, final double p0, final double r0) { * * @param t0 the temperature at the station [K] * @param p0 the atmospheric pressure at the station [mbar] - * @param r0 the humidity at the station [fraction] (50% -> 0.5) + * @param r0 the humidity at the station [fraction] (50% → 0.5) * @param deltaRFileName regular expression for filename containing δR * correction term table (typically {@link #DELTA_R_FILE_NAME}), if null * default values from the reference book are used * @since 7.1 - * @see ModifiedSaastamoinenModel#ModifiedSaastamoinenModel(double, double, double, String, DataProvidersManager) + * @see ModifiedSaastamoinenModel#ModifiedSaastamoinenModel(PressureTemperatureHumidityProvider, String, DataProvidersManager) */ @DefaultDataContext public SaastamoinenModel(final double t0, final double p0, final double r0, final String deltaRFileName) { - super(t0, p0, r0, deltaRFileName); + this(t0, p0, r0, deltaRFileName, DataContext.getDefault().getDataProvidersManager()); } /** Create a new Saastamoinen model for the troposphere using the given @@ -72,7 +84,7 @@ public SaastamoinenModel(final double t0, final double p0, final double r0, * * @param t0 the temperature at the station [K] * @param p0 the atmospheric pressure at the station [mbar] - * @param r0 the humidity at the station [fraction] (50% -> 0.5) + * @param r0 the humidity at the station [fraction] (50% → 0.5) * @param deltaRFileName regular expression for filename containing δR * correction term table (typically {@link #DELTA_R_FILE_NAME}), if null * default values from the reference book are used @@ -84,12 +96,22 @@ public SaastamoinenModel(final double t0, final double r0, final String deltaRFileName, final DataProvidersManager dataProvidersManager) { - super(t0, p0, r0, deltaRFileName, dataProvidersManager); + super(new ConstantPressureTemperatureHumidityProvider(new PressureTemperatureHumidity(0.0, + TroposphericModelUtils.HECTO_PASCAL.toSI(p0), + t0, + new Wang1988(). + waterVaporPressure(TroposphericModelUtils.HECTO_PASCAL.toSI(p0), + t0, + r0), + Double.NaN, + Double.NaN)), + deltaRFileName, dataProvidersManager); } /** Create a new Saastamoinen model using a standard atmosphere model. * *
        + *
      • altitude: 0m
      • *
      • temperature: 18 degree Celsius *
      • pressure: 1013.25 mbar *
      • humidity: 50% @@ -97,9 +119,31 @@ public SaastamoinenModel(final double t0, * * @return a Saastamoinen model with standard environmental values */ + @DefaultDataContext public static SaastamoinenModel getStandardModel() { return new SaastamoinenModel(273.16 + 18, 1013.25, 0.5); } -} + /** {@inheritDoc} */ + @Override + @Deprecated + public double pathDelay(final double elevation, final GeodeticPoint point, + final double[] parameters, final AbsoluteDate date) { + return pathDelay(new TrackingCoordinates(0.0, elevation, 0.0), point, + getPth0Provider().getWeatherParamerers(point, date), parameters, date).getDelay(); + } + /** {@inheritDoc} */ + @Override + @Deprecated + public > T pathDelay(final T elevation, + final FieldGeodeticPoint point, + final T[] parameters, + final FieldAbsoluteDate date) { + return pathDelay(new FieldTrackingCoordinates<>(date.getField().getZero(), elevation, date.getField().getZero()), + point, + getPth0Provider().getWeatherParamerers(point, date), + parameters, date).getDelay(); + } + +} diff --git a/src/main/java/org/orekit/models/earth/troposphere/TimeSpanEstimatedModel.java b/src/main/java/org/orekit/models/earth/troposphere/TimeSpanEstimatedModel.java new file mode 100644 index 0000000000..38748d8d5a --- /dev/null +++ b/src/main/java/org/orekit/models/earth/troposphere/TimeSpanEstimatedModel.java @@ -0,0 +1,277 @@ +/* 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.models.earth.troposphere; + +import java.util.ArrayList; +import java.util.List; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.util.MathArrays; +import org.orekit.annotation.DefaultDataContext; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; +import org.orekit.models.earth.weather.PressureTemperatureHumidity; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.TimeScale; +import org.orekit.time.TimeScalesFactory; +import org.orekit.utils.FieldTrackingCoordinates; +import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TimeSpanMap; +import org.orekit.utils.TimeSpanMap.Span; +import org.orekit.utils.TrackingCoordinates; + +/** + * Time span estimated tropospheric model. + *

        + * This class is closely related to {@link org.orekit.models.earth.troposphere EstimatedModel} class.
        + * The difference is that it has a {@link TimeSpanMap} of {@link EstimatedModel} objects as attribute.
        + * The idea behind this model is to allow the user to design a tropospheric model that can see its physical parameters + * (total zenith delay) change with time, at dates chosen by the user.
        + *

        + * @author Bryan Cazabonne + * @since 10.2 + */ +public class TimeSpanEstimatedModel implements TroposphericModel { + + /** Prefix for dates before in the tropospheric parameter drivers' name. */ + public static final String DATE_BEFORE = " - Before "; + + /** Prefix for dates after in the tropospheric parameter drivers' name. */ + public static final String DATE_AFTER = " - After "; + + /** Time scale for transition dates. */ + private final TimeScale timeScale; + + /** It contains all the models use for the whole period of measurements. */ + private final TimeSpanMap troposphericModelMap; + + /** + * Constructor with default UTC time scale. + * @param model the initial model which going to be used for all the models initialization. + */ + @DefaultDataContext + public TimeSpanEstimatedModel(final EstimatedModel model) { + this(model, TimeScalesFactory.getUTC()); + } + + /** + * Constructor with default UTC time scale. + * @param model the initial model which going to be used for all the models initialization. + * @param timeScale timeScale Time scale used for the default names of the tropospheric parameter drivers + */ + public TimeSpanEstimatedModel(final EstimatedModel model, final TimeScale timeScale) { + this.troposphericModelMap = new TimeSpanMap<>(model); + this.timeScale = timeScale; + } + + /** {@inheritDoc} + *

        + * All the parameter drivers of all Estimated models are returned in an array. + * Models are ordered chronologically. + *

        + */ + @Override + public List getParametersDrivers() { + + // Get all transitions from the TimeSpanMap + final List listTroposphericParameterDrivers = new ArrayList<>(); + + // Loop on the spans + for (Span span = getFirstSpan(); span != null; span = span.next()) { + // Add all the parameter drivers of each span + for (ParameterDriver tropoDriver : span.getData().getParametersDrivers()) { + // Add the driver only if the name does not exist already + if (!findByName(listTroposphericParameterDrivers, tropoDriver.getName())) { + listTroposphericParameterDrivers.add(tropoDriver); + } + } + } + + // Return an array of parameter drivers with no duplicated name + return listTroposphericParameterDrivers; + + } + + /** Add an EstimatedTroposphericModel entry valid before a limit date.
        + * Using addTroposphericValidBefore(entry, t) will make entry + * valid in ]-∞, t[ (note the open bracket). + * @param model EstimatedTroposphericModel entry + * @param latestValidityDate date before which the entry is valid + * (must be different from all dates already used for transitions) + */ + public void addTroposphericModelValidBefore(final EstimatedModel model, final AbsoluteDate latestValidityDate) { + troposphericModelMap.addValidBefore(changeTroposphericParameterDriversNames(model, + latestValidityDate, + DATE_BEFORE), + latestValidityDate, false); + } + + /** Add a EstimatedTroposphericModel entry valid after a limit date.
        + * Using addTroposphericModelValidAfter(entry, t) will make entry + * valid in [t, +∞[ (note the closed bracket). + * @param model EstimatedTroposphericModel entry + * @param earliestValidityDate date after which the entry is valid + * (must be different from all dates already used for transitions) + */ + public void addTroposphericModelValidAfter(final EstimatedModel model, final AbsoluteDate earliestValidityDate) { + troposphericModelMap.addValidAfter(changeTroposphericParameterDriversNames(model, + earliestValidityDate, + DATE_AFTER), + earliestValidityDate, false); + } + + /** Get the {@link EstimatedModel} model valid at a date. + * @param date the date of validity + * @return the EstimatedTroposphericModel model valid at date + */ + public EstimatedModel getTroposphericModel(final AbsoluteDate date) { + return troposphericModelMap.get(date); + } + + /** Get the first {@link Span time span} of the tropospheric model time span map. + * @return the first {@link Span time span} of the tropospheric model time span map + * @since 11.1 + */ + public Span getFirstSpan() { + return troposphericModelMap.getFirstSpan(); + } + + /** Extract the proper parameter drivers' values from the array in input of the + * {@link #pathDelay(TrackingCoordinates, GeodeticPoint, PressureTemperatureHumidity, double[], AbsoluteDate) pathDelay} method. + * Parameters are filtered given an input date. + * @param parameters the input parameters array + * @param date the date + * @return the parameters given the date + */ + public double[] extractParameters(final double[] parameters, final AbsoluteDate date) { + + // Get the tropospheric parameter drivers of the date + final List troposphericParameterDriver = getTroposphericModel(date).getParametersDrivers(); + + // Find out the indexes of the parameters in the whole array of parameters + final List allTroposphericParameters = getParametersDrivers(); + final double[] outParameters = new double[troposphericParameterDriver.size()]; + int index = 0; + for (int i = 0; i < allTroposphericParameters.size(); i++) { + final String driverName = allTroposphericParameters.get(i).getName(); + for (ParameterDriver tropoDriver : troposphericParameterDriver) { + if (tropoDriver.getName().equals(driverName)) { + outParameters[index++] = parameters[i]; + } + } + } + return outParameters; + } + + /** Extract the proper parameter drivers' values from the array in input of the + * {@link #pathDelay(TrackingCoordinates, GeodeticPoint, PressureTemperatureHumidity, double[], AbsoluteDate) pathDelay} method. + * Parameters are filtered given an input date. + * @param parameters the input parameters array + * @param date the date + * @param extends CalculusFieldElements + * @return the parameters given the date + */ + public > T[] extractParameters(final T[] parameters, + final FieldAbsoluteDate date) { + + // Get the tropospheric parameter drivers of the date + final List troposphericParameterDriver = getTroposphericModel(date.toAbsoluteDate()).getParametersDrivers(); + + // Find out the indexes of the parameters in the whole array of parameters + final List allTroposphericParameters = getParametersDrivers(); + final T[] outParameters = MathArrays.buildArray(date.getField(), troposphericParameterDriver.size()); + int index = 0; + for (int i = 0; i < allTroposphericParameters.size(); i++) { + final String driverName = allTroposphericParameters.get(i).getName(); + for (ParameterDriver tropoDriver : troposphericParameterDriver) { + if (tropoDriver.getName().equals(driverName)) { + outParameters[index++] = parameters[i]; + } + } + } + return outParameters; + } + + /** {@inheritDoc} */ + @Override + public TroposphericDelay pathDelay(final TrackingCoordinates trackingCoordinates, + final GeodeticPoint point, + final PressureTemperatureHumidity weather, + final double[] parameters, final AbsoluteDate date) { + // Extract the proper parameters valid at date from the input array + final double[] extractedParameters = extractParameters(parameters, date); + // Compute and return the path delay + return getTroposphericModel(date).pathDelay(trackingCoordinates, point, weather, + extractedParameters, date); + } + + /** {@inheritDoc} */ + @Override + public > FieldTroposphericDelay pathDelay(final FieldTrackingCoordinates trackingCoordinates, + final FieldGeodeticPoint point, + final FieldPressureTemperatureHumidity weather, + final T[] parameters, final FieldAbsoluteDate date) { + // Extract the proper parameters valid at date from the input array + final T[] extractedParameters = extractParameters(parameters, date); + // Compute and return the path delay + return getTroposphericModel(date.toAbsoluteDate()).pathDelay(trackingCoordinates, point, weather, + extractedParameters, date); + } + + /** Find if a parameter driver with a given name already exists in a list of parameter drivers. + * @param driversList the list of parameter drivers + * @param name the parameter driver's name to filter with + * @return true if the name was found, false otherwise + */ + private boolean findByName(final List driversList, final String name) { + for (final ParameterDriver driver : driversList) { + if (driver.getName().equals(name)) { + return true; + } + } + return false; + } + + /** Change the parameter drivers names of a {@link EstimatedModel} model, if needed. + *

        + * This is done to avoid that several parameter drivers have the same name.
        + * It is done only if the user hasn't modify the EstimatedTroposphericModel parameter drivers default names. + *

        + * @param troposphericModel the EstimatedTroposphericModel model + * @param date the date used in the parameter driver's name + * @param datePrefix the date prefix used in the parameter driver's name + * @return the EstimatedTroposphericModel with its drivers' names changed + */ + private EstimatedModel changeTroposphericParameterDriversNames(final EstimatedModel troposphericModel, + final AbsoluteDate date, + final String datePrefix) { + // Loop on the parameter drivers of the EstimatedTroposphericModel model + for (ParameterDriver driver: troposphericModel.getParametersDrivers()) { + final String driverName = driver.getName(); + + // If the name is the default name for EstimatedTroposphericModel parameter drivers + // Modify the name to add the prefix and the date + if (driverName.equals(EstimatedModel.TOTAL_ZENITH_DELAY)) { + driver.setName(driverName + datePrefix + date.toString(timeScale)); + } + } + return troposphericModel; + } + +} diff --git a/src/main/java/org/orekit/models/earth/troposphere/TimeSpanEstimatedTroposphericModel.java b/src/main/java/org/orekit/models/earth/troposphere/TimeSpanEstimatedTroposphericModel.java index 092193ab2c..f0f6ab84c3 100644 --- a/src/main/java/org/orekit/models/earth/troposphere/TimeSpanEstimatedTroposphericModel.java +++ b/src/main/java/org/orekit/models/earth/troposphere/TimeSpanEstimatedTroposphericModel.java @@ -42,7 +42,9 @@ *

        * @author Bryan Cazabonne * @since 10.2 + * @deprecated as of 12.1, replaced by {@link TimeSpanEstimatedModel} */ +@Deprecated public class TimeSpanEstimatedTroposphericModel implements DiscreteTroposphericModel { /** Prefix for dates before in the tropospheric parameter drivers' name. */ diff --git a/src/main/java/org/orekit/models/earth/troposphere/TroposphereMappingFunction.java b/src/main/java/org/orekit/models/earth/troposphere/TroposphereMappingFunction.java new file mode 100644 index 0000000000..21da4eef38 --- /dev/null +++ b/src/main/java/org/orekit/models/earth/troposphere/TroposphereMappingFunction.java @@ -0,0 +1,68 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.troposphere; + +import org.hipparchus.CalculusFieldElement; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; +import org.orekit.models.earth.weather.PressureTemperatureHumidity; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldTrackingCoordinates; +import org.orekit.utils.TrackingCoordinates; + +/** Interface for mapping functions used in the tropospheric delay computation. + * @author Bryan Cazabonne + * @author Luc Maisonobe + */ +public interface TroposphereMappingFunction { + + /** This method allows the computation of the hydrostatic and + * wet mapping functions. The resulting element is an array having the following form: + *
          + *
        • double[0] = mh(e) → hydrostatic mapping function + *
        • double[1] = mw(e) → wet mapping function + *
        + * @param trackingCoordinates tracking coordinates of the satellite + * @param point station location + * @param weather weather parameters + * @param date current date + * @return a two components array containing the hydrostatic and wet mapping functions. + */ + double[] mappingFactors(TrackingCoordinates trackingCoordinates, GeodeticPoint point, + PressureTemperatureHumidity weather, AbsoluteDate date); + + /** This method allows the computation of the hydrostatic and + * wet mapping functions. The resulting element is an array having the following form: + *
          + *
        • T[0] = mh(e) → hydrostatic mapping function + *
        • T[1] = mw(e) → wet mapping function + *
        + * @param trackingCoordinates tracking coordinates of the satellite + * @param point station location + * @param weather weather parameters + * @param date current date + * @param type of the elements + * @return a two components array containing the hydrostatic and wet mapping functions. + */ + > T[] mappingFactors(FieldTrackingCoordinates trackingCoordinates, + FieldGeodeticPoint point, + FieldPressureTemperatureHumidity weather, + FieldAbsoluteDate date); + +} diff --git a/src/main/java/org/orekit/models/earth/troposphere/TroposphereMappingFunctionAdapter.java b/src/main/java/org/orekit/models/earth/troposphere/TroposphereMappingFunctionAdapter.java new file mode 100644 index 0000000000..adff2a7188 --- /dev/null +++ b/src/main/java/org/orekit/models/earth/troposphere/TroposphereMappingFunctionAdapter.java @@ -0,0 +1,88 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.troposphere; + +import org.hipparchus.CalculusFieldElement; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; +import org.orekit.models.earth.weather.PressureTemperatureHumidity; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldTrackingCoordinates; +import org.orekit.utils.TrackingCoordinates; + +/** Adapter between {@link MappingFunction} and {@link TroposphereMappingFunction}. + *

        + * This class is a temporary adapter, it will be removed when + * {@link MappingFunction} is removed. + *

        + * @author Luc Maisonobe + * @since 12.1 + * @deprecated temporary adapter to be removed when {@link MappingFunction} is removed + */ +@Deprecated +public class TroposphereMappingFunctionAdapter implements TroposphereMappingFunction { + + /** Underlying model. */ + private final MappingFunction model; + + /** Simple constructor. + * @param model underlying model + */ + public TroposphereMappingFunctionAdapter(final MappingFunction model) { + this.model = model; + } + + /** This method allows the computation of the hydrostatic and + * wet mapping functions. The resulting element is an array having the following form: + *
          + *
        • double[0] = mh(e) → hydrostatic mapping function + *
        • double[1] = mw(e) → wet mapping function + *
        + * @param trackingCoordinates tracking coordinates of the satellite + * @param point station location + * @param weather weather parameters + * @param date current date + * @return a two components array containing the hydrostatic and wet mapping functions. + */ + public double[] mappingFactors(final TrackingCoordinates trackingCoordinates, final GeodeticPoint point, + final PressureTemperatureHumidity weather, final AbsoluteDate date) { + return model.mappingFactors(trackingCoordinates.getElevation(), point, date); + } + + /** This method allows the computation of the hydrostatic and + * wet mapping functions. The resulting element is an array having the following form: + *
          + *
        • T[0] = mh(e) → hydrostatic mapping function + *
        • T[1] = mw(e) → wet mapping function + *
        + * @param trackingCoordinates tracking coordinates of the satellite + * @param point station location + * @param weather weather parameters + * @param date current date + * @param type of the elements + * @return a two components array containing the hydrostatic and wet mapping functions. + */ + public > T[] mappingFactors(final FieldTrackingCoordinates trackingCoordinates, + final FieldGeodeticPoint point, + final FieldPressureTemperatureHumidity weather, + final FieldAbsoluteDate date) { + return model.mappingFactors(trackingCoordinates.getElevation(), point, date); + } + +} diff --git a/src/main/java/org/orekit/models/earth/troposphere/TroposphericDelay.java b/src/main/java/org/orekit/models/earth/troposphere/TroposphericDelay.java new file mode 100644 index 0000000000..6f1325b58f --- /dev/null +++ b/src/main/java/org/orekit/models/earth/troposphere/TroposphericDelay.java @@ -0,0 +1,85 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.troposphere; + +/** Container for tropospheric delay. + * @author Luc Maisonobe + * @since 12.1 + */ +public class TroposphericDelay { + + /** Hydrostatic zenith delay (m). */ + private final double zh; + + /** Wet zenith delay (m). */ + private final double zw; + + /** Hydrostatic slanted delay (m). */ + private final double sh; + + /** Wet slanted delay (m). */ + private final double sw; + + /** Simple constructor. + * @param zh hydrostatic zenith delay (m) + * @param zw wet zenith delay (m) + * @param sh hydrostatic slanted delay (m) + * @param sw wet slanted delay (m) + */ + public TroposphericDelay(final double zh, final double zw, final double sh, final double sw) { + this.zh = zh; + this.zw = zw; + this.sh = sh; + this.sw = sw; + } + + /** Get hydrostatic zenith delay (m). + * @return hydrostatic zenith delay (m) + */ + public double getZh() { + return zh; + } + + /** Get wet zenith delay (m). + * @return wet zenith delay (m) + */ + public double getZw() { + return zw; + } + + /** Get slanted delay (m). + * @return slanted delay (m) + */ + public double getSh() { + return sh; + } + + /** Get wet slanted delay (m). + * @return wet slanted delay (m) + */ + public double getSw() { + return sw; + } + + /** Get the total slanted delay (m). + * @return total slanted delay (m) + */ + public double getDelay() { + return sh + sw; + } + +} diff --git a/src/main/java/org/orekit/models/earth/troposphere/TroposphericModel.java b/src/main/java/org/orekit/models/earth/troposphere/TroposphericModel.java new file mode 100644 index 0000000000..c60f56968d --- /dev/null +++ b/src/main/java/org/orekit/models/earth/troposphere/TroposphericModel.java @@ -0,0 +1,68 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.troposphere; + +import org.hipparchus.CalculusFieldElement; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; +import org.orekit.models.earth.weather.PressureTemperatureHumidity; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldTrackingCoordinates; +import org.orekit.utils.ParameterDriversProvider; +import org.orekit.utils.TrackingCoordinates; + +/** Defines a tropospheric model, used to calculate the path delay imposed to + * electro-magnetic signals between an orbital satellite and a ground station. + * @author Luc Maisonobe + * @since 12.1 + */ +public interface TroposphericModel extends ParameterDriversProvider { + + /** Calculates the tropospheric path delay for the signal path from a ground + * station to a satellite. + * + * @param trackingCoordinates tracking coordinates of the satellite + * @param point station location + * @param weather weather parameters + * for constant default values) + * @param parameters tropospheric model parameters + * @param date current date + * @return the path delay due to the troposphere + */ + TroposphericDelay pathDelay(TrackingCoordinates trackingCoordinates, GeodeticPoint point, + PressureTemperatureHumidity weather, + double[] parameters, AbsoluteDate date); + + /** Calculates the tropospheric path delay for the signal path from a ground + * station to a satellite. + * + * @param type of the elements + * @param trackingCoordinates tracking coordinates of the satellite + * @param point station location + * @param weather weather parameters + * for constant default values) + * @param parameters tropospheric model parameters at current date + * @param date current date + * @return the path delay due to the troposphere + */ + > FieldTroposphericDelay pathDelay(FieldTrackingCoordinates trackingCoordinates, + FieldGeodeticPoint point, + FieldPressureTemperatureHumidity weather, + T[] parameters, FieldAbsoluteDate date); +} diff --git a/src/main/java/org/orekit/models/earth/troposphere/TroposphericModelAdapter.java b/src/main/java/org/orekit/models/earth/troposphere/TroposphericModelAdapter.java new file mode 100644 index 0000000000..7ad22cfa5c --- /dev/null +++ b/src/main/java/org/orekit/models/earth/troposphere/TroposphericModelAdapter.java @@ -0,0 +1,103 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.troposphere; + +import java.util.List; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.util.MathUtils; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; +import org.orekit.models.earth.weather.PressureTemperatureHumidity; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldTrackingCoordinates; +import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TrackingCoordinates; + +/** Adapter between {@link DiscreteTroposphericModel} and {@link TroposphericModel}. + *

        + * This class is a temporary adapter, it will be removed when + * {@link DiscreteTroposphericModel} is removed. + *

        + * @author Luc Maisonobe + * @since 12.1 + * @deprecated temporary adapter to be removed when {@link DiscreteTroposphericModel} is removed + */ +@Deprecated +public class TroposphericModelAdapter implements TroposphericModel { + + /** Underlying model. */ + private final DiscreteTroposphericModel model; + + /** Simple constructor. + * @param model underlying model + */ + public TroposphericModelAdapter(final DiscreteTroposphericModel model) { + this.model = model; + } + + /** {@inheritDoc} + *

        + * All delays are affected to {@link TroposphericDelay#getZh() hydrostatic zenith} + * and {@link TroposphericDelay#getSh() hydrostatic slanted} delays, the wet delays + * are arbitrarily set to 0. + *

        + */ + @Override + public TroposphericDelay pathDelay(final TrackingCoordinates trackingCoordinates, + final GeodeticPoint point, + final PressureTemperatureHumidity weather, + final double[] parameters, + final AbsoluteDate date) { + return new TroposphericDelay(model.pathDelay(MathUtils.SEMI_PI, + point, parameters, date), + 0.0, + model.pathDelay(trackingCoordinates.getElevation(), + point, parameters, date), + 0.0); + } + + /** {@inheritDoc} + *

        + * All delays are affected to {@link FieldTroposphericDelay#getZh() hydrostatic zenith} + * and {@link FieldTroposphericDelay#getSh() hydrostatic slanted} delays, the wet delays + * are arbitrarily set to 0. + *

        + */ + @Override + public > FieldTroposphericDelay pathDelay(final FieldTrackingCoordinates trackingCoordinates, + final FieldGeodeticPoint point, + final FieldPressureTemperatureHumidity weather, + final T[] parameters, + final FieldAbsoluteDate date) { + return new FieldTroposphericDelay<>(model.pathDelay(date.getField().getZero().newInstance(MathUtils.SEMI_PI), + point, parameters, date), + date.getField().getZero(), + model.pathDelay(trackingCoordinates.getElevation(), + point, parameters, date), + date.getField().getZero()); + } + + /** {@inheritDoc} */ + @Override + public List getParametersDrivers() { + return model.getParametersDrivers(); + } + +} diff --git a/src/main/java/org/orekit/models/earth/troposphere/TroposphericModelUtils.java b/src/main/java/org/orekit/models/earth/troposphere/TroposphericModelUtils.java index bcfaec30f7..6b844b1193 100644 --- a/src/main/java/org/orekit/models/earth/troposphere/TroposphericModelUtils.java +++ b/src/main/java/org/orekit/models/earth/troposphere/TroposphericModelUtils.java @@ -19,6 +19,11 @@ import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; import org.hipparchus.util.FastMath; +import org.orekit.models.earth.weather.ConstantPressureTemperatureHumidityProvider; +import org.orekit.models.earth.weather.PressureTemperatureHumidity; +import org.orekit.models.earth.weather.PressureTemperatureHumidityProvider; +import org.orekit.models.earth.weather.water.CIPM2007; +import org.orekit.utils.units.Unit; /** * Utility class for tropospheric models. @@ -27,6 +32,50 @@ */ public class TroposphericModelUtils { + /** Nanometers unit. + * @since 12.1 + */ + public static final Unit NANO_M = Unit.parse("nm"); + + /** Micrometers unit. + * @since 12.1 + */ + public static final Unit MICRO_M = Unit.parse("µm"); + + /** HectoPascal unit. + * @since 12.1 + */ + public static final Unit HECTO_PASCAL = Unit.parse("hPa"); + + /** Standard atmosphere. + *
          + *
        • altitude: 0m
        • + *
        • temperature: 20 degree Celsius
        • + *
        • pressure: 1013.25 mbar
        • + *
        • humidity: 50%
        • + *
        + * @see #STANDARD_ATMOSPHERE_PROVIDER + * @since 12.1 + */ + public static final PressureTemperatureHumidity STANDARD_ATMOSPHERE; + + /** Provider for {@link #STANDARD_ATMOSPHERE standard atmosphere}. + * @since 12.1 + */ + public static final PressureTemperatureHumidityProvider STANDARD_ATMOSPHERE_PROVIDER; + + static { + final double h = 0.0; + final double p = HECTO_PASCAL.toSI(1013.25); + final double t = 273.15 + 20; + final double rh = 0.5; + STANDARD_ATMOSPHERE = new PressureTemperatureHumidity(h, p, t, + new CIPM2007().waterVaporPressure(p, t, rh), + Double.NaN, Double.NaN); + STANDARD_ATMOSPHERE_PROVIDER = + new ConstantPressureTemperatureHumidityProvider(STANDARD_ATMOSPHERE); + } + /** * Private constructor as class is a utility. */ @@ -116,7 +165,7 @@ public static > T computeHeightCorrection(fina final T fixedHeight = FastMath.max(zero, height); final T sinE = FastMath.sin(elevation); // Ref: Eq. 4 - final T function = TroposphericModelUtils.mappingFunction(zero.add(2.53e-5), zero.add(5.49e-3), zero.add(1.14e-3), elevation); + final T function = TroposphericModelUtils.mappingFunction(zero.newInstance(2.53e-5), zero.newInstance(5.49e-3), zero.newInstance(1.14e-3), elevation); // Ref: Eq. 6 final T dmdh = sinE.reciprocal().subtract(function); // Ref: Eq. 7 diff --git a/src/main/java/org/orekit/models/earth/troposphere/ViennaACoefficients.java b/src/main/java/org/orekit/models/earth/troposphere/ViennaACoefficients.java new file mode 100644 index 0000000000..27aa2d179a --- /dev/null +++ b/src/main/java/org/orekit/models/earth/troposphere/ViennaACoefficients.java @@ -0,0 +1,54 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.troposphere; + +/** Container for the {@link ViennaOne} and {@link ViennaThree} coefficients ah and aw. + * @author Luc Maisonobe + * @since 12.1 + */ +public class ViennaACoefficients { + + /** Hydrostatic coefficient. */ + private final double ah; + + /** Wet coefficient. */ + private final double aw; + + /** Simple constructor. + * @param ah hydrostatic coefficient + * @param aw wet coefficient + */ + public ViennaACoefficients(final double ah, final double aw) { + this.ah = ah; + this.aw = aw; + } + + /** Get hydrostatic coefficient. + * @return hydrostatic coefficient + */ + public double getAh() { + return ah; + } + + /** Get wet coefficient. + * @return wet coefficient + */ + public double getAw() { + return aw; + } + +} diff --git a/src/main/java/org/orekit/models/earth/troposphere/ViennaAProvider.java b/src/main/java/org/orekit/models/earth/troposphere/ViennaAProvider.java new file mode 100644 index 0000000000..cbcdff64eb --- /dev/null +++ b/src/main/java/org/orekit/models/earth/troposphere/ViennaAProvider.java @@ -0,0 +1,55 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.troposphere; + +import org.hipparchus.CalculusFieldElement; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; + +/** Provider for {@link ViennaOne} and {@link ViennaThree} coefficients ah and aw. + * @since 12.1 + * @author Luc Maisonobe + */ +public interface ViennaAProvider { + + /** Get coefficients array for VMF mapping function. + *
          + *
        • double[0] = ah + *
        • double[1] = aw + *
        + * @param location location at which parameters are requested + * @param date date at which parameters are requested + * @return the coefficients array for VMF mapping function + */ + ViennaACoefficients getA(GeodeticPoint location, AbsoluteDate date); + + /** Get coefficients array for VMF mapping function. + *
          + *
        • double[0] = ah + *
        • double[1] = aw + *
        + * @param type of the field elements + * @param location location at which parameters are requested + * @param date date at which parameters are requested + * @return the coefficients array for VMF mapping function + */ + > FieldViennaACoefficients getA(FieldGeodeticPoint location, + FieldAbsoluteDate date); + +} diff --git a/src/main/java/org/orekit/models/earth/troposphere/ViennaOne.java b/src/main/java/org/orekit/models/earth/troposphere/ViennaOne.java new file mode 100644 index 0000000000..034f00483f --- /dev/null +++ b/src/main/java/org/orekit/models/earth/troposphere/ViennaOne.java @@ -0,0 +1,202 @@ +/* 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.models.earth.troposphere; + +import java.util.Collections; +import java.util.List; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.MathArrays; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; +import org.orekit.models.earth.weather.PressureTemperatureHumidity; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.TimeScale; +import org.orekit.utils.FieldTrackingCoordinates; +import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TrackingCoordinates; + +/** The Vienna 1 tropospheric delay model for radio techniques. + * The Vienna model data are given with a time interval of 6 hours + * as well as on a global 2.5° * 2.0° grid. + * + * This version considered the height correction for the hydrostatic part + * developed by Niell, 1996. + * + * @see "Boehm, J., Werl, B., and Schuh, H., (2006), + * Troposhere mapping functions for GPS and very long baseline + * interferometry from European Centre for Medium-Range Weather + * Forecasts operational analysis data, J. Geophy. Res., Vol. 111, + * B02406, doi:10.1029/2005JB003629" + * @since 12.1 + * @author Bryan Cazabonne + * @author Luc Maisonobe + */ +public class ViennaOne extends AbstractVienna { + + /** Build a new instance. + * @param aProvider provider for ah and aw coefficients + * @param gProvider provider for {@link AzimuthalGradientCoefficients} and {@link FieldAzimuthalGradientCoefficients} + * @param zenithDelayProvider provider for zenith delays + * @param utc UTC time scale + */ + public ViennaOne(final ViennaAProvider aProvider, + final AzimuthalGradientProvider gProvider, + final TroposphericModel zenithDelayProvider, + final TimeScale utc) { + super(aProvider, gProvider, zenithDelayProvider, utc); + } + + /** {@inheritDoc} */ + @Override + public double[] mappingFactors(final TrackingCoordinates trackingCoordinates, + final GeodeticPoint point, + final PressureTemperatureHumidity weather, + final AbsoluteDate date) { + + // a coefficients + final ViennaACoefficients a = getAProvider().getA(point, date); + + // Day of year computation + final int dofyear = getDayOfYear(date); + + // General constants | Hydrostatic part + final double bh = 0.0029; + final double c0h = 0.062; + final double c10h; + final double c11h; + final double psi; + + // Latitude of the station + final double latitude = point.getLatitude(); + + // sin(latitude) > 0 -> northern hemisphere + if (FastMath.sin(latitude) > 0) { + c10h = 0.001; + c11h = 0.005; + psi = 0; + } else { + c10h = 0.002; + c11h = 0.007; + psi = FastMath.PI; + } + + // Temporal factor + double t0 = 28; + if (latitude < 0) { + // southern hemisphere: t0 = 28 + an integer half of year + t0 += 183; + } + // Compute hydrostatique coefficient c + final double coef = ((dofyear - t0) / 365) * 2 * FastMath.PI + psi; + final double ch = c0h + ((FastMath.cos(coef) + 1) * (c11h / 2) + c10h) * (1 - FastMath.cos(latitude)); + + // General constants | Wet part + final double bw = 0.00146; + final double cw = 0.04391; + + final double[] function = new double[2]; + function[0] = TroposphericModelUtils.mappingFunction(a.getAh(), bh, ch, + trackingCoordinates.getElevation()); + function[1] = TroposphericModelUtils.mappingFunction(a.getAw(), bw, cw, + trackingCoordinates.getElevation()); + + // Apply height correction + final double correction = TroposphericModelUtils.computeHeightCorrection(trackingCoordinates.getElevation(), + point.getAltitude()); + function[0] = function[0] + correction; + + return function; + } + + /** {@inheritDoc} */ + @Override + public > T[] mappingFactors(final FieldTrackingCoordinates trackingCoordinates, + final FieldGeodeticPoint point, + final FieldPressureTemperatureHumidity weather, + final FieldAbsoluteDate date) { + + final Field field = date.getField(); + final T zero = field.getZero(); + + // a coefficients + final FieldViennaACoefficients a = getAProvider().getA(point, date); + + // Day of year computation + final int dofyear = getDayOfYear(date.toAbsoluteDate()); + + // General constants | Hydrostatic part + final T bh = zero.newInstance(0.0029); + final T c0h = zero.newInstance(0.062); + final T c10h; + final T c11h; + final T psi; + + // Latitude and longitude of the station + final T latitude = point.getLatitude(); + + // sin(latitude) > 0 -> northern hemisphere + if (FastMath.sin(latitude.getReal()) > 0) { + c10h = zero.newInstance(0.001); + c11h = zero.newInstance(0.005); + psi = zero; + } else { + c10h = zero.newInstance(0.002); + c11h = zero.newInstance(0.007); + psi = zero.getPi(); + } + + // Compute hydrostatique coefficient c + // Temporal factor + double t0 = 28; + if (latitude.getReal() < 0) { + // southern hemisphere: t0 = 28 + an integer half of year + t0 += 183; + } + final T coef = psi.add(zero.getPi().multiply(2.0).multiply((dofyear - t0) / 365)); + final T ch = c11h.divide(2.0).multiply(FastMath.cos(coef).add(1.0)).add(c10h).multiply(FastMath.cos(latitude).negate().add(1.)).add(c0h); + + // General constants | Wet part + final T bw = zero.newInstance(0.00146); + final T cw = zero.newInstance(0.04391); + + final T[] function = MathArrays.buildArray(field, 2); + function[0] = TroposphericModelUtils.mappingFunction(a.getAh(), bh, ch, + trackingCoordinates.getElevation()); + function[1] = TroposphericModelUtils.mappingFunction(a.getAw(), bw, cw, + trackingCoordinates.getElevation()); + + // Apply height correction + final T correction = TroposphericModelUtils.computeHeightCorrection(trackingCoordinates.getElevation(), + point.getAltitude(), + field); + function[0] = function[0].add(correction); + + return function; + } + + /** {@inheritDoc} */ + @Override + public List getParametersDrivers() { + return Collections.emptyList(); + } + +} diff --git a/src/main/java/org/orekit/models/earth/troposphere/ViennaOneModel.java b/src/main/java/org/orekit/models/earth/troposphere/ViennaOneModel.java index 9a3a7d711d..f442ee42bb 100644 --- a/src/main/java/org/orekit/models/earth/troposphere/ViennaOneModel.java +++ b/src/main/java/org/orekit/models/earth/troposphere/ViennaOneModel.java @@ -16,22 +16,19 @@ */ package org.orekit.models.earth.troposphere; -import java.util.Collections; -import java.util.List; - -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; -import org.hipparchus.util.FastMath; +import org.hipparchus.Field; import org.hipparchus.util.MathArrays; import org.orekit.annotation.DefaultDataContext; import org.orekit.bodies.FieldGeodeticPoint; import org.orekit.bodies.GeodeticPoint; import org.orekit.data.DataContext; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; import org.orekit.time.AbsoluteDate; -import org.orekit.time.DateTimeComponents; import org.orekit.time.FieldAbsoluteDate; import org.orekit.time.TimeScale; -import org.orekit.utils.ParameterDriver; +import org.orekit.utils.FieldTrackingCoordinates; +import org.orekit.utils.TrackingCoordinates; /** The Vienna1 tropospheric delay model for radio techniques. * The Vienna model data are given with a time interval of 6 hours @@ -47,18 +44,14 @@ * B02406, doi:10.1029/2005JB003629" * * @author Bryan Cazabonne + * @deprecated as of 12.1, replaced by {@link ViennaOne} */ -public class ViennaOneModel implements DiscreteTroposphericModel, MappingFunction { - - /** The a coefficient for the computation of the wet and hydrostatic mapping functions.*/ - private final double[] coefficientsA; +@Deprecated +public class ViennaOneModel extends ViennaOne implements DiscreteTroposphericModel, MappingFunction { /** Values of hydrostatic and wet delays as provided by the Vienna model. */ private final double[] zenithDelay; - /** UTC time scale. */ - private final TimeScale utc; - /** Build a new instance. * *

        This constructor uses the {@link DataContext#getDefault() default data context}. @@ -85,33 +78,34 @@ public ViennaOneModel(final double[] coefficientA, final double[] zenithDelay) { public ViennaOneModel(final double[] coefficientA, final double[] zenithDelay, final TimeScale utc) { - this.coefficientsA = coefficientA.clone(); - this.zenithDelay = zenithDelay.clone(); - this.utc = utc; + super(new ConstantViennaAProvider(new ViennaACoefficients(coefficientA[0], coefficientA[1])), + new ConstantAzimuthalGradientProvider(null), + new ConstantTroposphericModel(new TroposphericDelay(zenithDelay[0], zenithDelay[1], + zenithDelay[0], zenithDelay[1])), + utc); + this.zenithDelay = zenithDelay.clone(); } /** {@inheritDoc} */ @Override + @Deprecated public double pathDelay(final double elevation, final GeodeticPoint point, final double[] parameters, final AbsoluteDate date) { - // zenith delay - final double[] delays = computeZenithDelay(point, parameters, date); - // mapping function - final double[] mappingFunction = mappingFactors(elevation, point, date); - // Tropospheric path delay - return delays[0] * mappingFunction[0] + delays[1] * mappingFunction[1]; + return pathDelay(new TrackingCoordinates(0.0, elevation, 0.0), + point, TroposphericModelUtils.STANDARD_ATMOSPHERE, parameters, date). + getDelay(); } /** {@inheritDoc} */ @Override + @Deprecated public > T pathDelay(final T elevation, final FieldGeodeticPoint point, - final T[] parameters, final FieldAbsoluteDate date) { - // zenith delay - final T[] delays = computeZenithDelay(point, parameters, date); - // mapping function - final T[] mappingFunction = mappingFactors(elevation, point, date); - // Tropospheric path delay - return delays[0].multiply(mappingFunction[0]).add(delays[1].multiply(mappingFunction[1])); + final T[] parameters, final FieldAbsoluteDate date) { + return pathDelay(new FieldTrackingCoordinates<>(date.getField().getZero(), elevation, date.getField().getZero()), + point, + new FieldPressureTemperatureHumidity<>(date.getField(), TroposphericModelUtils.STANDARD_ATMOSPHERE), + parameters, date). + getDelay(); } /** This method allows the computation of the zenith hydrostatic and @@ -142,130 +136,36 @@ public double[] computeZenithDelay(final GeodeticPoint point, final double[] par * @return a two components array containing the zenith hydrostatic and wet delays. */ public > T[] computeZenithDelay(final FieldGeodeticPoint point, final T[] parameters, - final FieldAbsoluteDate date) { + final FieldAbsoluteDate date) { final Field field = date.getField(); final T zero = field.getZero(); final T[] delays = MathArrays.buildArray(field, 2); - delays[0] = zero.add(zenithDelay[0]); - delays[1] = zero.add(zenithDelay[1]); + delays[0] = zero.newInstance(zenithDelay[0]); + delays[1] = zero.newInstance(zenithDelay[1]); return delays; } /** {@inheritDoc} */ @Override + @Deprecated public double[] mappingFactors(final double elevation, final GeodeticPoint point, final AbsoluteDate date) { - // Day of year computation - final DateTimeComponents dtc = date.getComponents(utc); - final int dofyear = dtc.getDate().getDayOfYear(); - - // General constants | Hydrostatic part - final double bh = 0.0029; - final double c0h = 0.062; - final double c10h; - final double c11h; - final double psi; - - // Latitude of the station - final double latitude = point.getLatitude(); - - // sin(latitude) > 0 -> northern hemisphere - if (FastMath.sin(latitude) > 0) { - c10h = 0.001; - c11h = 0.005; - psi = 0; - } else { - c10h = 0.002; - c11h = 0.007; - psi = FastMath.PI; - } - - // Temporal factor - double t0 = 28; - if (latitude < 0) { - // southern hemisphere: t0 = 28 + an integer half of year - t0 += 183; - } - // Compute hydrostatique coefficient c - final double coef = ((dofyear - t0) / 365) * 2 * FastMath.PI + psi; - final double ch = c0h + ((FastMath.cos(coef) + 1) * (c11h / 2) + c10h) * (1 - FastMath.cos(latitude)); - - // General constants | Wet part - final double bw = 0.00146; - final double cw = 0.04391; - - final double[] function = new double[2]; - function[0] = TroposphericModelUtils.mappingFunction(coefficientsA[0], bh, ch, elevation); - function[1] = TroposphericModelUtils.mappingFunction(coefficientsA[1], bw, cw, elevation); - - // Apply height correction - final double correction = TroposphericModelUtils.computeHeightCorrection(elevation, point.getAltitude()); - function[0] = function[0] + correction; - - return function; + return mappingFactors(new TrackingCoordinates(0.0, elevation, 0.0), + point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + date); } /** {@inheritDoc} */ @Override + @Deprecated public > T[] mappingFactors(final T elevation, final FieldGeodeticPoint point, - final FieldAbsoluteDate date) { - final Field field = date.getField(); - final T zero = field.getZero(); - - // Day of year computation - final DateTimeComponents dtc = date.getComponents(utc); - final int dofyear = dtc.getDate().getDayOfYear(); - - // General constants | Hydrostatic part - final T bh = zero.add(0.0029); - final T c0h = zero.add(0.062); - final T c10h; - final T c11h; - final T psi; - - // Latitude and longitude of the station - final T latitude = point.getLatitude(); - - // sin(latitude) > 0 -> northern hemisphere - if (FastMath.sin(latitude.getReal()) > 0) { - c10h = zero.add(0.001); - c11h = zero.add(0.005); - psi = zero; - } else { - c10h = zero.add(0.002); - c11h = zero.add(0.007); - psi = zero.getPi(); - } - - // Compute hydrostatique coefficient c - // Temporal factor - double t0 = 28; - if (latitude.getReal() < 0) { - // southern hemisphere: t0 = 28 + an integer half of year - t0 += 183; - } - final T coef = psi.add(zero.getPi().multiply(2.0).multiply((dofyear - t0) / 365)); - final T ch = c11h.divide(2.0).multiply(FastMath.cos(coef).add(1.0)).add(c10h).multiply(FastMath.cos(latitude).negate().add(1.)).add(c0h); - - // General constants | Wet part - final T bw = zero.add(0.00146); - final T cw = zero.add(0.04391); - - final T[] function = MathArrays.buildArray(field, 2); - function[0] = TroposphericModelUtils.mappingFunction(zero.add(coefficientsA[0]), bh, ch, elevation); - function[1] = TroposphericModelUtils.mappingFunction(zero.add(coefficientsA[1]), bw, cw, elevation); - - // Apply height correction - final T correction = TroposphericModelUtils.computeHeightCorrection(elevation, point.getAltitude(), field); - function[0] = function[0].add(correction); - - return function; - } - - /** {@inheritDoc} */ - @Override - public List getParametersDrivers() { - return Collections.emptyList(); + final FieldAbsoluteDate date) { + return mappingFactors(new FieldTrackingCoordinates<>(date.getField().getZero(), elevation, date.getField().getZero()), + point, + new FieldPressureTemperatureHumidity<>(date.getField(), + TroposphericModelUtils.STANDARD_ATMOSPHERE), + date); } } diff --git a/src/main/java/org/orekit/models/earth/troposphere/ViennaThree.java b/src/main/java/org/orekit/models/earth/troposphere/ViennaThree.java new file mode 100644 index 0000000000..1b6b624b59 --- /dev/null +++ b/src/main/java/org/orekit/models/earth/troposphere/ViennaThree.java @@ -0,0 +1,1144 @@ +/* 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.models.earth.troposphere; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.FieldSinCos; +import org.hipparchus.util.MathArrays; +import org.hipparchus.util.SinCos; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; +import org.orekit.models.earth.weather.PressureTemperatureHumidity; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.TimeScale; +import org.orekit.utils.FieldLegendrePolynomials; +import org.orekit.utils.FieldTrackingCoordinates; +import org.orekit.utils.LegendrePolynomials; +import org.orekit.utils.TrackingCoordinates; + +/** The Vienna 3 tropospheric delay model for radio techniques. + * The Vienna model data are given with a time interval of 6 hours. + *

        + * The empirical coefficients bh, bw, ch + * and cw are computed with spherical harmonics. + * In that respect, they are considerably more advanced than those of + * {@link ViennaOne VMF1} model. + *

        + * + * @see "Landskron, D. & Böhm, J. J Geod (2018) + * VMF3/GPT3: refined discrete and empirical troposphere mapping functions + * 92: 349. https://doi.org/10.1007/s00190-017-1066-2" + * + * @see "Landskron D (2017) Modeling tropospheric delays for space geodetic + * techniques. Dissertation, Department of Geodesy and Geoinformation, TU Wien, Supervisor: J. Böhm. + * http://repositum.tuwien.ac.at/urn:nbn:at:at-ubtuw:1-100249" + * + * @author Bryan Cazabonne + */ +public class ViennaThree extends AbstractVienna { + + /** Build a new instance. + * @param aProvider provider for ah and aw coefficients + * @param gProvider provider for {@link AzimuthalGradientCoefficients} and {@link FieldAzimuthalGradientCoefficients} + * @param zenithDelayProvider provider for zenith delays + * @param utc UTC time scale + */ + public ViennaThree(final ViennaAProvider aProvider, + final AzimuthalGradientProvider gProvider, + final TroposphericModel zenithDelayProvider, + final TimeScale utc) { + super(aProvider, gProvider, zenithDelayProvider, utc); + } + + /** {@inheritDoc} */ + @Override + public double[] mappingFactors(final TrackingCoordinates trackingCoordinates, + final GeodeticPoint point, + final PressureTemperatureHumidity weather, + final AbsoluteDate date) { + + // a coefficients + final ViennaACoefficients a = getAProvider().getA(point, date); + + // Day of year computation + final int dofyear = getDayOfYear(date); + + // Compute Legendre Polynomials Pnm(cos(0.5 * pi - phi)) + final int degree = 12; + final int order = 12; + final LegendrePolynomials p = new LegendrePolynomials(degree, order, FastMath.cos(0.5 * FastMath.PI - point.getLatitude())); + + // Compute coefficients bh, bw, ch and cw with spherical harmonics + double a0Bh = 0.; + double a0Bw = 0.; + double a0Ch = 0.; + double a0Cw = 0.; + double a1Bh = 0.; + double a1Bw = 0.; + double a1Ch = 0.; + double a1Cw = 0.; + double b1Bh = 0.; + double b1Bw = 0.; + double b1Ch = 0.; + double b1Cw = 0.; + double a2Bh = 0.; + double a2Bw = 0.; + double a2Ch = 0.; + double a2Cw = 0.; + double b2Bh = 0.; + double b2Bw = 0.; + double b2Ch = 0.; + double b2Cw = 0.; + final LegendreFunctions AnmBnm = new LegendreFunctions(); + int j = 0; + for (int n = 0; n <= 12; n++) { + for (int m = 0; m <= n; m++) { + final SinCos sc = FastMath.sinCos(m * point.getLongitude()); + final double pCosmLambda = p.getPnm(n, m) * sc.cos(); + final double pSinmLambda = p.getPnm(n, m) * sc.sin(); + + a0Bh = a0Bh + (AnmBnm.getAnmBh(j, 0) * pCosmLambda + AnmBnm.getBnmBh(j, 0) * pSinmLambda); + a0Bw = a0Bw + (AnmBnm.getAnmBw(j, 0) * pCosmLambda + AnmBnm.getBnmBw(j, 0) * pSinmLambda); + a0Ch = a0Ch + (AnmBnm.getAnmCh(j, 0) * pCosmLambda + AnmBnm.getBnmCh(j, 0) * pSinmLambda); + a0Cw = a0Cw + (AnmBnm.getAnmCw(j, 0) * pCosmLambda + AnmBnm.getBnmCw(j, 0) * pSinmLambda); + + a1Bh = a1Bh + (AnmBnm.getAnmBh(j, 1) * pCosmLambda + AnmBnm.getBnmBh(j, 1) * pSinmLambda); + a1Bw = a1Bw + (AnmBnm.getAnmBw(j, 1) * pCosmLambda + AnmBnm.getBnmBw(j, 1) * pSinmLambda); + a1Ch = a1Ch + (AnmBnm.getAnmCh(j, 1) * pCosmLambda + AnmBnm.getBnmCh(j, 1) * pSinmLambda); + a1Cw = a1Cw + (AnmBnm.getAnmCw(j, 1) * pCosmLambda + AnmBnm.getBnmCw(j, 1) * pSinmLambda); + + b1Bh = b1Bh + (AnmBnm.getAnmBh(j, 2) * pCosmLambda + AnmBnm.getBnmBh(j, 2) * pSinmLambda); + b1Bw = b1Bw + (AnmBnm.getAnmBw(j, 2) * pCosmLambda + AnmBnm.getBnmBw(j, 2) * pSinmLambda); + b1Ch = b1Ch + (AnmBnm.getAnmCh(j, 2) * pCosmLambda + AnmBnm.getBnmCh(j, 2) * pSinmLambda); + b1Cw = b1Cw + (AnmBnm.getAnmCw(j, 2) * pCosmLambda + AnmBnm.getBnmCw(j, 2) * pSinmLambda); + + a2Bh = a2Bh + (AnmBnm.getAnmBh(j, 3) * pCosmLambda + AnmBnm.getBnmBh(j, 3) * pSinmLambda); + a2Bw = a2Bw + (AnmBnm.getAnmBw(j, 3) * pCosmLambda + AnmBnm.getBnmBw(j, 3) * pSinmLambda); + a2Ch = a2Ch + (AnmBnm.getAnmCh(j, 3) * pCosmLambda + AnmBnm.getBnmCh(j, 3) * pSinmLambda); + a2Cw = a2Cw + (AnmBnm.getAnmCw(j, 3) * pCosmLambda + AnmBnm.getBnmCw(j, 3) * pSinmLambda); + + b2Bh = b2Bh + (AnmBnm.getAnmBh(j, 4) * pCosmLambda + AnmBnm.getBnmBh(j, 4) * pSinmLambda); + b2Bw = b2Bw + (AnmBnm.getAnmBw(j, 4) * pCosmLambda + AnmBnm.getBnmBw(j, 4) * pSinmLambda); + b2Ch = b2Ch + (AnmBnm.getAnmCh(j, 4) * pCosmLambda + AnmBnm.getBnmCh(j, 4) * pSinmLambda); + b2Cw = b2Cw + (AnmBnm.getAnmCw(j, 4) * pCosmLambda + AnmBnm.getBnmCw(j, 4) * pSinmLambda); + + j = j + 1; + } + } + + // Eq. 6 + final double bh = computeSeasonalFit(dofyear, a0Bh, a1Bh, a2Bh, b1Bh, b2Bh); + final double bw = computeSeasonalFit(dofyear, a0Bw, a1Bw, a2Bw, b1Bw, b2Bw); + final double ch = computeSeasonalFit(dofyear, a0Ch, a1Ch, a2Ch, b1Ch, b2Ch); + final double cw = computeSeasonalFit(dofyear, a0Cw, a1Cw, a2Cw, b1Cw, b2Cw); + + // Compute Mapping Function Eq. 4 + final double[] function = new double[2]; + function[0] = TroposphericModelUtils.mappingFunction(a.getAh(), bh, ch, + trackingCoordinates.getElevation()); + function[1] = TroposphericModelUtils.mappingFunction(a.getAw(), bw, cw, + trackingCoordinates.getElevation()); + + return function; + } + + /** {@inheritDoc} */ + @Override + public > T[] mappingFactors(final FieldTrackingCoordinates trackingCoordinates, + final FieldGeodeticPoint point, + final FieldPressureTemperatureHumidity weather, + final FieldAbsoluteDate date) { + final Field field = date.getField(); + final T zero = field.getZero(); + + // a coefficients + final FieldViennaACoefficients a = getAProvider().getA(point, date); + + // Day of year computation + final int dofyear = getDayOfYear(date.toAbsoluteDate()); + + // Compute Legendre Polynomials Pnm(cos(0.5 * pi - phi)) + final int degree = 12; + final int order = 12; + final FieldLegendrePolynomials p = new FieldLegendrePolynomials<>(degree, order, FastMath.cos(point.getLatitude().negate().add(zero.getPi().multiply(0.5)))); + + // Compute coefficients bh, bw, ch and cw with spherical harmonics + T a0Bh = zero; + T a0Bw = zero; + T a0Ch = zero; + T a0Cw = zero; + T a1Bh = zero; + T a1Bw = zero; + T a1Ch = zero; + T a1Cw = zero; + T b1Bh = zero; + T b1Bw = zero; + T b1Ch = zero; + T b1Cw = zero; + T a2Bh = zero; + T a2Bw = zero; + T a2Ch = zero; + T a2Cw = zero; + T b2Bh = zero; + T b2Bw = zero; + T b2Ch = zero; + T b2Cw = zero; + final LegendreFunctions AnmBnm = new LegendreFunctions(); + int j = 0; + for (int n = 0; n <= 12; n++) { + for (int m = 0; m <= n; m++) { + final FieldSinCos sc = FastMath.sinCos(point.getLongitude().multiply(m)); + final T pCosmLambda = p.getPnm(n, m).multiply(sc.cos()); + final T pSinmLambda = p.getPnm(n, m).multiply(sc.sin()); + + a0Bh = a0Bh.add(pCosmLambda.multiply(AnmBnm.getAnmBh(j, 0)).add(pSinmLambda.multiply(AnmBnm.getBnmBh(j, 0)))); + a0Bw = a0Bw.add(pCosmLambda.multiply(AnmBnm.getAnmBw(j, 0)).add(pSinmLambda.multiply(AnmBnm.getBnmBw(j, 0)))); + a0Ch = a0Ch.add(pCosmLambda.multiply(AnmBnm.getAnmCh(j, 0)).add(pSinmLambda.multiply(AnmBnm.getBnmCh(j, 0)))); + a0Cw = a0Cw.add(pCosmLambda.multiply(AnmBnm.getAnmCw(j, 0)).add(pSinmLambda.multiply(AnmBnm.getBnmCw(j, 0)))); + + a1Bh = a1Bh.add(pCosmLambda.multiply(AnmBnm.getAnmBh(j, 1)).add(pSinmLambda.multiply(AnmBnm.getBnmBh(j, 1)))); + a1Bw = a1Bw.add(pCosmLambda.multiply(AnmBnm.getAnmBw(j, 1)).add(pSinmLambda.multiply(AnmBnm.getBnmBw(j, 1)))); + a1Ch = a1Ch.add(pCosmLambda.multiply(AnmBnm.getAnmCh(j, 1)).add(pSinmLambda.multiply(AnmBnm.getBnmCh(j, 1)))); + a1Cw = a1Cw.add(pCosmLambda.multiply(AnmBnm.getAnmCw(j, 1)).add(pSinmLambda.multiply(AnmBnm.getBnmCw(j, 1)))); + + b1Bh = b1Bh.add(pCosmLambda.multiply(AnmBnm.getAnmBh(j, 2)).add(pSinmLambda.multiply(AnmBnm.getBnmBh(j, 2)))); + b1Bw = b1Bw.add(pCosmLambda.multiply(AnmBnm.getAnmBw(j, 2)).add(pSinmLambda.multiply(AnmBnm.getBnmBw(j, 2)))); + b1Ch = b1Ch.add(pCosmLambda.multiply(AnmBnm.getAnmCh(j, 2)).add(pSinmLambda.multiply(AnmBnm.getBnmCh(j, 2)))); + b1Cw = b1Cw.add(pCosmLambda.multiply(AnmBnm.getAnmCw(j, 2)).add(pSinmLambda.multiply(AnmBnm.getBnmCw(j, 2)))); + + a2Bh = a2Bh.add(pCosmLambda.multiply(AnmBnm.getAnmBh(j, 3)).add(pSinmLambda.multiply(AnmBnm.getBnmBh(j, 3)))); + a2Bw = a2Bw.add(pCosmLambda.multiply(AnmBnm.getAnmBw(j, 3)).add(pSinmLambda.multiply(AnmBnm.getBnmBw(j, 3)))); + a2Ch = a2Ch.add(pCosmLambda.multiply(AnmBnm.getAnmCh(j, 3)).add(pSinmLambda.multiply(AnmBnm.getBnmCh(j, 3)))); + a2Cw = a2Cw.add(pCosmLambda.multiply(AnmBnm.getAnmCw(j, 3)).add(pSinmLambda.multiply(AnmBnm.getBnmCw(j, 3)))); + + b2Bh = b2Bh.add(pCosmLambda.multiply(AnmBnm.getAnmBh(j, 4)).add(pSinmLambda.multiply(AnmBnm.getBnmBh(j, 4)))); + b2Bw = b2Bw.add(pCosmLambda.multiply(AnmBnm.getAnmBw(j, 4)).add(pSinmLambda.multiply(AnmBnm.getBnmBw(j, 4)))); + b2Ch = b2Ch.add(pCosmLambda.multiply(AnmBnm.getAnmCh(j, 4)).add(pSinmLambda.multiply(AnmBnm.getBnmCh(j, 4)))); + b2Cw = b2Cw.add(pCosmLambda.multiply(AnmBnm.getAnmCw(j, 4)).add(pSinmLambda.multiply(AnmBnm.getBnmCw(j, 4)))); + + j = j + 1; + } + } + + // Eq. 6 + final T bh = computeSeasonalFit(dofyear, a0Bh, a1Bh, a2Bh, b1Bh, b2Bh); + final T bw = computeSeasonalFit(dofyear, a0Bw, a1Bw, a2Bw, b1Bw, b2Bw); + final T ch = computeSeasonalFit(dofyear, a0Ch, a1Ch, a2Ch, b1Ch, b2Ch); + final T cw = computeSeasonalFit(dofyear, a0Cw, a1Cw, a2Cw, b1Cw, b2Cw); + + // Compute Mapping Function Eq. 4 + final T[] function = MathArrays.buildArray(field, 2); + function[0] = TroposphericModelUtils.mappingFunction(a.getAh(), bh, ch, + trackingCoordinates.getElevation()); + function[1] = TroposphericModelUtils.mappingFunction(a.getAw(), bw, cw, + trackingCoordinates.getElevation()); + + return function; + } + + /** Computes the empirical temporal information for the mapping function + * coefficients b and c. A seasonal fit formula is performed. + * @param doy day of year + * @param A0 Mean value of the coefficient + * @param A1 Annual amplitude of the coefficient + * @param A2 Semi-annual amplitude of the coefficient + * @param B1 Annual amplitude of the coefficient + * @param B2 Semi-annual amplitude of the coefficient + * @return the mapping function coefficient at a given day. + */ + private double computeSeasonalFit(final int doy, final double A0, final double A1, + final double A2, final double B1, final double B2) { + + final double coef = (doy / 365.25) * 2 * FastMath.PI; + final SinCos sc1 = FastMath.sinCos(coef); + final SinCos sc2 = FastMath.sinCos(2.0 * coef); + + return A0 + + A1 * sc1.cos() + B1 * sc1.sin() + + A2 * sc2.cos() + B2 * sc2.sin(); + } + + /** Computes the empirical temporal information for the mapping function + * coefficients b and c. A seasonal fit formula is performed. + * @param type of the elements + * @param doy day of year + * @param A0 Mean value of the coefficient + * @param A1 Annual amplitude of the coefficient + * @param A2 Semi-annual amplitude of the coefficient + * @param B1 Annual amplitude of the coefficient + * @param B2 Semi-annual amplitude of the coefficient + * @return the mapping function coefficient at a given day. + */ + private > T computeSeasonalFit(final int doy, final T A0, final T A1, + final T A2, final T B1, final T B2) { + final T coef = A0.getPi().multiply(2.0).multiply(doy / 365.25); + final FieldSinCos sc1 = FastMath.sinCos(coef); + final FieldSinCos sc2 = FastMath.sinCos(coef.multiply(2.0)); + + return A0.add( + A1.multiply(sc1.cos())).add(B1.multiply(sc1.sin())).add( + A2.multiply(sc2.cos())).add(B2.multiply(sc2.sin())); + } + + /** Class for the values of the coefficients anm and bnm + * of the spherical harmonics expansion. + */ + private static class LegendreFunctions { + + /** Legendre functions anm for coefficient bh.*/ + private static final double[][] BH_A = { + {0.00271285863109945, -1.39197786008938e-06, 1.34955672002719e-06, 2.71686279717968e-07, 1.56659301773925e-06}, + {9.80476624811974e-06, -5.83922611260673e-05, -2.07307023860417e-05, 1.14628726961148e-06, 4.93610283608719e-06}, + {-1.03443106534268e-05, -2.05536138785961e-06, 2.09692641914244e-06, -1.55491034130965e-08, -1.89706404675801e-07}, + {-3.00353961749658e-05, 2.37284447073503e-05, 2.02236885378918e-05, 1.69276006349609e-06, 8.72156681243892e-07}, + {-7.99121077044035e-07, -5.39048313389504e-06, -4.21234502039861e-06, -2.70944149806894e-06, -6.80894455531746e-07}, + {7.51439609883296e-07, 3.85509708865520e-07, 4.41508016098164e-08, -2.07507808307757e-08, 4.95354985050743e-08}, + {2.21790962160087e-05, -5.56986238775212e-05, -1.81287885563308e-05, -4.41076013532589e-06, 4.93573223917278e-06}, + {-4.47639989737328e-06, -2.60452893072120e-06, 2.56376320011189e-06, 4.41600992220479e-07, 2.93437730332869e-07}, + {8.14992682244945e-07, 2.03945571424434e-07, 1.11832498659806e-08, 3.25756664234497e-08, 3.01029040414968e-08}, + {-7.96927680907488e-08, -3.66953150925865e-08, -6.74742632186619e-09, -1.30315731273651e-08, -2.00748924306947e-09}, + {-2.16138375166934e-05, 1.67350317962556e-05, 1.93768260076821e-05, 1.99595120161850e-06, -2.42463528222014e-06}, + {5.34360283708044e-07, -3.64189022040600e-06, -2.99935375194279e-06, -2.06880962903922e-06, -9.40815692626002e-07}, + {6.80235884441822e-07, 1.33023436079845e-07, -1.80349593705226e-08, 2.51276252565192e-08, -1.43240592002794e-09}, + {-7.13790897253802e-08, 7.81998506267559e-09, 1.13826909570178e-09, -5.89629600214654e-09, -4.20760865522804e-09}, + {-5.80109372399116e-09, 1.13702284491976e-09, 7.29046067602764e-10, -9.10468988754012e-10, -2.58814364808642e-10}, + {1.75558618192965e-05, -2.85579168876063e-05, -1.47442190284602e-05, -6.29300414335248e-06, -5.12204538913460e-07}, + {-1.90788558291310e-06, -1.62144845155361e-06, 7.57239241641566e-07, 6.93365788711348e-07, 6.88855644570695e-07}, + {2.27050351488552e-07, 1.03925791277660e-07, -3.31105076632079e-09, 2.88065761026675e-08, -8.00256848229136e-09}, + {-2.77028851807614e-08, -5.96251132206930e-09, 2.95987495527251e-10, -5.87644249625625e-09, -3.28803981542337e-09}, + {-1.89918479865558e-08, 3.54083436578857e-09, 8.10617835854935e-10, 4.99207055948336e-10, -1.52691648387663e-10}, + {1.04022499586096e-09, -2.36437143845013e-10, -2.25110813484842e-10, -7.39850069252329e-11, 7.95929405440911e-11}, + {-3.11579421267630e-05, -3.43576336877494e-06, 5.81663608263384e-06, 8.31534700351802e-07, 4.02619520312154e-06}, + {6.00037066879001e-07, -1.12538760056168e-07, -3.86745332115590e-07, -3.88218746020826e-07, -6.83764967176388e-07}, + {-9.79583981249316e-08, 9.14964449851003e-08, 4.77779838549237e-09, 2.44283811750703e-09, -6.26361079345158e-09}, + {-2.37742207548109e-08, -5.53336301671633e-09, -3.73625445257115e-09, -1.92304189572886e-09, -7.18681390197449e-09}, + {-6.58203463929583e-09, 9.28456148541896e-10, 2.47218904311077e-10, 1.10664919110218e-10, -4.20390976974043e-11}, + {9.45857603373426e-10, -3.29683402990254e-11, -8.15440375865127e-11, -1.21615589356628e-12, -9.70713008848085e-12}, + {1.61377382316176e-10, 6.84326027598147e-12, -4.66898885683671e-12, 2.31211355085535e-12, 2.39195112937346e-12}, + {2.99634365075821e-07, 8.14391615472128e-06, 6.70458490942443e-06, -9.92542646762000e-07, -3.04078064992750e-06}, + {-6.52697933801393e-07, 2.87255329776428e-07, -1.78227609772085e-08, 2.65525429849935e-07, 8.60650570551813e-08}, + {-1.62727164011710e-07, 1.09102479325892e-07, 4.97827431850001e-09, 7.86649963082937e-11, -6.67193813407656e-09}, + {-2.96370000987760e-09, 1.20008401576557e-09, 1.75885448022883e-09, -1.74756709684384e-09, 3.21963061454248e-09}, + {-9.91101697778560e-10, 7.54541713140752e-10, -2.95880967800875e-10, 1.81009160501278e-10, 8.31547411640954e-11}, + {1.21268051949609e-10, -5.93572774509587e-11, -5.03295034994351e-11, 3.05383430975252e-11, 3.56280438509939e-11}, + {6.92012970333794e-11, -9.02885345797597e-12, -3.44151832744880e-12, 2.03164894681921e-12, -5.44852265137606e-12}, + {5.56731263672800e-12, 3.57272150106101e-12, 2.25885622368678e-12, -2.44508240047675e-13, -6.83314378535235e-13}, + {3.96883487797254e-06, -4.57100506169608e-06, -3.30208117813256e-06, 3.32599719134845e-06, 4.26539325549339e-06}, + {1.10123151770973e-06, 4.58046760144882e-07, 1.86831972581926e-07, -1.60092770735081e-07, -5.58956114867062e-07}, + {-3.40344900506653e-08, 2.87649741373047e-08, -1.83929753066251e-08, -9.74179203885847e-09, -2.42064137485043e-09}, + {-6.49731596932566e-09, -3.07048108404447e-09, -2.84380614669848e-09, 1.55123146524283e-09, 4.53694984588346e-10}, + {5.45175793803325e-10, -3.73287624700125e-10, -1.16293122618336e-10, 7.25845618602690e-11, -4.34112440021627e-11}, + {1.89481447552805e-10, 3.67431482211078e-12, -1.72180065021194e-11, 1.47046319023226e-11, 1.31920481414062e-11}, + {2.10125915737167e-12, -3.08420783495975e-12, -4.87748712363020e-12, 1.16363599902490e-14, 1.26698255558605e-13}, + {-8.07894928696254e-12, 9.19344620512607e-13, 3.26929173307443e-13, 2.00438149416495e-13, -9.57035765212079e-15}, + {1.38737151773284e-12, 1.09340178371420e-13, 5.15714202449053e-14, -5.92156438588931e-14, -3.29586752336143e-14}, + {6.38137197198254e-06, 4.62426300749908e-06, 4.42334454191034e-06, 1.15374736092349e-06, -2.61859702227253e-06}, + {-2.25320619636149e-07, 3.21907705479353e-07, -3.34834530764823e-07, -4.82132753601810e-07, -3.22410936343355e-07}, + {3.48894515496995e-09, 3.49951261408458e-08, -6.01128959281142e-09, 4.78213900943443e-09, 1.46012816168576e-08}, + {-9.66682871952083e-11, 3.75806627535317e-09, 2.38984004956705e-09, 2.07545049877203e-09, 1.58573595632766e-09}, + {1.06834370693917e-09, -4.07975055112153e-10, -2.37598937943957e-10, 5.89327007480137e-11, 1.18891820437634e-10}, + {5.22433722695807e-11, 6.02011995016293e-12, -7.80605402956048e-12, 1.50873145627341e-11, -1.40550093106311e-12}, + {2.13396242187279e-13, -1.71939313965536e-12, -3.57625378660975e-14, -5.01675184988446e-14, -1.07805487368797e-12}, + {-1.24352330043311e-12, 8.26105883301606e-13, 4.63606970128517e-13, 6.39517888984486e-14, -7.35135439920086e-14}, + {-5.39023859065631e-13, 2.54188315588243e-14, 1.30933833278664e-14, 6.06153473304781e-15, -4.24722717533726e-14}, + {3.12767756884813e-14, -2.29517847871632e-15, 2.53117304424948e-16, 7.07504914138118e-16, -1.20089065310688e-15}, + {2.08311178819214e-06, -1.22179185044174e-06, -2.98842190131044e-06, 3.07310218974299e-06, 2.27100346036619e-06}, + {-3.94601643855452e-07, -5.44014825116083e-07, -6.16955333162507e-08, -2.31954821580670e-07, 1.14010813005310e-07}, + {6.11067575043044e-08, -3.93240193194272e-08, -1.62979132528933e-08, 1.01339204652581e-08, 1.97319601566071e-08}, + {2.57770508710055e-09, 1.87799543582899e-09, 1.95407654714372e-09, 1.15276419281270e-09, 2.25397005402120e-09}, + {7.16926338026236e-10, -3.65857693313858e-10, -1.54864067050915e-11, 6.50770211276549e-11, -7.85160007413546e-12}, + {4.90007693914221e-12, 3.31649396536340e-12, 4.81664871165640e-13, 7.26080745617085e-12, 2.30960953372164e-12}, + {9.75489202240545e-13, -1.68967954531421e-13, 7.38383391334110e-13, -3.58435515913239e-13, -3.01564710027450e-13}, + {-3.79533601922805e-13, 2.76681830946617e-13, 1.21480375553803e-13, -1.57729077644850e-14, -8.87664977818700e-14}, + {-3.96462845480288e-14, 2.94155690934610e-14, 6.78413205760717e-15, -4.12135802787361e-15, -1.46373307795619e-14}, + {-8.64941937408121e-15, -1.91822620970386e-15, -8.01725413560744e-16, 5.02941051180784e-16, -1.07572628474344e-15}, + {-4.13816294742758e-15, -7.43602019785880e-17, -5.54248556346072e-17, -4.83999456005158e-17, -1.19622559730466e-16}, + {-8.34852132750364e-07, -7.45794677612056e-06, -6.58132648865533e-06, -1.38608110346732e-06, 5.32326534882584e-07}, + {-2.75513802414150e-07, 3.64713745106279e-08, -7.12385417940442e-08, -7.86206067228882e-08, 2.28048393207161e-08}, + {-4.26696415431918e-08, -4.65599668635087e-09, 7.35037936327566e-09, 1.17098354115804e-08, 1.44594777658035e-08}, + {1.12407689274199e-09, 7.62142529563709e-10, -6.72563708415472e-10, -1.18094592485992e-10, -1.17043815733292e-09}, + {1.76612225246125e-10, -1.01188552503192e-10, 7.32546072616968e-11, 1.79542821801610e-11, -2.23264859965402e-11}, + {-9.35960722512375e-12, 1.90894283812231e-12, -6.34792824525760e-13, 3.98597963877826e-12, -4.47591409078971e-12}, + {-3.34623858556099e-12, 4.56384903915853e-14, 2.72561108521416e-13, -3.57942733300468e-15, 1.99794810657713e-13}, + {-6.16775522568954e-14, 8.25316968328823e-14, 7.19845814260518e-14, -2.92415710855106e-14, -5.49570017444031e-15}, + {-8.50728802453217e-15, 8.38161600916267e-15, 3.43651657459983e-15, -8.19429434115910e-16, -4.08905746461100e-15}, + {4.39042894275548e-15, -3.69440485320477e-16, 1.22249256876779e-16, -2.09359444520984e-16, -3.34211740264257e-16}, + {-5.36054548134225e-16, 3.29794204041989e-17, 2.13564354374585e-17, -1.37838993720865e-18, -1.29188342867753e-17}, + {-3.26421841529845e-17, 7.38235405234126e-18, 2.49291659676210e-18, 8.18252735459593e-19, 1.73824952279230e-20}, + {4.67237509268208e-06, 1.93611283787239e-06, 9.39035455627622e-07, -5.84565118072823e-07, -1.76198705802101e-07}, + {-3.33739157421993e-07, 4.12139555299163e-07, 1.58754695700856e-07, 1.37448753329669e-07, 1.04722936936873e-07}, + {6.64200603076386e-09, 1.45412222625734e-08, 1.82498796118030e-08, 2.86633517581614e-09, 1.06066984548100e-09}, + {5.25549696746655e-09, -1.33677183394083e-09, 7.60804375937931e-11, -1.07918624219037e-10, 8.09178898247941e-10}, + {1.89318454110039e-10, 9.23092164791765e-11, 5.51434573131180e-11, 3.86696392289240e-11, -1.15208165047149e-11}, + {-1.02252706006226e-12, -7.25921015411136e-13, -1.98110126887620e-12, -2.18964868282672e-13, -7.18834476685625e-13}, + {-2.69770025318548e-12, -2.17850340796321e-14, 4.73040820865871e-13, 1.57947421572149e-13, 1.86925164972766e-13}, + {1.07831718354771e-13, 2.26681841611017e-14, 2.56046087047783e-14, -1.14995851659554e-14, -2.27056907624485e-14}, + {6.29825154734712e-15, 8.04458225889001e-16, 9.53173540411138e-16, 1.16892301877735e-15, -1.04324684545047e-15}, + {-5.57345639727027e-16, -2.93949227634932e-16, 7.47621406284534e-18, -5.36416885470756e-17, -2.87213280230513e-16}, + {1.73219775047208e-16, 2.05017387523061e-17, 9.08873886345587e-18, -2.86881547225742e-18, -1.25303645304992e-17}, + {-7.30829109684568e-18, 2.03711261415353e-18, 7.62162636124024e-19, -7.54847922012517e-19, -8.85105098195030e-19}, + {5.62039968280587e-18, -1.38144206573507e-19, 1.68028711767211e-20, 1.81223858251981e-19, -8.50245194985878e-20} + }; + + /** Legendre functions anm for coefficient bw.*/ + private static final double[][] BW_A = { + {0.00136127467401223, -6.83476317823061e-07, -1.37211986707674e-06, 7.02561866200582e-07, -2.16342338010651e-07}, + {-9.53197486400299e-06, 6.58703762338336e-06, 2.42000663952044e-06, -6.04283463108935e-07, 2.02144424676990e-07}, + {-6.76728911259359e-06, 6.03830755085583e-07, -8.72568628835897e-08, 2.21750344140938e-06, 1.05146032931020e-06}, + {-3.21102832397338e-05, -7.88685357568093e-06, -2.55495673641049e-06, -1.99601934456719e-06, -4.62005252198027e-07}, + {-7.84639263523250e-07, 3.11624739733849e-06, 9.02170019697389e-07, 6.37066632506008e-07, -9.44485038780872e-09}, + {2.19476873575507e-06, -2.20580510638233e-07, 6.94761415598378e-07, 4.80770865279717e-07, -1.34357837196401e-07}, + {2.18469215148328e-05, -1.80674174262038e-06, -1.52754285605060e-06, -3.51212288219241e-07, 2.73741237656351e-06}, + {2.85579058479116e-06, 1.57201369332361e-07, -2.80599072875081e-07, -4.91267304946072e-07, -2.11648188821805e-07}, + {2.81729255594770e-06, 3.02487362536122e-07, -1.64836481475431e-07, -2.11607615408593e-07, -6.47817762225366e-08}, + {1.31809947620223e-07, -1.58289524114549e-07, -7.05580919885505e-08, 5.56781440550867e-08, 1.23403290710365e-08}, + {-1.29252282695869e-05, -1.07247072037590e-05, -3.31109519638196e-06, 2.13776673779736e-06, -1.49519398373391e-07}, + {1.81685152305722e-06, -1.17362204417861e-06, -3.19205277136370e-08, 4.09166457255416e-07, 1.53286667406152e-07}, + {1.63477723125362e-06, -2.68584775517243e-08, 4.94662064805191e-09, -7.09027987928288e-08, 4.44353430574937e-08}, + {-2.13090618917978e-07, 4.05836983493219e-08, 2.94495876336549e-08, -1.75005469063176e-08, -3.03015988647002e-09}, + {-2.16074435298006e-09, 9.37631708987675e-09, -2.05996036369828e-08, 6.97068002894092e-09, -8.90988987979604e-09}, + {1.38047798906967e-05, 2.05528261553901e-05, 1.59072148872708e-05, 7.34088731264443e-07, 1.28226710383580e-06}, + {7.08175753966264e-07, -9.27988276636505e-07, 1.60535820026081e-07, -3.27296675122065e-07, -2.20518321170684e-07}, + {1.90932483086199e-07, -7.44215272759193e-08, 1.81330673333187e-08, 4.37149649043616e-08, 4.18884335594172e-08}, + {-5.37009063880924e-08, 2.22870057779431e-08, 1.73740123037651e-08, -4.45137302235032e-09, 9.44721910524571e-09}, + {-6.83406949047909e-08, -1.95046676795923e-10, 2.57535903049686e-09, 4.82643164083020e-09, 3.37657333705158e-09}, + {3.96128688448981e-09, -6.63809403270686e-10, 2.44781464212534e-10, 5.92280853590699e-11, -4.78502591970721e-10}, + {1.75859399041414e-05, -2.81238050668481e-06, -2.43670534594848e-06, 3.58244562699714e-06, -1.76547446732691e-06}, + {-1.06451311473304e-07, 1.54336689617184e-06, -2.00690000442673e-07, 1.38790047911880e-09, -1.62490619890017e-07}, + {-2.72757421686155e-07, 1.71139266205398e-07, -2.55080309401917e-08, -8.40793079489831e-09, -1.01129447760167e-08}, + {2.92966025844079e-08, -2.07556718857313e-08, 5.45985315647905e-09, 8.76857690274150e-09, 1.06785510440474e-08}, + {-1.22059608941331e-08, 6.52491630264276e-09, -1.79332492326928e-10, 3.75921793745396e-10, -7.06416506254786e-10}, + {1.63224355776652e-09, 4.95586028736232e-10, -3.07879011759040e-10, -7.78354087544277e-11, 1.43959047067250e-10}, + {3.86319414653663e-10, -2.06467134617933e-10, 4.37330971382694e-11, -5.00421056263711e-11, -9.40237773015723e-12}, + {-1.23856142706451e-05, 7.61047394008415e-06, -1.99104114578138e-07, 6.86177748886858e-07, -1.09466747592827e-07}, + {2.99866062403128e-07, 1.87525561397390e-07, 4.99374806994715e-08, 4.86229763781404e-07, 4.46570575517658e-07}, + {-5.05748332368430e-07, 1.95523624722285e-08, -9.17535435911345e-08, -2.56671607433547e-08, -7.11896201616653e-08}, + {-2.66062200406494e-08, -5.40470019739274e-09, -2.29718660244954e-09, -3.73328592264404e-09, 3.38748313712376e-09}, + {5.30855327954894e-10, 5.28851845648032e-10, -2.22278913745418e-10, -5.52628653064771e-11, -9.24825145219684e-10}, + {6.03737227573716e-10, -3.52190673510919e-12, -1.30371720641414e-10, -9.12787239944822e-12, 6.42187285537238e-12}, + {1.78081862458539e-10, 2.93772078656037e-12, -1.04698379945322e-11, -2.82260024833024e-11, -5.61810459067525e-12}, + {9.35003092299580e-12, -8.23133834521577e-13, 5.54878414224198e-13, -3.62943215777181e-13, 2.38858933771653e-12}, + {-1.31216096107331e-05, -5.70451670731759e-06, -5.11598683573971e-06, -4.99990779887599e-06, 1.27389320221511e-07}, + {-1.23108260369048e-06, 5.53093245213587e-07, 8.60093183929302e-07, 2.65569700925696e-07, 1.95485134805575e-07}, + {-2.29647072638049e-07, -5.45266515081825e-08, 2.85298129762263e-08, 1.98167939680185e-08, 5.52227340898335e-09}, + {-2.73844745019857e-08, -4.48345173291362e-10, -1.93967347049382e-09, -1.41508853776629e-09, -1.75456962391145e-09}, + {-2.68863184376108e-11, -2.20546981683293e-09, 6.56116990576877e-10, 1.27129855674922e-10, -2.32334506413213e-10}, + {1.98303136881156e-10, 6.04782006047075e-11, 2.91291115431570e-11, 6.18098615782757e-11, -3.82682292530379e-11}, + {9.48294455071158e-12, -3.05873596453015e-13, 5.31539408055057e-13, -7.31016438665600e-12, -1.19921002209198e-11}, + {-2.25188050845725e-11, -3.91627574966393e-13, -6.80217235976769e-13, 5.91033607278405e-13, 5.02991534452191e-13}, + {1.29532063896247e-12, 1.66337285851564e-13, 3.25543028344555e-13, 1.89143357962363e-13, 3.32288378169726e-13}, + {-2.45864358781728e-06, 4.49460524898260e-06, 1.03890496648813e-06, -2.73783420376785e-06, 7.12695730642593e-07}, + {-9.27805078535168e-07, -4.97733876686731e-07, 9.18680298906510e-08, -2.47200617423980e-07, 6.16163630140379e-08}, + {-1.39623661883136e-08, -1.12580495666505e-07, 2.61821435950379e-08, -2.31875562002885e-08, 5.72679835033659e-08}, + {-9.52538983318497e-09, -5.40909215302433e-09, 1.88698793952475e-09, -4.08127746406372e-09, 1.09534895853812e-10}, + {3.79767457525741e-09, 1.11549801373366e-10, -6.45504957274111e-10, 3.05477141010356e-10, 1.26261210565856e-10}, + {5.08813577945300e-11, 1.43250547678637e-11, 8.81616572082448e-12, 2.58968878880804e-11, 3.83421818249954e-11}, + {8.95094368142044e-12, -3.26220304555971e-12, -1.28047847191896e-12, 2.67562170258942e-12, 2.72195031576670e-12}, + {-6.47181697409757e-12, 1.13776457455685e-12, 2.84856274334969e-13, -7.63667272085395e-14, -1.34451657758826e-13}, + {-1.25291265888343e-12, 8.63500441050317e-14, -1.21307856635548e-13, 5.12570529540511e-14, 3.32389276976573e-14}, + {3.73573418085813e-14, -5.37808783042784e-16, -4.23430408270850e-16, -4.75110565740493e-15, 6.02553212780166e-15}, + {8.95483987262751e-06, -3.90778212666235e-06, -1.12115019808259e-06, 1.78678942093383e-06, 1.46806344157962e-06}, + {-4.59185232678613e-07, 1.09497995905419e-07, 1.31663977640045e-07, 4.20525791073626e-08, -9.71470741607431e-08}, + {1.63399802579572e-07, 1.50909360648645e-08, -1.11480472593347e-08, -1.84000857674573e-08, 7.82124614794256e-09}, + {1.22887452385094e-08, -4.06647399822746e-10, -6.49120327585597e-10, 8.63651225791194e-10, -2.73440085913102e-09}, + {2.51748630889583e-09, 4.79895880425564e-10, -2.44908073860844e-10, 2.56735882664876e-10, -1.64815306286912e-10}, + {4.85671381736718e-11, -2.51742732115131e-11, -2.60819437993179e-11, 6.12728324086123e-12, 2.16833310896138e-11}, + {4.11389702320298e-12, -8.09433180989935e-13, -1.19812498226024e-12, 1.46885737888520e-12, 3.15807685137836e-12}, + {-1.47614580597013e-12, 4.66726413909320e-13, 1.72089709006255e-13, 1.13854935381418e-13, 2.77741161317003e-13}, + {-1.02257724967727e-13, 1.10394382923502e-13, -3.14153505370805e-15, 2.41103099110106e-14, 2.13853053149771e-14}, + {-3.19080885842786e-14, -9.53904307973447e-15, 2.74542788156379e-15, 2.33797859107844e-15, -2.53192474907304e-15}, + {-5.87702222126367e-15, -1.80133850930249e-15, -3.09793125614454e-16, -1.04197538975295e-16, 3.72781664701327e-16}, + {1.86187054729085e-06, 8.33098045333428e-06, 3.18277735484232e-06, -7.68273797022231e-07, -1.52337222261696e-06}, + {-5.07076646593648e-07, -8.61959553442156e-07, -3.51690005432816e-07, -4.20797082902431e-07, -3.07652993252673e-07}, + {-7.38992472164147e-08, -8.39473083080280e-08, -2.51587083298935e-08, 7.30691259725451e-09, -3.19457155958983e-08}, + {-1.99777182012924e-09, -3.21265085916022e-09, -4.84477421865675e-10, -1.82924814205799e-09, -3.46664344655997e-10}, + {-7.05788559634927e-11, 1.21840735569025e-10, 7.97347726425926e-11, 1.08275679614409e-10, -1.17891254809785e-10}, + {1.10299718947774e-11, -3.22958261390263e-11, -1.43535798209229e-11, 6.87096504209595e-12, -6.64963212272352e-12}, + {-6.47393639740084e-12, 1.03156978325120e-12, -9.20099775082358e-14, -2.40150316641949e-13, 1.14008812047857e-12}, + {-1.23957846397250e-13, 2.85996703969692e-13, 1.91579874982553e-13, 5.20597174693064e-14, -4.06741434883370e-14}, + {-2.35479068911236e-14, 1.97847338186993e-14, 1.58935977518516e-15, -2.32217195254742e-15, -8.48611789490575e-15}, + {1.03992320391626e-14, 1.54017082092642e-15, 1.05950035082788e-16, -1.17870898461353e-15, -1.10937420707372e-15}, + {-1.09011948374520e-15, -6.04168007633584e-16, -9.10901998157436e-17, 1.98379116989461e-16, -1.03715496658498e-16}, + {-1.38171942108278e-16, -6.33037999097522e-17, -1.38777695011470e-17, 1.94191397045401e-17, 5.70055906754485e-18}, + {1.92989406002085e-06, -3.82662130483128e-06, -4.60189561036048e-07, 2.24290587856309e-06, 1.40544379451550e-06}, + {6.49033717633394e-08, 2.41396114435326e-07, 2.73948898223321e-07, 1.10633664439332e-07, -3.19555270171075e-08}, + {-2.91988966963297e-08, -6.03828192816571e-09, 1.18462386444840e-08, 1.32095545004128e-08, -5.06572721528914e-09}, + {7.31079058474148e-09, -8.42775299751834e-10, 1.10190810090667e-09, 1.96592273424306e-09, -2.13135932785688e-09}, + {7.06656405314388e-11, 1.43441125783756e-10, 1.46962246686924e-10, 7.44592776425197e-11, -3.64331892799173e-11}, + {-2.52393942119372e-11, 1.07520964869263e-11, 5.84669886072094e-12, 6.52029744217103e-12, 1.82947123132059e-12}, + {-4.15669940115121e-12, -1.95963254053648e-13, 2.16977822834301e-13, -2.84701408462031e-13, 4.27194601040231e-13}, + {3.07891105454129e-13, 1.91523190672955e-13, 1.05367297580989e-13, -5.28136363920236e-14, -3.53364110005917e-14}, + {7.02156663274738e-15, 9.52230536780849e-15, -3.41019408682733e-15, -3.59825303352899e-15, -2.62576411636150e-15}, + {-1.75110277413804e-15, 5.29265220719483e-16, 4.45015980897919e-16, -3.80179856341347e-16, -4.32917763829695e-16}, + {1.16038609651443e-16, -6.69643574373352e-17, 2.65667154817303e-17, -9.76010333683956e-17, 4.07312981076655e-17}, + {5.72659246346386e-18, 1.30357528108671e-18, 2.49193258417535e-18, 1.76247014075584e-18, 7.59614374197688e-19}, + {1.03352170833303e-17, -2.30633516638829e-18, 2.84777940620193e-18, -7.72161347944693e-19, 6.07028034506380e-19} + }; + + /** Legendre functions anm for coefficient ch.*/ + private static final double[][] CH_A = { + {0.0571481238161787, 3.35402081801137e-05, 3.15988141788728e-05, -1.34477341887086e-05, -2.61831023577773e-07}, + {5.77367395845715e-05, -0.000669057185209558, -6.51057691648904e-05, -1.61830149147091e-06, 8.96771209464758e-05}, + {-8.50773002452907e-05, -4.87106614880272e-05, 4.03431160775277e-05, 2.54090162741464e-06, -5.59109319864264e-06}, + {0.00150536423187709, 0.000611682258892697, 0.000369730024614855, -1.95658439780282e-05, -3.46246726553700e-05}, + {-2.32168718433966e-05, -0.000127478686553809, -9.00292451740728e-05, -6.07834315901830e-05, -1.04628419422714e-05}, + {-1.38607250922551e-06, -3.97271603842309e-06, -8.16155320152118e-07, 5.73266706046665e-07, 2.00366060212696e-07}, + {6.52491559188663e-05, -0.00112224323460183, -0.000344967958304075, -7.67282640947300e-05, 0.000107907110551939}, + {-0.000138870461448036, -7.29995695401936e-05, 5.35986591445824e-05, 9.03804869703890e-06, 8.61370129482732e-06}, + {-9.98524443968768e-07, -6.84966792665998e-08, 1.47478021860771e-07, 1.94857794008064e-06, 7.17176852732910e-07}, + {1.27066367911720e-06, 1.12113289164288e-06, 2.71525688515375e-07, -2.76125723009239e-07, -1.05429690305013e-07}, + {-0.000377264999981652, 0.000262691217024294, 0.000183639785837590, 3.93177048515576e-06, -6.66187081899168e-06}, + {-4.93720951871921e-05, -0.000102820030405771, -5.69904376301748e-05, -3.79603438055116e-05, -3.96726017834930e-06}, + {-2.21881958961135e-06, -1.40207117987894e-06, 1.60956630798516e-07, 2.06121145135022e-06, 6.50944708093149e-07}, + {2.21876332411271e-07, 1.92272880430386e-07, -6.44016558013941e-09, -1.40954921332410e-07, -4.26742169137667e-07}, + {-3.51738525149881e-08, 2.89616194332516e-08, -3.40343352397886e-08, -2.89763392721812e-08, -6.40980581663785e-10}, + {3.51240856823468e-05, -0.000725895015345786, -0.000322514037108045, -0.000106143759981636, 4.08153152459337e-05}, + {-2.36269716929413e-05, -4.20691836557932e-05, 1.43926743222922e-05, 2.61811210631784e-05, 2.09610762194903e-05}, + {-7.91765756673890e-07, 1.64556789159745e-06, -9.43930166276555e-07, 6.46641738736139e-07, -5.91509547299176e-07}, + {3.92768838766879e-07, -1.98027731703690e-07, -5.41303590057253e-08, -4.21705797874207e-07, -6.06042329660681e-08}, + {-1.56650141024305e-08, 7.61808165752027e-08, -1.81900460250934e-08, 1.30196216971675e-08, 1.08616031342379e-08}, + {-2.80964779829242e-08, -7.25951488826103e-09, -2.59789823306225e-09, -2.79271942407154e-09, 4.10558774868586e-09}, + {-0.000638227857648286, -0.000154814045363391, 7.78518327501759e-05, -2.95961469342381e-05, 1.15965225055757e-06}, + {4.47833146915112e-06, 1.33712284237555e-05, 3.61048816552123e-06, -2.50717844073547e-06, -1.28100822021734e-05}, + {-2.26958070007455e-06, 2.57779960912242e-06, 1.08395653197976e-06, 1.29403393862805e-07, -1.04854652812567e-06}, + {-3.98954043463392e-07, -2.26931182815454e-07, -1.09169545045028e-07, -1.49509536031939e-07, -3.98376793949903e-07}, + {2.30418911071110e-08, 1.23098508481555e-08, -1.71161401463708e-08, 2.35829696577657e-09, 1.31136164162040e-08}, + {3.69423793101582e-09, 3.49231027561927e-10, -1.18581468768647e-09, 5.43180735828820e-10, 5.43192337651588e-10}, + {-1.38608847117992e-09, -1.86719145546559e-10, -8.13477384765498e-10, 2.01919878240491e-10, 1.00067892622287e-10}, + {-4.35499078415956e-05, 0.000450727967957804, 0.000328978494268850, -3.05249478582848e-05, -3.21914834544310e-05}, + {1.24887940973241e-05, 1.34275239548403e-05, 1.11275518344713e-06, 7.46733554562851e-06, -2.12458664760353e-06}, + {9.50250784948476e-07, 2.34367372695203e-06, -5.43099244798980e-07, -4.35196904508734e-07, -8.31852234345897e-07}, + {5.91775478636535e-09, -1.48970922508592e-07, 2.99840061173840e-08, -1.30595933407792e-07, 1.27136765045597e-07}, + {-1.78491083554475e-08, 1.76864919393085e-08, -1.96740493482011e-08, 1.21096708004261e-08, 2.95518703155064e-10}, + {1.75053510088658e-09, -1.31414287871615e-09, -1.44689439791928e-09, 1.14682483668460e-09, 1.74488616540169e-09}, + {1.08152964586251e-09, -3.85678162063266e-10, -2.77851016629979e-10, 3.89890578625590e-11, -2.54627365853495e-10}, + {-1.88340955578221e-10, 5.19645384002867e-11, 2.14131326027631e-11, 1.24027770392728e-11, -9.42818962431967e-12}, + {0.000359777729843898, -0.000111692619996219, -6.87103418744904e-05, 0.000115128973879551, 7.59796247722486e-05}, + {5.23717968000879e-05, 1.32279078116467e-05, -5.72277317139479e-07, -7.56326558610214e-06, -1.95749622214651e-05}, + {1.00109213210139e-06, -2.75515216592735e-07, -1.13393194050846e-06, -4.75049734870663e-07, -3.21499480530932e-07}, + {-2.07013716598890e-07, -7.31392258077707e-08, -3.96445714084160e-08, 3.21390452929387e-08, -1.43738764991525e-08}, + {2.03081434931767e-09, -1.35423687136122e-08, -4.47637454261816e-09, 2.18409121726643e-09, -3.74845286805217e-09}, + {3.17469255318367e-09, 2.44221027314129e-10, -2.46820614760019e-10, 7.55851003884434e-10, 6.98980592550891e-10}, + {9.89541493531067e-11, -2.78762878057315e-11, -2.10947962916771e-10, 3.77882267360636e-11, -1.20009542671532e-12}, + {5.01720575730940e-11, 1.66470417102135e-11, -7.50624817938091e-12, 9.97880221482238e-12, 4.87141864438892e-12}, + {2.53137945301589e-11, 1.93030083090772e-12, -1.44708804231290e-12, -1.77837100743423e-12, -8.10068935490951e-13}, + {0.000115735341520738, 0.000116910591048350, 8.36315620479475e-05, 1.61095702669207e-05, -7.53084853489862e-05}, + {-9.76879433427199e-06, 9.16968438003335e-06, -8.72755127288830e-06, -1.30077933880053e-05, -9.78841937993320e-06}, + {1.04902782517565e-07, 2.14036988364936e-07, -7.19358686652888e-07, 1.12529592946332e-07, 7.07316352860448e-07}, + {7.63177265285080e-08, 1.22781974434290e-07, 8.99971272969286e-08, 5.63482239352990e-08, 4.31054352285547e-08}, + {3.29855763107355e-09, -6.95004336734441e-09, -6.52491370576354e-09, 1.97749180391742e-09, 3.51941791940498e-09}, + {3.85373745846559e-10, 1.65754130924183e-10, -3.31326088103057e-10, 5.93256024580436e-10, 1.27725220636915e-10}, + {-1.08840956376565e-10, -4.56042860268189e-11, -4.77254322645633e-12, -2.94405398621875e-12, -3.07199979999475e-11}, + {2.07389879095010e-11, 1.51186798732451e-11, 9.28139802941848e-12, 5.92738269687687e-12, 9.70337402306505e-13}, + {-2.85879708060306e-12, 1.92164314717053e-13, 4.02664678967890e-14, 5.18246319204277e-13, -7.91438726419423e-13}, + {6.91890667590734e-13, -8.49442290988352e-14, -5.54404947212402e-15, 9.71093377538790e-15, -5.33714333415971e-14}, + {-5.06132972789792e-05, -4.28348772058883e-05, -6.90746551020305e-05, 8.48380415176836e-05, 7.04135614675053e-05}, + {-1.27945598849788e-05, -1.92362865537803e-05, -2.30971771867138e-06, -8.98515975724166e-06, 5.25675205004752e-06}, + {-8.71907027470177e-07, -1.02091512861164e-06, -1.69548051683864e-07, 4.87239045855761e-07, 9.13163249899837e-07}, + {-6.23651943425918e-08, 6.98993315829649e-08, 5.91597766733390e-08, 4.36227124230661e-08, 6.45321798431575e-08}, + {-1.46315079552637e-10, -7.85142670184337e-09, 1.48788168857903e-09, 2.16870499912160e-09, -1.16723047065545e-09}, + {3.31888494450352e-10, 1.90931898336457e-10, -3.13671901557599e-11, 2.60711798190524e-10, 8.45240112207997e-11}, + {1.36645682588537e-11, -5.68830303783976e-12, 1.57518923848140e-11, -1.61935794656758e-11, -4.16568077748351e-12}, + {9.44684950971905e-13, 7.30313977131995e-12, 3.14451447892684e-12, 6.49029875639842e-13, -9.66911019905919e-13}, + {-8.13097374090024e-13, 5.23351897822186e-13, 8.94349188113951e-14, -1.33327759673270e-13, -4.04549450989029e-13}, + {-3.76176467005839e-14, -6.19953702289713e-14, -3.74537190139726e-14, 1.71275486301958e-14, -3.81946773167132e-14}, + {-4.81393385544160e-14, 3.66084990006325e-15, 3.10432030972253e-15, -4.10964475657416e-15, -6.58644244242900e-15}, + {-7.81077363746945e-05, -0.000254773632197303, -0.000214538508009518, -3.80780934346726e-05, 1.83495359193990e-05}, + {5.89140224113144e-06, -3.17312632433258e-06, -3.81872516710791e-06, -2.27592226861647e-06, 1.57044619888023e-06}, + {-1.44272505088690e-06, -1.10236588903758e-07, 2.64336813084693e-07, 4.76074163332460e-07, 4.28623587694570e-07}, + {3.98889120733904e-08, -1.29638005554027e-08, -4.13668481273828e-08, 1.27686793719542e-09, -3.54202962042383e-08}, + {1.60726837551750e-09, -2.70750776726156e-09, 2.79387092681070e-09, -3.01419734793998e-10, -1.29101669438296e-10}, + {-2.55708290234943e-10, 2.27878015173471e-11, -6.43063443462716e-12, 1.26531554846856e-10, -1.65822147437220e-10}, + {-3.35886470557484e-11, -3.51895009091595e-12, 5.80698399963198e-12, -2.84881487149207e-12, 8.91708061745902e-12}, + {-3.12788523950588e-12, 3.35366912964637e-12, 2.52236848033838e-12, -8.12801050709184e-13, -2.63510394773892e-13}, + {6.83791881183142e-14, 2.41583263270381e-13, 8.58807794189356e-14, -5.12528492761045e-14, -1.40961725631276e-13}, + {-1.28585349115321e-14, -2.11049721804969e-14, 5.26409596614749e-15, -4.31736582588616e-15, -1.60991602619068e-14}, + {-9.35623261461309e-15, -3.94384886372442e-16, 5.04633016896942e-16, -5.40268998456055e-16, -1.07857944298104e-15}, + {8.79756791888023e-16, 4.52529935675330e-16, 1.36886341163227e-16, -1.12984402980452e-16, 6.30354561057224e-18}, + {0.000117829256884757, 2.67013591698442e-05, 2.57913446775250e-05, -4.40766244878807e-05, -1.60651761172523e-06}, + {-1.87058092029105e-05, 1.34371169060024e-05, 5.59131416451555e-06, 4.50960364635647e-06, 2.87612873904633e-06}, + {2.79835536517287e-07, 8.93092708148293e-07, 8.37294601021795e-07, -1.99029785860896e-08, -8.87240405168977e-08}, + {4.95854313394905e-08, -1.44694570735912e-08, 2.51662229339375e-08, -3.87086600452258e-09, 2.29741919071270e-08}, + {4.71497840986162e-09, 2.47509999454076e-09, 1.67323845102824e-09, 8.14196768283530e-10, -3.71467396944165e-10}, + {-1.07340743907054e-10, -8.07691657949326e-11, -5.99381660248133e-11, 2.33173929639378e-12, -2.26994195544563e-11}, + {-3.83130441984224e-11, -5.82499946138714e-12, 1.43286311435124e-11, 3.15150503353387e-12, 5.97891025146774e-12}, + {-5.64389191072230e-13, 9.57258316335954e-13, 1.12055192185939e-12, -4.42417706775420e-13, -9.93190361616481e-13}, + {1.78188860269677e-13, 7.82582024904950e-14, 5.18061650118009e-14, 2.13456507353387e-14, -5.26202113779510e-14}, + {-8.18481324740893e-15, -3.71256746886786e-15, 4.23508855164371e-16, -2.91292502923102e-15, -1.15454205389350e-14}, + {6.16578691696810e-15, 6.74087154080877e-16, 5.71628946437034e-16, -2.05251213979975e-16, -7.25999138903781e-16}, + {9.35481959699383e-17, 6.23535830498083e-17, 3.18076728802060e-18, -2.92353209354587e-17, 7.65216088665263e-19}, + {2.34173078531701e-17, -8.30342420281772e-18, -4.33602329912952e-18, 1.90226281379981e-18, -7.85507922718903e-19} + }; + + /** Legendre functions anm for coefficient cw.*/ + private static final double[][] CW_A = { + {0.0395329695826997, -0.000131114380761895, -0.000116331009006233, 6.23548420410646e-05, 5.72641113425116e-05}, + {-0.000441837640880650, 0.000701288648654908, 0.000338489802858270, 3.76700309908602e-05, -8.70889013574699e-06}, + {1.30418530496887e-05, -0.000185046547597376, 4.31032103066723e-05, 0.000105583334124319, 3.23045436993589e-05}, + {3.68918433448519e-05, -0.000219433014681503, 3.46768613485000e-06, -9.17185187163528e-05, -3.69243242456081e-05}, + {-6.50227201116778e-06, 2.07614874282187e-05, -5.09131314798362e-05, -3.08053225174359e-05, -4.18483655873918e-05}, + {2.67879176459056e-05, -6.89303730743691e-05, 2.11046783217168e-06, 1.93163912538178e-05, -1.97877143887704e-06}, + {0.000393937595007422, -0.000452948381236406, -0.000136517846073846, 0.000138239247989489, 0.000133175232977863}, + {5.00214539435002e-05, 3.57229726719727e-05, -9.38010547535432e-07, -3.52586798317563e-05, -7.01218677681254e-06}, + {3.91965314099929e-05, 1.02236686806489e-05, -1.95710695226022e-05, -5.93904795230695e-06, 3.24339769876093e-06}, + {6.68158778290653e-06, -8.10468752307024e-06, -9.91192994096109e-06, -1.89755520007723e-07, -3.26799467595579e-06}, + {0.000314196817753895, -0.000296548447162009, -0.000218410153263575, -1.57318389871000e-05, 4.69789570185785e-05}, + {0.000104597721123977, -3.31000119089319e-05, 5.60326793626348e-05, 4.71895007710715e-05, 3.57432326236664e-05}, + {8.95483021572039e-06, 1.44019305383365e-05, 4.87912790492931e-06, -3.45826387853503e-06, 3.23960320438157e-06}, + {-1.35249651009930e-05, -2.49349762695977e-06, -2.51509483521132e-06, -9.14254874104858e-07, -8.57897406100890e-07}, + {-1.68143325235195e-06, 1.72073417594235e-06, 1.38765993969565e-06, 4.09770982137530e-07, -6.60908742097123e-07}, + {-0.000639889366487161, 0.00120194042474696, 0.000753258598887703, 3.87356377414663e-05, 1.31231811175345e-05}, + {2.77062763606783e-05, -9.51425270178477e-06, -6.61068056107547e-06, -1.38713669012109e-05, 9.84662092961671e-06}, + {-2.69398078539471e-06, 6.50860676783123e-06, 3.80855926988090e-06, -1.98076068364785e-06, 1.17187335666772e-06}, + {-2.63719028151905e-06, 5.03149473656743e-07, 7.38964893399716e-07, -8.38892485369078e-07, 1.30943917775613e-06}, + {-1.56634992245479e-06, -2.97026487417045e-08, 5.06602801102463e-08, -4.60436007958792e-08, -1.62536449440997e-07}, + {-2.37493912770935e-07, 1.69781593069938e-08, 8.35178275224265e-08, -4.83564044549811e-08, -4.96448864199318e-08}, + {0.00134012259587597, -0.000250989369253194, -2.97647945512547e-05, -6.47889968094926e-05, 8.41302130716859e-05}, + {-0.000113287184900929, 4.78918993866293e-05, -3.14572113583139e-05, -2.10518256626847e-05, -2.03933633847417e-05}, + {-4.97413321312139e-07, 3.72599822034753e-06, -3.53221588399266e-06, -1.05232048036416e-06, -2.74821498198519e-06}, + {4.81988542428155e-06, 4.21400219782474e-07, 1.02814808667637e-06, 4.40299068486188e-09, 3.37103399036634e-09}, + {1.10140301678818e-08, 1.90257670180182e-07, -1.00831353341885e-08, 1.44860642389714e-08, -5.29882089987747e-08}, + {6.12420414245775e-08, -4.48953461152996e-09, -1.38837603709003e-08, -2.05533675904779e-08, 1.49517908802329e-09}, + {9.17090243673643e-10, -9.24878857867367e-09, -2.30856560363943e-09, -4.36348789716735e-09, -4.45808881183025e-10}, + {-0.000424912699609112, -0.000114365438471564, -0.000403200981827193, 4.19949560550194e-05, -3.02068483713739e-05}, + {3.85435472851225e-05, -5.70726887668306e-05, 4.96313706308613e-07, 1.02395703617082e-05, 5.85550000567006e-06}, + {-7.38204470183331e-06, -4.56638770109511e-06, -3.94007992121367e-06, -2.16666812189101e-06, -4.55694264113194e-06}, + {5.89841165408527e-07, 1.40862905173449e-08, 1.08149086563211e-07, -2.18592601537944e-07, -3.78927431428119e-07}, + {4.85164687450468e-08, 8.34273921293655e-08, 1.47489605513673e-08, 6.01494125001291e-08, 6.43812884159484e-09}, + {1.13055580655363e-08, 3.50568765400469e-09, -5.09396162501750e-09, -1.83362063152411e-09, -4.11227251553035e-09}, + {3.16454132867156e-09, -1.39634794131087e-09, -7.34085003895929e-10, -7.55541371271796e-10, -1.57568747643705e-10}, + {1.27572900992112e-09, -3.51625955080441e-10, -4.84132020565098e-10, 1.52427274930711e-10, 1.27466120431317e-10}, + {-0.000481655666236529, -0.000245423313903835, -0.000239499902816719, -0.000157132947351028, 5.54583099258017e-05}, + {-1.52987254785589e-05, 2.78383892116245e-05, 4.32299123991860e-05, 1.70981319744327e-05, -1.35090841769225e-06}, + {-8.65400907717798e-06, -6.51882656990376e-06, -2.43810171017369e-07, 8.54348785752623e-07, 2.98371863248143e-07}, + {-1.68155571776752e-06, -3.53602587563318e-07, -1.00404435881759e-07, -2.14162249012859e-08, -2.42131535531526e-07}, + {-1.08048603277187e-08, -9.78850785763030e-08, -2.32906554437417e-08, 2.22003630858805e-08, -2.27230368089683e-09}, + {-5.98864391551041e-09, 7.38970926486848e-09, 3.61322835311957e-09, 3.70037329172919e-09, -3.41121137081362e-09}, + {-7.33113754909726e-10, -9.08374249335220e-11, -1.78204392133739e-10, 8.28618491929026e-11, -1.32966817912373e-10}, + {-5.23340481314676e-10, 1.36403528233346e-10, -7.04478837151279e-11, -6.83175201536443e-12, -2.86040864071134e-12}, + {3.75347503578356e-11, -1.08518134138781e-11, -2.53583751744508e-12, 1.00168232812303e-11, 1.74929602713312e-11}, + {-0.000686805336370570, 0.000591849814585706, 0.000475117378328026, -2.59339398048415e-05, 3.74825110514968e-05}, + {3.35231363034093e-05, 2.38331521146909e-05, 7.43545963794093e-06, -3.41430817541849e-06, 7.20180957675353e-06}, + {3.60564374432978e-07, -3.13300039589662e-06, -6.38974746108020e-07, -8.63985524672024e-07, 2.43367665208655e-06}, + {-4.09605238516094e-07, -2.51158699554904e-07, -1.29359217235188e-07, -2.27744642483133e-07, 7.04065989970205e-08}, + {6.74886341820129e-08, -1.02009407061935e-08, -3.30790296448812e-08, 1.64959795655031e-08, 1.40641779998855e-08}, + {1.31706886235108e-09, -1.06243701278671e-09, -2.85573799673944e-09, 3.72566568681289e-09, 2.48402582003925e-09}, + {-3.68427463251097e-11, -1.90028122983781e-10, -3.98586561768697e-11, 1.14458831693287e-11, -2.27722300377854e-12}, + {-7.90029729611056e-11, 3.81213646526419e-11, 4.63303426711788e-11, 1.52294835905903e-11, -2.99094751490726e-12}, + {-2.36146602045017e-11, 1.03852674709985e-11, -4.47242126307100e-12, 5.30884113537806e-12, 1.68499023262969e-12}, + {-3.30107358134527e-13, -4.73989085379655e-13, 5.17199549822684e-13, 2.34951744478255e-13, 2.05931351608192e-13}, + {0.000430215687511780, -0.000132831373000014, -3.41830835017045e-05, 4.70312161436033e-06, -3.84807179340006e-05}, + {1.66861163032403e-05, -8.10092908523550e-06, 8.20658107437905e-06, 6.12399025026683e-06, -1.85536495631911e-06}, + {1.53552093641337e-06, 2.19486495660361e-06, -1.07253805120137e-06, -4.72141767909137e-07, 4.00744581573216e-07}, + {2.56647305130757e-07, -8.07492046592274e-08, -2.05858469296168e-07, 1.09784168930599e-07, -7.76823030181225e-08}, + {1.77744008115031e-08, 1.64134677817420e-08, 4.86163044879020e-09, 1.13334251800856e-08, -7.17260621115426e-09}, + {1.61133063219326e-09, -1.85414677057024e-09, -2.13798537812651e-09, 1.15255123229679e-09, 2.24504700129464e-09}, + {1.23344223096739e-10, -1.20385012169848e-10, -2.18038256346433e-12, 3.23033120628279e-11, 8.01179568213400e-11}, + {-6.55745274387847e-12, 1.22127104697198e-11, 5.83805016355883e-12, -8.31201582509817e-12, 1.90985373872656e-12}, + {-2.89199983667265e-12, 5.05962500506667e-12, 1.28092925110279e-12, 5.60353813743813e-13, 1.76753731968770e-12}, + {-1.61678729774956e-13, -3.92206170988615e-13, -9.04941327579237e-14, 1.89847694200763e-13, 4.10008676756463e-14}, + {-1.16808369005656e-13, -9.97464591430510e-14, 7.46366550245722e-15, 2.53398578153179e-14, 1.06510689748906e-14}, + {-0.000113716921384790, -0.000131902722651488, -0.000162844886485788, 7.90171538739454e-06, -0.000178768066961413}, + {-2.13146535366500e-06, -3.57818705543597e-05, -1.50825855069298e-05, -2.17909259570022e-05, -8.19332236308581e-06}, + {-2.88001138617357e-06, -2.09957465440793e-06, 6.81466526687552e-08, 3.58308906974448e-07, -4.18502067223724e-07}, + {-1.10761444317605e-07, 6.91773860777929e-08, 8.17125372450372e-08, -2.16476237959181e-08, 7.59221970502074e-08}, + {-9.56994224818941e-09, 6.64104921728432e-09, 6.33077902928348e-09, 2.85721181743727e-09, -6.39666681678123e-09}, + {4.62558627839842e-10, -1.69014863754621e-09, -2.80260429599733e-10, 4.27558937623863e-11, -1.66926133269027e-10}, + {-7.23385132663753e-11, 5.51961193545280e-11, 3.04070791942335e-11, 3.23227055919062e-12, 8.47312431934829e-11}, + {-1.61189613765486e-11, 1.66868155925172e-11, 1.05370341694715e-11, -4.41495859079592e-12, -2.24939051401750e-12}, + {-8.72229568056267e-13, 1.88613726203286e-12, 1.21711137534390e-14, -1.13342372297867e-12, -6.87151975256052e-13}, + {7.99311988544090e-15, 4.46150979586709e-14, 7.50406779454998e-14, -3.20385428942275e-14, -1.26543636054393e-14}, + {4.80503817699514e-14, -3.35545623603729e-14, -1.18546423610485e-14, 4.19419209985980e-15, -1.73525614436880e-14}, + {-1.20464898830163e-15, -8.80752065000456e-16, -1.22214298993313e-15, 1.69928513019657e-15, 1.93593051311405e-16}, + {1.68528879784841e-05, 3.57144412031081e-05, -1.65999910125077e-05, 5.40370336805755e-05, 0.000118138122851376}, + {-3.28151779115881e-05, 1.04231790790798e-05, -2.80761862890640e-06, 2.98996152515593e-06, -2.67641158709985e-06}, + {-2.08664816151978e-06, -1.64463884697475e-06, 6.79099429284834e-08, 7.23955842946495e-07, -6.86378427465657e-07}, + {-2.88205823027255e-09, 2.38319699493291e-09, 1.14169347509045e-07, 8.12981074994402e-08, -1.56957943666988e-07}, + {-7.09711403570189e-09, 6.29470515502988e-09, 3.50833306577579e-09, 8.31289199649054e-09, -2.14221463168338e-09}, + {-8.11910123910038e-10, 3.34047829618955e-10, 3.70619377446490e-10, 3.30426088213373e-10, 4.86297305597865e-11}, + {1.98628160424161e-11, -4.98557831380098e-12, -5.90523187802174e-12, -1.27027116925122e-12, 1.49982368570355e-11}, + {2.62289263262748e-12, 3.91242360693861e-12, 6.56035499387192e-12, -1.17412941089401e-12, -9.40878197853394e-13}, + {-3.37805010124487e-13, 5.39454874299593e-13, -2.41569839991525e-13, -2.41572016820792e-13, -3.01983673057198e-13}, + {-1.85034053857964e-13, 4.31132161871815e-14, 4.13497222026824e-15, -4.60075514595980e-14, -1.92454846400146e-14}, + {2.96113888929854e-15, -1.11688534391626e-14, 3.76275373238932e-15, -3.72593295948136e-15, 1.98205490249604e-16}, + {1.40074667864629e-15, -5.15564234798333e-16, 3.56287382196512e-16, 5.07242777691587e-16, -2.30405782826134e-17}, + {2.96822530176851e-16, -4.77029898301223e-17, 1.12782285532775e-16, 1.58443229778573e-18, 8.22141904662969e-17} + }; + + /** Legendre functions bnm for coefficient bh.*/ + private static final double[][] BH_B = { + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {-2.29210587053658e-06, -2.33805004374529e-06, -7.49312880102168e-07, -5.12022747852006e-07, 5.88926055066172e-07}, + {0, 0, 0, 0, 0}, + {-4.63382754843690e-06, -2.23853015662938e-06, 8.14830531656518e-07, 1.15453269407116e-06, -4.53555450927571e-07}, + {-6.92432096320778e-07, -2.98734455136141e-07, 1.48085153955641e-08, 1.37881746148773e-07, -6.92492118460215e-09}, + {0, 0, 0, 0, 0}, + {-1.91507979850310e-06, -1.83614825459598e-06, -7.46807436870647e-07, -1.28329122348007e-06, 5.04937180063059e-07}, + {-8.07527103916713e-07, 2.83997840574570e-08, -6.01890498063025e-08, -2.48339507554546e-08, 2.46284627824308e-08}, + {-2.82995069303093e-07, 1.38818274596408e-09, 3.22731214161408e-09, 2.87731153972404e-10, 1.53895537278496e-08}, + {0, 0, 0, 0, 0}, + {-6.68210270956800e-07, -2.19104833297845e-06, 1.30116691657253e-07, 4.78445730433450e-07, -4.40344300914051e-07}, + {-2.36946755740436e-07, -1.32730991878204e-07, 1.83669593693860e-08, 7.90218931983569e-08, -4.70161979232584e-08}, + {1.07746083292179e-07, -4.17088637760330e-09, -1.83296035841109e-09, -5.80243971371211e-09, -2.11682361167439e-09}, + {-5.44712355496109e-08, 1.89717032256923e-09, 2.27327316287804e-10, 7.78400728280038e-10, 8.82380487618991e-12}, + {0, 0, 0, 0, 0}, + {-5.61707049615673e-08, -1.09066447089585e-06, -2.25742250174119e-07, -8.64367795924377e-07, 1.06411275240680e-08}, + {2.41782935157918e-08, -3.65762298303819e-08, -6.93420659586875e-08, -3.97316214341991e-08, -2.08767816486390e-08}, + {6.38293030383436e-08, 1.11377936334470e-08, 6.91424941454782e-09, 1.39887159955004e-09, 5.25428749022906e-09}, + {1.09291268489958e-08, 1.23935926756516e-10, 3.92917259954515e-10, -1.79144682483562e-10, -9.11802874917597e-10}, + {-4.40957607823325e-09, 1.45751390560667e-10, 1.24641258165301e-10, -6.45810339804674e-11, -8.92894658893326e-12}, + {0, 0, 0, 0, 0}, + {1.54754294162102e-08, -1.60154742388847e-06, -4.08425188394881e-07, 6.18170290113531e-09, -2.58919765162122e-07}, + {1.37130642286873e-08, -6.67813955828458e-08, -7.01410996605609e-09, 3.82732572660461e-08, -2.73381870915135e-08}, + {2.19113155379218e-08, 4.11027496396868e-09, 6.33816020485226e-09, -1.49242411327524e-09, -6.14224941851705e-10}, + {6.26573021218961e-09, 5.17137416480052e-10, -3.49784328298676e-10, 1.13578756343208e-10, 2.80414613398411e-10}, + {1.65048133258794e-11, 1.00047239417239e-10, 1.05124654878499e-10, -3.03826002621926e-11, 4.57155388334682e-11}, + {6.20221691418381e-11, 9.75852610098156e-12, -5.46716005756984e-12, 1.31643349569537e-11, 3.61618775715470e-12}, + {0, 0, 0, 0, 0}, + {-1.03938913012708e-06, -1.78417431315664e-07, 2.86040141364439e-07, 1.83508599345952e-08, -1.34452220464346e-07}, + {-4.36557481393662e-08, 7.49780206868834e-09, -8.62829428674082e-09, 5.50577793039009e-09, -9.46897502333254e-09}, + {3.43193738406672e-10, 1.13545447306468e-08, 1.25242388852214e-09, 6.03221501959620e-10, 1.57172070361180e-09}, + {-4.73307591021391e-10, 1.70855824051391e-10, -2.62470421477037e-11, 2.04525835988874e-10, -1.17859695928164e-10}, + {-3.36185995299839e-10, 3.19243054562183e-11, 1.17589412418126e-10, -1.35478747434514e-12, 5.11192214558542e-11}, + {3.19640547592136e-11, 2.94297823804643e-12, -1.00651526276990e-11, -1.67028733953153e-12, 3.03938833625503e-12}, + {1.68928641118173e-11, -7.90032886682002e-13, -1.40899773539137e-12, 7.76937592393354e-13, 7.32539820298651e-13}, + {0, 0, 0, 0, 0}, + {2.32949756055277e-07, 1.46237594908093e-07, -1.07770884952484e-07, 1.26824870644476e-07, -2.36345735961108e-08}, + {8.89572676497766e-08, 7.24810004121931e-08, 2.67583556180119e-08, 2.48434796111361e-08, -3.55004782858686e-09}, + {-1.00823909773603e-08, 8.84433929029076e-10, -2.55502517594511e-10, -5.48034274059119e-10, -8.50241938494079e-10}, + {1.13259819566467e-09, 5.55186945221216e-10, 7.63679807785295e-11, -1.70067998092043e-11, 1.57081965572493e-10}, + {-2.37748192185353e-10, 2.45463764948000e-11, 3.23208414802860e-11, -2.72624834520723e-12, 8.14449183666500e-12}, + {-1.54977633126025e-11, 4.58754903157884e-12, -1.25864665839074e-12, 2.44139868157872e-12, -1.82827441958193e-12}, + {3.28285563794513e-12, -1.10072329225465e-12, -7.23470501810935e-13, 5.85309745620389e-13, 4.11317589687125e-13}, + {4.57596974384170e-13, 9.84198128213558e-14, 3.34503817702830e-14, 7.08431086558307e-15, 2.79891177268807e-14}, + {0, 0, 0, 0, 0}, + {-3.67820719155580e-07, 6.98497901205902e-07, 1.83397388750300e-07, 2.39730262495372e-07, -2.58441984368194e-07}, + {5.17793954077994e-08, 5.54614175977835e-08, 1.75026214305232e-09, -2.55518450411346e-09, -6.12272723006537e-09}, + {-7.94292648157198e-09, -1.01709107852895e-09, -1.49251241812310e-09, 9.32827213605682e-10, -8.24490722043118e-10}, + {1.36410408475679e-11, 2.16390220454971e-10, 1.24934806872235e-10, -6.82507825145903e-11, -4.01575177719668e-11}, + {-1.41619917600555e-11, -1.54733230409082e-11, 1.36792829351538e-11, 1.11157862104733e-12, 2.08548465892268e-11}, + {-3.56521723755846e-12, 4.47877185884557e-12, -6.34096209274637e-16, -1.13010624512348e-12, -2.82018136861041e-13}, + {2.22758955943441e-12, -4.63876465559380e-13, -5.80688019272507e-13, 2.45878690598655e-13, 1.49997666808106e-13}, + {-6.26833903786958e-14, 2.73416335780807e-14, 1.91842340758425e-14, 1.67405061129010e-14, -2.45268543953704e-17}, + {1.81972870222228e-14, 5.43036245069085e-15, 1.92476637107321e-15, 8.78498602508626e-17, -1.42581647227657e-15}, + {0, 0, 0, 0, 0}, + {9.74322164613392e-07, -5.23101820582724e-07, -2.81997898176227e-07, 4.54762451707384e-08, -3.34645078118827e-08}, + {-6.75813194549663e-09, 3.49744702199583e-08, -5.09170419895883e-09, 5.24359476874755e-09, 4.96664262534662e-09}, + {4.53858847892396e-10, -1.49347392165963e-09, -2.00939511362154e-09, 9.30987163387955e-10, 9.74450200826854e-11}, + {-4.92900885858693e-10, 5.34223033225688e-12, 1.08501839729368e-10, -6.43526142089173e-11, -3.11063319142619e-11}, + {1.38469246386690e-11, -7.91180584906922e-12, 2.26641656746936e-13, 4.55251515177956e-12, 6.05270575117769e-12}, + {4.02247935664225e-12, 1.82776657951829e-12, -1.28348801405445e-13, -2.16257301300350e-13, -5.54363979435025e-14}, + {4.15005914461687e-13, -2.00647573581168e-13, -1.67278251942946e-13, 1.30332398257985e-13, 1.52742363652434e-13}, + {6.36376500056974e-14, 1.65794532815776e-14, -3.80832559052662e-15, -6.40262894005341e-16, 2.42577181848072e-15}, + {-5.55273521249151e-15, 3.69725182221479e-15, 2.02114207545759e-15, -4.50870833392161e-16, 9.62950493696677e-17}, + {1.00935904205024e-17, 6.54751873609395e-17, -1.09138810997186e-16, -8.62396750098759e-17, -3.82788257844306e-17}, + {0, 0, 0, 0, 0}, + {4.21958510903678e-07, -8.30678271007705e-08, -3.47006439555247e-07, -3.36442823712421e-08, 9.90739768222027e-08}, + {2.64389033612742e-08, 2.65825090066479e-09, -1.28895513428522e-08, -7.07182694980098e-10, 7.10907165301180e-09}, + {6.31203524153492e-09, -1.67038260990134e-09, 1.33104703539822e-09, 8.34376495185149e-10, -2.52478613522612e-10}, + {1.18414896299279e-10, -2.57745052288455e-11, 2.88295935685818e-11, -3.27782977418354e-11, -1.05705000036156e-11}, + {-4.20826459055091e-12, -6.97430607432268e-12, -3.90660545970607e-12, -3.90449239948755e-13, -4.60384797517466e-13}, + {-9.47668356558200e-13, 6.53305025354881e-13, 2.63240185434960e-13, 1.40129115015734e-13, 3.85788887132074e-14}, + {2.23947810407291e-13, 7.35262771548253e-15, -3.83348211931292e-14, 4.20376514344176e-14, 4.26445836468461e-14}, + {-3.88008154470596e-16, 2.28561424667750e-15, -8.73599966653373e-16, 2.14321147947665e-15, 6.38631825071920e-16}, + {-8.62165565535721e-15, 1.79742912149810e-15, 1.01541125038661e-15, -7.91027655831866e-17, -4.06505132825230e-16}, + {-2.35355054392189e-16, -6.13997759731013e-17, -2.73490528665965e-17, 2.63895177155121e-17, -4.47531057245187e-18}, + {6.01909706823530e-17, 5.35520010856833e-18, -2.15530106132531e-18, -2.46778496746231e-18, -7.09947296442799e-19}, + {0, 0, 0, 0, 0}, + {-3.75005956318736e-07, -5.39872297906819e-07, -1.19929654883034e-07, 4.52771083775007e-08, 1.82790552943564e-07}, + {7.82606642505646e-09, -1.68890832383153e-08, -8.45995188378997e-09, 1.42958730598502e-09, 3.21075754133531e-09}, + {4.28818421913782e-09, -1.07501469928219e-09, 8.84086350297418e-10, 9.74171228764155e-10, 8.59877149602304e-12}, + {1.28983712172521e-10, -6.96375160373676e-11, -2.13481436408896e-11, 1.33516375568179e-11, -1.65864626508258e-11}, + {-4.48914384622368e-12, 9.68953616831263e-13, -1.61372463422897e-12, -2.09683563440448e-12, -1.90096826314068e-12}, + {-1.12626619779175e-13, 3.34903159106509e-14, -1.21721528343657e-13, 7.46246339290354e-14, 3.68424909859186e-13}, + {5.08294274367790e-14, 2.83036159977090e-14, 1.48074873486387e-14, -9.59633528834945e-15, -1.26231060951100e-14}, + {-4.01464098583541e-16, 1.97047929526674e-15, -5.29967950447497e-16, -3.59120406619931e-16, 1.69690933982683e-16}, + {-1.73919209873841e-15, 7.52792462841274e-16, 3.65589287101147e-16, -7.79247612043812e-17, -8.24599670368999e-17}, + {-4.61555616150128e-17, 4.94529746019753e-19, -1.09858157212270e-17, 3.95550811124928e-18, 3.23972399884100e-18}, + {-2.27040686655766e-17, -3.27855689001215e-18, -3.30649011116861e-19, 9.08748546536849e-19, 8.92197599890994e-19}, + {5.67241944733762e-18, 3.84449400209976e-19, 1.77668058015537e-19, 2.00432838283455e-20, -2.00801461564767e-19} + }; + + /** Legendre functions bnm for coefficient bw.*/ + private static final double[][] BW_B = { + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {-9.56715196386889e-06, -3.68040633020420e-08, 1.27846786489883e-07, 1.32525487755973e-06, 1.53075361125066e-06}, + {0, 0, 0, 0, 0}, + {-7.17682617983607e-06, 2.89994188119445e-06, -2.97763578173405e-07, 8.95742089134942e-07, 3.44416325304006e-07}, + {-8.02661132285210e-07, 3.66738692077244e-07, -3.02880965723280e-07, 3.54144282036103e-07, -1.68873066391463e-07}, + {0, 0, 0, 0, 0}, + {-2.89640569283461e-06, -7.83566373343614e-07, -8.36667214682577e-07, -7.41891843549121e-07, -9.23922655636489e-08}, + {-1.06144662284862e-06, 1.57709930505924e-07, 1.04203025714319e-07, 1.20783300488461e-07, -1.38726055821134e-07}, + {-4.16549018672265e-07, -1.35220897698872e-07, -6.40269964829901e-08, 1.63258283210837e-08, -2.57958025095959e-08}, + {0, 0, 0, 0, 0}, + {3.52324885892419e-06, -2.26705543513814e-07, 1.53835589488292e-06, -3.75263061267433e-07, 3.69384057396017e-07}, + {-2.06569149157664e-07, -9.36260183227175e-08, -3.55985284353048e-08, -9.13671163891094e-08, 6.93156256562600e-09}, + {1.32437594740782e-07, 4.44349887272663e-08, -3.38192451721674e-08, -3.97263855781102e-08, -1.93087822995800e-09}, + {-1.29595244818942e-07, -1.40852985547683e-08, 1.42587592939760e-09, 7.05779876554001e-09, -1.00996269264535e-08}, + {0, 0, 0, 0, 0}, + {4.06960756215938e-06, -1.97898540226986e-06, 7.21905857553588e-08, -1.19908881538755e-06, -5.67561861536903e-08}, + {6.53369660286999e-08, -2.42818687866392e-07, -1.66203004559493e-08, -2.41512414151897e-08, 4.45426333411018e-08}, + {1.44650670663281e-07, 8.50666367433859e-09, -4.61165612004307e-09, 4.88527987491045e-09, 1.06277326713172e-08}, + {1.86770937103513e-08, -6.44197940288930e-10, -7.60456736846174e-09, -9.97186468682689e-10, 8.73229752697716e-10}, + {-1.00206566229113e-08, 1.33934372663121e-09, 1.41691503439220e-09, 8.72352590578753e-10, -8.04561626629829e-10}, + {0, 0, 0, 0, 0}, + {3.07161843116618e-06, 1.82962085656470e-06, 1.87728623016069e-07, 7.10611617623261e-07, 2.26499092250481e-07}, + {4.50766403064905e-08, -1.67752393078256e-07, 2.47844723639070e-08, -3.56484348424869e-09, -1.56634836636584e-08}, + {3.77011651881090e-08, -7.23045828480496e-09, 5.22995988863761e-09, -1.03740320341306e-09, 4.57839777217789e-09}, + {8.09495635883121e-09, -3.01977244420529e-10, -2.30104544933093e-09, 3.63658580939428e-10, 4.39320811714867e-10}, + {9.37087629961269e-11, 1.00780920426635e-09, 1.28140539913350e-10, -6.65795285522138e-12, 4.71732796198631e-11}, + {-8.88504487069155e-11, -1.63253810435461e-10, 7.22669710644299e-11, 5.64715132584527e-11, -1.08949308197617e-12}, + {0, 0, 0, 0, 0}, + {-2.64054293284174e-07, -2.37611606117256e-06, -1.83671059706264e-06, -3.12199354841993e-07, -1.05598289276114e-07}, + {7.41706968747147e-08, -1.64359098062646e-08, -3.09750224040234e-08, -9.68640079410317e-09, -7.90399057863403e-08}, + {-1.00254376564271e-08, 1.12528248631191e-08, -2.67841549174100e-09, -2.69481819323647e-09, 1.56550607475331e-09}, + {-2.18568129350729e-09, 6.26422056977450e-10, 1.95007291427316e-09, 3.14226463591125e-10, -3.62000388344482e-10}, + {-9.30451291747549e-10, 5.62175549482704e-11, 1.01022849902012e-10, 5.18675856498499e-11, 5.37561696283235e-11}, + {5.33151334468794e-11, 1.07571307336725e-10, -1.31714567944652e-11, -4.17524405900018e-11, -2.16737797893502e-12}, + {4.69916869001309e-11, -4.34516364859583e-12, -6.61054225868897e-12, -5.75845818545368e-12, -2.32180293529175e-12}, + {0, 0, 0, 0, 0}, + {-3.50305843086926e-06, 1.76085131953403e-06, 8.16661224478572e-07, 4.09111042640801e-07, -9.85414469804995e-08}, + {1.44670876127274e-07, -1.41331228923029e-08, -3.06530152369269e-08, -1.46732098927996e-08, -2.30660839364244e-08}, + {-2.00043052422933e-08, 1.72145861031776e-09, 2.13714615094209e-09, 1.02982676689194e-09, -1.64945224692217e-10}, + {1.23552540016991e-09, 1.42028470911613e-09, 8.79622616627508e-10, -7.44465600265154e-10, -7.17124672589442e-11}, + {-6.67749524914644e-10, -5.77722874934050e-11, 3.40077806879472e-11, 4.26176076541840e-11, 8.23189659748212e-11}, + {-4.62771648935992e-11, -7.24005305716782e-13, 1.18233730497485e-12, 5.18156973532267e-12, -1.53329687155297e-12}, + {4.75581699468619e-12, -3.79782291469732e-12, 1.33077109836853e-12, -1.02426020107120e-12, 3.10385019249130e-13}, + {1.66486090578792e-12, 1.08573672403649e-12, 1.26268044166279e-13, -1.23509297742757e-13, -1.81842007284038e-13}, + {0, 0, 0, 0, 0}, + {9.93870680202303e-08, -1.85264736035628e-06, -5.58942734710854e-07, -5.54183448316270e-07, -3.95581289689398e-08}, + {7.88329069002365e-08, 2.04810091451078e-08, 3.74588851000076e-09, 3.42429296613803e-08, -2.00840228416712e-08}, + {-5.93700447329696e-10, -6.57499436973459e-10, -6.90560448220751e-09, 3.56586371051089e-09, 7.33310245621566e-11}, + {-6.38101662363634e-11, 4.23668020216529e-10, -2.43764895979202e-10, -9.31466610703172e-11, -3.17491457845975e-10}, + {1.50943725382470e-11, -6.11641188685078e-11, -4.37018785685645e-11, -2.32871158949602e-11, 4.19757251950526e-11}, + {-1.18165328825853e-11, -9.91299557532438e-13, 6.40908678055865e-14, 2.41049422936434e-12, -8.20746054454953e-14}, + {6.01892101914838e-12, -8.78487122873450e-13, -1.58887481332294e-12, -3.13556902469604e-13, 5.14523727801645e-14}, + {-1.50791729401891e-13, -1.45234807159695e-13, 1.65302377570887e-13, -5.77094211651483e-15, 9.22218953528393e-14}, + {-1.85618902787381e-14, 5.64333811864051e-14, -9.94311377945570e-15, -2.40992156199999e-15, -2.19196760659665e-14}, + {0, 0, 0, 0, 0}, + {-8.16252352075899e-08, 1.61725487723444e-06, 9.55522506715921e-07, 4.02436267433511e-07, -2.80682052597712e-07}, + {7.68684790328630e-09, -5.00940723761353e-09, -2.43640127974386e-08, -2.59119930503129e-08, 3.35015169182094e-08}, + {7.97903115186673e-09, 3.73803883416618e-09, 3.27888334636662e-09, 1.37481300578804e-09, -1.10677168734482e-10}, + {-1.67853012769912e-09, -1.61405252173139e-10, -1.98841576520056e-10, -1.46591506832192e-11, 9.35710487804660e-11}, + {4.08807084343221e-11, -3.74514169689568e-11, -3.03638493323910e-11, -5.02332555734577e-12, -8.03417498408344e-12}, + {6.48922619024579e-12, 1.96166891023817e-12, -1.96968755122868e-12, -5.20970156382361e-12, -1.62656885103402e-12}, + {1.28603518902875e-12, -4.88146958435109e-13, -3.37034886991840e-13, 1.37393696103000e-14, 4.41398325716943e-14}, + {1.48670014793021e-13, 4.41636026364555e-14, 2.06210477976005e-14, -3.43717583585390e-14, -1.21693704024213e-14}, + {-1.67624180330244e-14, 6.59317111144238e-15, 2.57238525440646e-15, -3.21568425020512e-17, 5.29659568026553e-15}, + {7.85453466393227e-16, 6.91252183915939e-16, -1.20540764178454e-15, -3.85803892583301e-16, 3.46606994632006e-16}, + {0, 0, 0, 0, 0}, + {2.86710087625579e-06, -1.68179842305865e-06, -8.48306772016870e-07, -7.08798062479598e-07, -1.27469453733635e-07}, + {2.11824305734993e-09, 2.02274279084379e-08, 1.61862253091554e-08, 3.25597167111807e-08, 3.40868964045822e-09}, + {1.21757111431438e-08, 1.68405530472906e-09, 1.55379338018638e-09, -3.81467795805531e-10, 2.53316405545058e-09}, + {-9.98413758659768e-11, 5.38382145421318e-10, 3.92629628330704e-10, -1.43067134097778e-10, 3.74959329667113e-12}, + {-1.57270407028909e-11, -9.02797202317592e-12, 8.45997059887690e-12, 4.71474382524218e-12, 5.41880986596427e-12}, + {-1.20658618702054e-12, 7.12940685593433e-13, 1.02148613026937e-12, 1.63063852348169e-13, 1.74048793197708e-13}, + {3.80559390991789e-13, 1.19678271353485e-13, 9.72859455604188e-14, 5.42642400031729e-14, 8.18796710714586e-14}, + {-4.69629218656902e-14, 5.59889038686206e-15, 2.05363292795059e-15, 5.38599403288686e-15, -2.68929559474202e-15}, + {-1.88759348081742e-14, 5.20975954705924e-15, -4.43585653096395e-16, 5.57436617793556e-16, -3.95922805817677e-16}, + {-9.80871456373282e-16, 2.50857658461759e-17, -1.24253000050963e-16, 6.00857065211394e-17, 3.53799635311500e-18}, + {2.49370713054872e-16, -1.49119714269816e-17, -3.12276052640583e-17, -2.42001662334001e-17, -1.69766504318143e-17}, + {0, 0, 0, 0, 0}, + {-1.69222102455713e-06, 1.64277906173064e-06, 5.28855114364096e-07, 4.28159853268650e-07, -1.57362445882665e-07}, + {1.67656782413678e-08, -3.77746114074055e-08, -2.21564555842165e-08, -3.37071806992217e-08, 1.47454008739800e-08}, + {1.06080499491408e-08, 3.21990403709678e-09, 3.87301757435359e-09, 2.92241827834347e-10, -1.86619473655742e-11}, + {1.62399669665839e-10, 3.51322865845172e-10, 2.67086377702958e-11, -1.31596563625491e-10, 3.14164569507034e-11}, + {-2.02180016657259e-11, 2.03305178342732e-11, 6.34969032565839e-12, 5.99522296668787e-12, -4.46275273451008e-12}, + {-9.88409290158885e-13, -1.47692750858224e-13, 3.14655550730530e-13, -2.41857189187879e-13, 4.47727504501486e-13}, + {1.71430777754854e-13, 1.73950835042486e-13, 5.92323956541558e-14, 8.06625710171825e-15, 2.33252485755634e-14}, + {-1.74184545690134e-15, -8.18003353124179e-16, -6.62369006497819e-16, 4.16303374396147e-15, 7.06513748014024e-15}, + {-6.02936238677014e-15, 1.89241084885229e-15, 1.99097881944270e-17, -6.99974290696640e-16, -2.69504942597709e-17}, + {-4.65632962602379e-16, 3.70281995445114e-18, -9.04232973763345e-17, 2.20847370761932e-17, 7.62909453726566e-17}, + {-6.25921477907943e-17, -2.10532795609842e-17, -1.03808073867183e-17, 1.15091380049019e-18, 4.66794445408388e-19}, + {9.39427013576903e-18, 9.17044662931859e-19, 2.04132745117549e-18, -1.72364063154625e-19, -1.18098896532163e-18} + }; + + /** Legendre functions bnm for coefficient ch.*/ + private static final double[][] CH_B = { + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {3.44092035729033e-05, -1.21876825440561e-05, -1.87490665238967e-05, -2.60980336247863e-05, 4.31639313264615e-06}, + {0, 0, 0, 0, 0}, + {-2.60125613000133e-05, 1.70570295762269e-05, 3.08331896996832e-05, 1.66256596588688e-05, -1.07841055501996e-05}, + {8.74011641844073e-06, -2.25874169896607e-06, 6.50985196673747e-07, 1.30424765493752e-06, -1.85081244549542e-07}, + {0, 0, 0, 0, 0}, + {3.77496505484964e-05, -1.08198973553337e-05, -1.67717574544937e-05, -3.22476096673598e-05, 1.12281888201134e-05}, + {-7.68623378647958e-07, -4.01400837153063e-06, -2.16390246700835e-06, -1.76912959937924e-06, -1.12740084951955e-06}, + {-2.37092815818895e-06, -9.52317223759653e-07, -2.22722065579131e-07, -6.25157619772530e-08, 1.86582003894639e-08}, + {0, 0, 0, 0, 0}, + {-6.10254317785872e-05, -2.51815503068494e-05, 2.01046207874667e-05, 7.21107723367308e-06, -1.30692058660457e-05}, + {-9.60655417241537e-06, -7.31381721742373e-06, -2.52767927589636e-06, 9.09039973214621e-07, -6.76454911344246e-07}, + {-2.25743206384908e-08, 2.33058746737575e-07, 2.24746779293445e-07, 6.78551351968876e-08, 1.25076011387284e-07}, + {-2.25744112770133e-07, -1.44429560891636e-07, -2.96810417448652e-08, -5.93858519742856e-08, -2.43210229455420e-08}, + {0, 0, 0, 0, 0}, + {7.45721015256308e-06, -3.81396821676410e-05, -1.41086198468687e-05, -2.28514517574713e-05, 7.28638705683277e-06}, + {-5.77517778169692e-06, -3.93061211403839e-06, -2.17369763310752e-06, -1.48060935583664e-07, -2.74200485662814e-07}, + {4.52962035878238e-07, 9.80990375495214e-07, 4.67492045269286e-07, -8.31032252212116e-09, 1.69426023427740e-07}, + {7.20536791795515e-10, 2.75612253452141e-09, 2.47772119382536e-09, 4.30621825021233e-09, -2.86498479499428e-08}, + {-2.46253956492716e-08, -3.10300833499669e-09, 8.06559148724445e-09, 2.98197408430123e-10, 6.32503656532846e-09}, + {0, 0, 0, 0, 0}, + {-6.01147094179306e-05, -3.16631758509869e-05, 4.10038115100010e-06, 3.55215057231403e-07, -2.23606515237408e-06}, + {-2.85937516921923e-06, -3.67775706610630e-06, -5.06445540401637e-07, 8.21776759711184e-07, -5.98690271725558e-07}, + {7.77122595418965e-07, 3.60896376754085e-07, 3.88610487893381e-07, -4.39533892679537e-08, -6.26882227849174e-08}, + {1.05759993661891e-07, 2.58009912408833e-08, -1.51356049060972e-08, -1.13335813107412e-09, 5.37470857850370e-10}, + {7.99831506181984e-09, 1.67423735327465e-09, 2.94736760548677e-09, -1.56727133704788e-09, 8.46186800849124e-10}, + {3.07727104043851e-09, 3.93584215798484e-10, 3.86721562770643e-11, 1.72181091277391e-10, -2.16915737920145e-10}, + {0, 0, 0, 0, 0}, + {-1.16335389078126e-05, -1.39864676661484e-05, 2.52546278407717e-06, -8.79152625440188e-06, -8.97665132187974e-06}, + {-3.95874550504316e-06, -1.17976262528730e-07, 7.03189926369300e-07, 3.38907065351535e-07, -3.67714052493558e-07}, + {2.29082449370440e-07, 5.72961531093329e-07, 4.21969662578894e-08, 1.24112958141431e-08, 9.56404486571888e-08}, + {1.44631865298671e-09, 6.19368473895584e-09, 1.67110424041236e-09, 2.57979463602951e-09, -6.90806907510366e-09}, + {1.77235802019153e-09, -8.14388846228970e-10, 4.50421956523579e-09, 5.67452314909707e-10, 2.47610443675560e-09}, + {4.85932343880617e-10, 2.24864117422804e-10, -2.22534534468511e-10, -7.96395824973477e-11, 3.12587399902493e-12}, + {-3.20173937255409e-11, -1.29872402028088e-11, -4.24092901203818e-11, 2.66570185704416e-11, -5.25164954403909e-12}, + {0, 0, 0, 0, 0}, + {-1.36010179191872e-05, 1.77873053642413e-05, 4.80988546657119e-06, 3.46859608161212e-06, -1.73247520896541e-06}, + {2.00020483116258e-06, 2.43393064079673e-06, 1.21478843695862e-06, 1.95582820041644e-07, -3.11847995109088e-07}, + {-8.13287218979310e-09, 1.05206830238665e-08, 6.54040136224164e-09, -1.96402660575990e-08, -1.40379796070732e-08}, + {4.01291020310740e-08, 2.92634301047947e-08, 6.04179709273169e-09, 8.61849065020545e-10, 5.98065429697245e-09}, + {-1.06149335032911e-09, -4.39748495862323e-10, 8.83040310269353e-10, 3.49392227277679e-10, 8.57722299002622e-10}, + {-1.25049888909390e-11, 2.05203288281631e-10, 1.37817670505319e-11, 6.82057794430145e-11, -9.41515631694254e-11}, + {7.47196022644130e-12, -2.51369898528782e-11, -2.12196687809200e-11, 1.55282119505201e-11, 9.99224438231805e-12}, + {-7.90534019004874e-13, 3.55824506982589e-12, 8.00835777767281e-13, 8.73460019069655e-13, 1.34176126600106e-12}, + {0, 0, 0, 0, 0}, + {3.12855262465316e-05, 1.31629386003608e-05, 2.65598119437581e-06, 8.68923340949135e-06, -7.51164082949678e-06}, + {1.56870792650533e-06, 1.89227301685370e-06, 4.15620385341985e-07, -2.74253787880603e-07, -4.28826210119200e-07}, + {-9.99176994565587e-08, -1.10785129426286e-07, -1.10318125091182e-07, 6.22726507350764e-09, -3.39214566386250e-08}, + {1.24872975018433e-08, 1.10663206077249e-08, 5.40658975901469e-09, -2.79119137105115e-09, -2.47500096192502e-09}, + {1.11518917154060e-10, -4.21965763244849e-10, 3.26786005211229e-10, 1.93488254914545e-10, 7.00774679999972e-10}, + {1.50889220040757e-10, 1.03130002661366e-10, -3.09481760816903e-11, -4.47656630703759e-11, -7.36245021803800e-12}, + {-1.91144562110285e-12, -1.11355583995978e-11, -1.76207323352556e-11, 8.15289793192265e-12, 3.45078925412654e-12}, + {-2.73248710476019e-12, -1.65089342283056e-13, -2.20125355220819e-13, 5.32589191504356e-13, 5.70008982140874e-13}, + {8.06636928368811e-13, 1.30893069976672e-13, 9.72079137767479e-14, 3.87410156264322e-14, -5.56410013263563e-14}, + {0, 0, 0, 0, 0}, + {2.02454485403216e-05, -9.77720471118669e-06, -4.35467548126223e-06, 2.19599868869063e-06, -3.26670819043690e-06}, + {-3.21839256310540e-08, 8.38760368015005e-07, -5.08058835724060e-07, 4.16177282491396e-08, 1.53842592762120e-07}, + {-1.57377633165313e-07, -7.86803586842404e-08, -7.40444711426898e-08, 3.15259864117954e-08, 5.60536231567172e-09}, + {-3.26080428920229e-10, -3.14576780695439e-09, 8.46796096612981e-10, -2.59329379174262e-09, -8.01054756588382e-10}, + {-4.58725236153576e-11, -6.87847958546571e-11, 8.18226480126754e-12, 1.81082075625897e-10, 1.74510532938256e-10}, + {7.60233505328792e-11, 4.76463939581321e-11, -2.47198455442033e-11, -8.83439688929965e-12, 5.93967446277316e-13}, + {-8.92919292558887e-12, -4.38524572312029e-12, -4.02709146060896e-12, 4.84344426425295e-12, 5.12869042781520e-12}, + {1.91518361809952e-12, 3.06846255371817e-13, -2.44830265306345e-13, 7.86297493099244e-14, 2.72347805801980e-13}, + {9.09936624159538e-14, 7.20650818861447e-15, 2.45383991578283e-14, -4.79580974186462e-15, 3.64604724046944e-14}, + {-4.63611142770709e-14, 1.73908246420636e-15, -4.41651410674801e-15, -6.61409045306922e-16, -1.60016049099639e-15}, + {0, 0, 0, 0, 0}, + {6.17105245892845e-06, -1.04342983738457e-05, -1.72711741097994e-05, -8.16815967888426e-07, 3.42789959967593e-06}, + {-2.44014060833825e-07, 2.06991837444652e-07, -3.85805819475679e-07, 1.67162359832166e-08, 4.15139610402483e-07}, + {8.18199006804020e-08, -3.20013409049159e-08, 5.94000906771151e-08, 2.24122167188946e-08, -1.33796186160409e-08}, + {7.66269294674338e-11, -6.07862178874828e-10, 4.95795757186248e-10, -3.07589245481422e-10, 3.44456287710689e-10}, + {-1.84076250254929e-10, -1.30985312312781e-10, -1.52547325533276e-10, -2.51000125929512e-11, -1.93924012590455e-11}, + {-2.93307452197665e-11, 2.88627386757582e-11, 5.58812021182217e-12, -1.68692874069187e-13, 1.80464313900575e-12}, + {-9.59053874473003e-13, 6.04803122874761e-13, -9.80015608958536e-13, 1.70530372034214e-12, 1.70458664160775e-12}, + {2.80169588226043e-13, 9.09573148053551e-14, 2.16449186617004e-14, 1.15550091496353e-13, 4.97772796761321e-14}, + {-3.04524400761371e-14, 3.42845631349694e-14, 2.44230630602064e-14, 5.76017546103056e-16, -9.74409465961093e-15}, + {5.98765340844291e-15, -2.63942474859535e-15, -1.80204805804437e-15, -1.84981819321183e-16, -5.85073392163660e-16}, + {-2.37069441910133e-15, 2.87429226086856e-16, -1.67055963193389e-16, 2.72110684914090e-18, 8.46646962667892e-17}, + {0, 0, 0, 0, 0}, + {-2.71386164105722e-05, -1.41834938338454e-05, -2.00777928859929e-07, 5.94329804681196e-07, 8.61856994375586e-06}, + {-3.93656495458664e-08, -6.36432821807576e-07, -2.47887475106438e-07, -2.64906446204966e-08, 1.10689794197004e-07}, + {5.25319489188562e-08, 9.00866357158695e-09, 5.00693379572512e-08, 2.47269011056404e-08, -7.27648556194598e-09}, + {1.87207107149043e-09, -1.46428282396138e-09, -2.71812237167257e-10, 8.44902265891466e-10, -5.62683870906027e-10}, + {-1.08295119666184e-10, 4.75553388543793e-11, -5.49429386495686e-11, -6.60907871731611e-11, -5.97347322824822e-11}, + {-4.95118306815571e-12, 5.31083735234970e-13, -1.93679746327378e-12, -1.61770521840510e-12, 1.23276727202510e-11}, + {6.68582682909900e-13, 7.38288575160449e-13, 5.47630483499201e-13, -1.00770258118914e-13, -1.65564928475981e-13}, + {5.80963409268471e-14, 6.93474288078737e-14, 6.60728092794315e-15, -5.21029056725202e-15, -1.11283532854883e-16}, + {-4.10567742688903e-15, 1.62252646805882e-14, 1.00774699865989e-14, -2.44793214897877e-16, -1.59283906414563e-15}, + {1.84669506619904e-17, 8.28473337813919e-17, -1.53400662078899e-16, -5.01060672199689e-17, -2.20727935766132e-16}, + {2.65355116203636e-16, -3.70233146147684e-17, 3.52689394451586e-18, -8.62215942516328e-18, 9.26909361974526e-18}, + {9.94266950643135e-17, 4.17028699663441e-18, -7.65153491125819e-21, -5.62131270981041e-18, -3.03732817297438e-18} + }; + + /** Legendre functions bnm for coefficient cw.*/ + private static final double[][] CW_B = { + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {-0.000209104872912563, -1.41530274973540e-05, 3.00318745764815e-05, -1.82864291318284e-05, -7.62965409959238e-06}, + {0, 0, 0, 0, 0}, + {-0.000186336519900275, 0.000191256553935638, 7.28356195304996e-05, 3.59637869639906e-05, -2.53927226167388e-05}, + {0.000108195343799485, -6.97050977217619e-05, -6.68037133871099e-05, 2.30387653190503e-05, -1.22735483925784e-05}, + {0, 0, 0, 0, 0}, + {0.000119941091277039, -7.70547844186875e-05, -8.15376297964528e-05, 1.06005789545203e-05, 2.31177232268720e-05}, + {-1.77494760217164e-05, -1.37061385686605e-05, -1.74805936475816e-05, -6.91745900867532e-07, -7.10231790947787e-06}, + {-1.47564103733219e-05, 2.08890785485260e-06, 3.19876879447867e-06, 9.43984664503715e-07, -4.90480527577521e-06}, + {0, 0, 0, 0, 0}, + {4.93300138389457e-05, -6.77641298460617e-05, -3.25043347246397e-05, 8.33226714911921e-06, 8.11499972792905e-06}, + {-2.80449863471272e-05, -1.04367606414606e-05, 1.64473584641163e-07, -3.57420965807816e-06, 2.95887156564038e-06}, + {1.88835280111533e-06, 5.69125761193702e-07, -2.22757382799409e-06, -1.96699131032252e-07, -2.91861219283659e-07}, + {-4.69918971436680e-06, -7.00778948636735e-07, 2.97544157334673e-09, 3.86100512544410e-07, 2.30939653701027e-07}, + {0, 0, 0, 0, 0}, + {1.77050610394149e-05, -3.18353071311574e-05, 3.04232260950316e-05, -6.26821316488169e-05, -1.75094810002378e-06}, + {9.25605901565775e-06, -8.25179123302247e-06, 6.74032752408358e-06, 3.22192289084524e-06, 6.09414500075259e-06}, + {4.28233825242200e-06, 2.10470570087927e-07, -4.75050074985668e-07, -4.89382663470592e-07, 8.75232347469207e-07}, + {8.50393520366934e-07, 1.58764911467186e-07, -2.16267638321210e-07, -7.43341300487416e-10, 1.75131729813230e-07}, + {-2.87064111623119e-07, 4.50393893102830e-08, 6.63315044416690e-08, 7.61199387418853e-08, -6.05694385243652e-09}, + {0, 0, 0, 0, 0}, + {-1.95692079507947e-05, 5.15486098887851e-05, 3.00852761598173e-05, 1.21485028343416e-05, -6.72450521493428e-06}, + {5.34496867088158e-06, 3.90973451680699e-06, 3.70148924718425e-06, 5.73731499938212e-08, 5.52258220288780e-07}, + {3.39950838185315e-07, -5.63443976772634e-07, 4.52082211980595e-07, -2.57094645806243e-07, -6.84885762924729e-08}, + {2.15793276880684e-07, 2.05911354090873e-07, 1.33747872341142e-08, -2.07997626478952e-08, -3.69812938736019e-08}, + {2.11952749403224e-09, 4.04317822544732e-08, 2.40972024883650e-09, 8.56289126938059e-09, 2.31035283490200e-08}, + {-2.08402298813248e-09, -8.50243600879112e-09, 2.60895410117768e-09, -6.69156841738591e-10, -5.16280278087006e-09}, + {0, 0, 0, 0, 0}, + {0.000124901291436683, -5.70770326719086e-05, -8.44887248105015e-05, -3.11442665354698e-05, -1.12982893252046e-05}, + {-8.38934444233944e-06, 1.56860091415414e-06, -1.77704563531825e-06, -5.70219068898717e-08, -4.30377735031244e-06}, + {3.72965318017681e-07, 6.98175439446187e-07, 1.75760544807919e-08, 1.59731284857151e-07, 3.62363848767891e-07}, + {-2.32148850787091e-07, -4.21888751852973e-08, 8.35926113952108e-08, -2.24572480575674e-08, -6.92114100904503e-08}, + {-2.92635642210745e-09, 3.38086229163415e-09, 4.72186694662901e-09, -8.32354437305758e-11, 4.19673890995627e-09}, + {-1.26452887692900e-09, 1.91309690886864e-09, 1.54755631983655e-09, -1.09865169400249e-09, 1.83645326319994e-10}, + {9.92539437011905e-10, -2.96318203488300e-10, 1.17466020823486e-10, -5.00185957995526e-10, -8.54777591408537e-11}, + {0, 0, 0, 0, 0}, + {-0.000182885335404854, 7.27424724520089e-05, 3.05286278023427e-05, 2.55324463432562e-05, -6.39859510763234e-06}, + {-5.21449265232557e-06, -6.70572386081398e-06, -3.95473351292738e-06, -6.41023334372861e-07, -3.11616331059009e-06}, + {2.37090789071727e-07, 3.58427517014705e-07, 2.55709192777007e-07, 8.44593804408541e-08, 9.27243162355359e-09}, + {7.24370898432057e-08, -7.43945120337710e-09, 8.61751911975683e-10, -2.34651212610623e-08, 2.94052921681456e-09}, + {-1.22127317934425e-08, -3.89758984276768e-09, 4.12890383904924e-11, 2.06528068002723e-09, 1.73488696972270e-09}, + {-5.44137406907620e-10, -4.81034553189921e-10, -2.56101759039694e-11, 3.21880564410154e-10, -2.70195343165250e-11}, + {1.08394225300546e-10, -7.99525492688661e-11, 1.73850287030654e-10, -8.06390014426271e-11, -7.63143364291160e-13}, + {-3.41446959267441e-11, 2.72675729042792e-11, 5.69674704865345e-12, -3.38402998344892e-12, -2.96732381931007e-12}, + {0, 0, 0, 0, 0}, + {2.91161315987250e-05, -7.24641166590735e-05, -8.58323519857884e-06, -1.14037444255820e-05, 1.32244819451517e-05}, + {1.24266748259826e-06, -4.13127038469802e-06, -8.47496394492885e-07, 5.48722958754267e-07, -1.98288551821205e-06}, + {-1.70671245196917e-08, 1.36891127083540e-08, -2.80901972249870e-07, -5.45369793946222e-09, -9.58796303763498e-08}, + {1.14115335901746e-08, 2.79308166429178e-08, -1.71144803132413e-08, 4.86116243565380e-09, -8.13061459952280e-09}, + {-1.19144311035824e-09, -1.28197815211763e-09, -1.22313592972373e-09, 6.23116336753674e-10, 2.11527825898689e-09}, + {4.94618645030426e-10, -1.01554483531252e-10, -3.58808808952276e-10, 1.23499783028794e-10, -1.21017599361833e-10}, + {1.33959569836451e-10, -1.87140898812283e-11, -3.04265350158941e-11, -1.42907553051431e-11, -1.09873858099638e-11}, + {1.30277419203512e-11, -4.95312627777245e-12, 2.23070215544358e-12, 1.66450226016423e-12, 6.26222944728474e-12}, + {-4.40721204874728e-12, 2.99575133064885e-12, -1.54917262009097e-12, 8.90015664527060e-14, -1.59135267012937e-12}, + {0, 0, 0, 0, 0}, + {-4.17667211323160e-05, 1.39005215116294e-05, 1.46521361817829e-05, 3.23485458024416e-05, -8.57936261085263e-06}, + {9.48491026524450e-07, 1.67749735481991e-06, 6.80159475477603e-07, -1.34558044496631e-06, 1.62108231492249e-06}, + {-2.67545753355631e-07, -3.31848493018159e-08, 1.05837219557465e-07, 1.55587655479400e-07, -2.84996014386667e-08}, + {-5.15113778734878e-08, 8.83630725241303e-09, 3.36579455982772e-09, -6.22350102096402e-09, 5.03959133095369e-09}, + {2.04635880823035e-11, -1.07923589059151e-09, -6.96482137669712e-10, -4.70238500452793e-10, -6.60277903598297e-10}, + {-2.41897168749189e-11, 1.33547763615216e-10, -5.13534673658908e-11, -8.32767177662817e-11, 5.72614717082428e-11}, + {7.55170562359940e-12, -1.57123461699055e-11, -1.48874069619124e-11, -7.10529462981252e-13, -7.99006335025107e-12}, + {2.41883156738960e-12, 2.97346980183361e-12, 1.28719977731450e-12, -2.49240876894143e-12, 6.71155595793198e-13}, + {4.16995565336914e-13, -1.71584521275288e-13, -7.23064067359978e-14, 2.45405880599037e-13, 4.43532934905830e-13}, + {3.56937508828997e-14, 2.43012511260300e-14, -7.96090778289326e-14, -1.59548529636358e-14, 8.99103763000507e-15}, + {0, 0, 0, 0, 0}, + {0.000117579258399489, -4.52648448635772e-05, -2.69130037097862e-05, -3.82266335794366e-05, -4.36549257701084e-06}, + {-1.43270371215502e-06, 1.21565440183855e-06, 8.53701136074284e-07, 1.52709810023665e-06, 1.22382663462904e-06}, + {3.06089147519664e-07, 9.79084123751975e-08, 7.96524661441178e-08, 4.54770947973458e-08, 2.22842369458882e-07}, + {-9.94254707745127e-09, 1.43251376378012e-08, 1.93911753685160e-08, -6.52214645690987e-09, -1.97114016452408e-09}, + {-9.20751919828404e-10, -9.44312829629076e-10, 7.24196738163952e-11, -6.71801072324561e-11, 2.33146774065873e-10}, + {-1.43544298956410e-11, 1.78464235318769e-10, 7.69950023012326e-11, -4.22390057304453e-12, 3.05176324574816e-11}, + {-7.88053753973990e-12, -3.20207793051003e-12, 1.01527407317625e-12, 6.02788185858449e-12, 1.14919530900453e-11}, + {-1.21558899266069e-12, 5.31300597882986e-13, 3.44023865079264e-13, -6.22598216726224e-14, -5.47031650765402e-14}, + {-4.15627948750943e-13, 2.77620907292721e-13, -8.99784134364011e-14, 1.07254247320864e-13, 6.85990080564196e-14}, + {-3.91837863922901e-14, 9.74714976816180e-15, 6.79982450963903e-15, -2.41420876658572e-15, -2.20889384455344e-15}, + {9.25912068402776e-15, -4.02621719248224e-15, -2.43952036351187e-15, -1.97006876049866e-15, 1.03065621527869e-16}, + {0, 0, 0, 0, 0}, + {-0.000103762036940193, 4.38145356960292e-05, 2.43406920349913e-05, 7.89103527673736e-06, -1.66841465339160e-05}, + {-1.18428449371744e-06, -1.30188721737259e-06, -1.88013557116650e-06, -1.01342046295303e-06, 9.21813037802502e-07}, + {1.51836068712460e-07, 1.11362553803933e-07, 1.55375052233052e-07, 1.94450910788747e-09, -1.73093755828342e-08}, + {-3.77758211813121e-09, 1.23323969583610e-08, 1.72510045250302e-09, -1.88609789458597e-09, 1.28937597985937e-09}, + {-1.07947760393523e-09, 5.26051570105365e-10, -3.67657536332496e-11, 3.16110123523840e-10, -3.24273198242170e-10}, + {-2.00385649209820e-12, 2.54703869682390e-11, 4.08563622440851e-12, -4.83350348928636e-11, -3.98153443845079e-13}, + {2.73094467727215e-12, 5.08900664114903e-12, -7.66669089075134e-13, 2.50015592643012e-12, 4.29763262853853e-12}, + {6.53946487537890e-13, -2.24958413781008e-13, 6.74638861781238e-15, 3.28537647613903e-14, 2.54199700290116e-13}, + {-1.09122051193505e-13, 8.36362392931501e-14, -3.90750153912300e-14, -5.44915910741950e-14, 2.43816947219217e-14}, + {-1.41882561550134e-14, 1.00455397812713e-14, 2.63347255121581e-15, 1.53043256823601e-15, 2.49081021428095e-15}, + {-1.17256193152654e-15, 1.05648985031971e-16, 1.31778372453016e-16, 1.44815198666577e-16, -3.72532768618480e-16}, + {2.66203457773766e-16, -7.67224608659658e-17, 3.51487351031864e-18, 4.10287131339291e-17, -6.72171711728514e-17} + }; + + /** Build a new instance. */ + LegendreFunctions() { + + } + + /** Get the value of the anm coefficient for bh. + * @param n index + * @param m index + * @return the anm coefficient for bh + */ + public double getAnmBh(final int n, final int m) { + return BH_A[n][m]; + } + + /** Get the value of the anm coefficient for bw. + * @param n index + * @param m index + * @return the anm coefficient for bw + */ + public double getAnmBw(final int n, final int m) { + return BW_A[n][m]; + } + + /** Get the value of the anm coefficient for ch. + * @param n index + * @param m index + * @return the anm coefficient for ch + */ + public double getAnmCh(final int n, final int m) { + return CH_A[n][m]; + } + + /** Get the value of the anm coefficient for cw. + * @param n index + * @param m index + * @return the anm coefficient for cw + */ + public double getAnmCw(final int n, final int m) { + return CW_A[n][m]; + } + + /** Get the value of the bnm coefficient for bh. + * @param n index + * @param m index + * @return the bnm coefficient for bh + */ + public double getBnmBh(final int n, final int m) { + return BH_B[n][m]; + } + + /** Get the value of the bnm coefficient for bw. + * @param n index + * @param m index + * @return the bnm coefficient for bw + */ + public double getBnmBw(final int n, final int m) { + return BW_B[n][m]; + } + + /** Get the value of the bnm coefficient for ch. + * @param n index + * @param m index + * @return the bnm coefficient for ch + */ + public double getBnmCh(final int n, final int m) { + return CH_B[n][m]; + } + + /** Get the value of the bnm coefficient for cw. + * @param n index + * @param m index + * @return the bnm coefficient for cw + */ + public double getBnmCw(final int n, final int m) { + return CW_B[n][m]; + } + + } +} diff --git a/src/main/java/org/orekit/models/earth/troposphere/ViennaThreeModel.java b/src/main/java/org/orekit/models/earth/troposphere/ViennaThreeModel.java index 17188f02dd..2b221723a3 100644 --- a/src/main/java/org/orekit/models/earth/troposphere/ViennaThreeModel.java +++ b/src/main/java/org/orekit/models/earth/troposphere/ViennaThreeModel.java @@ -16,26 +16,19 @@ */ package org.orekit.models.earth.troposphere; -import java.util.Collections; -import java.util.List; - -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; -import org.hipparchus.util.FastMath; -import org.hipparchus.util.FieldSinCos; +import org.hipparchus.Field; import org.hipparchus.util.MathArrays; -import org.hipparchus.util.SinCos; import org.orekit.annotation.DefaultDataContext; import org.orekit.bodies.FieldGeodeticPoint; import org.orekit.bodies.GeodeticPoint; import org.orekit.data.DataContext; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; import org.orekit.time.AbsoluteDate; -import org.orekit.time.DateTimeComponents; import org.orekit.time.FieldAbsoluteDate; import org.orekit.time.TimeScale; -import org.orekit.utils.FieldLegendrePolynomials; -import org.orekit.utils.LegendrePolynomials; -import org.orekit.utils.ParameterDriver; +import org.orekit.utils.FieldTrackingCoordinates; +import org.orekit.utils.TrackingCoordinates; /** The Vienna3 tropospheric delay model for radio techniques. * The Vienna model data are given with a time interval of 6 hours. @@ -55,18 +48,14 @@ * http://repositum.tuwien.ac.at/urn:nbn:at:at-ubtuw:1-100249" * * @author Bryan Cazabonne + * @deprecated as of 12.1, replaced by {@link ViennaThree} */ -public class ViennaThreeModel implements DiscreteTroposphericModel, MappingFunction { - - /** The a coefficient for the computation of the wet and hydrostatic mapping functions.*/ - private final double[] coefficientsA; +@Deprecated +public class ViennaThreeModel extends ViennaThree implements DiscreteTroposphericModel, MappingFunction { /** Values of hydrostatic and wet delays as provided by the Vienna model. */ private final double[] zenithDelay; - /** UTC time scale. */ - private final TimeScale utc; - /** Build a new instance. * *

        This constructor uses the {@link DataContext#getDefault() default data context}. @@ -90,206 +79,56 @@ public ViennaThreeModel(final double[] coefficientA, final double[] zenithDelay) public ViennaThreeModel(final double[] coefficientA, final double[] zenithDelay, final TimeScale utc) { - this.coefficientsA = coefficientA.clone(); - this.zenithDelay = zenithDelay.clone(); - this.utc = utc; + super(new ConstantViennaAProvider(new ViennaACoefficients(coefficientA[0], coefficientA[1])), + new ConstantAzimuthalGradientProvider(null), + new ConstantTroposphericModel(new TroposphericDelay(zenithDelay[0], zenithDelay[1], + zenithDelay[0], zenithDelay[1])), + utc); + this.zenithDelay = zenithDelay.clone(); } /** {@inheritDoc} */ @Override + @Deprecated public double[] mappingFactors(final double elevation, final GeodeticPoint point, final AbsoluteDate date) { - // Day of year computation - final DateTimeComponents dtc = date.getComponents(utc); - final int dofyear = dtc.getDate().getDayOfYear(); - - // Compute Legendre Polynomials Pnm(cos(0.5 * pi - phi)) - final int degree = 12; - final int order = 12; - final LegendrePolynomials p = new LegendrePolynomials(degree, order, FastMath.cos(0.5 * FastMath.PI - point.getLatitude())); - - // Compute coefficients bh, bw, ch and cw with spherical harmonics - double a0Bh = 0.; - double a0Bw = 0.; - double a0Ch = 0.; - double a0Cw = 0.; - double a1Bh = 0.; - double a1Bw = 0.; - double a1Ch = 0.; - double a1Cw = 0.; - double b1Bh = 0.; - double b1Bw = 0.; - double b1Ch = 0.; - double b1Cw = 0.; - double a2Bh = 0.; - double a2Bw = 0.; - double a2Ch = 0.; - double a2Cw = 0.; - double b2Bh = 0.; - double b2Bw = 0.; - double b2Ch = 0.; - double b2Cw = 0.; - final LegendreFunctions AnmBnm = new LegendreFunctions(); - int j = 0; - for (int n = 0; n <= 12; n++) { - for (int m = 0; m <= n; m++) { - final SinCos sc = FastMath.sinCos(m * point.getLongitude()); - final double pCosmLambda = p.getPnm(n, m) * sc.cos(); - final double pSinmLambda = p.getPnm(n, m) * sc.sin(); - - a0Bh = a0Bh + (AnmBnm.getAnmBh(j, 0) * pCosmLambda + AnmBnm.getBnmBh(j, 0) * pSinmLambda); - a0Bw = a0Bw + (AnmBnm.getAnmBw(j, 0) * pCosmLambda + AnmBnm.getBnmBw(j, 0) * pSinmLambda); - a0Ch = a0Ch + (AnmBnm.getAnmCh(j, 0) * pCosmLambda + AnmBnm.getBnmCh(j, 0) * pSinmLambda); - a0Cw = a0Cw + (AnmBnm.getAnmCw(j, 0) * pCosmLambda + AnmBnm.getBnmCw(j, 0) * pSinmLambda); - - a1Bh = a1Bh + (AnmBnm.getAnmBh(j, 1) * pCosmLambda + AnmBnm.getBnmBh(j, 1) * pSinmLambda); - a1Bw = a1Bw + (AnmBnm.getAnmBw(j, 1) * pCosmLambda + AnmBnm.getBnmBw(j, 1) * pSinmLambda); - a1Ch = a1Ch + (AnmBnm.getAnmCh(j, 1) * pCosmLambda + AnmBnm.getBnmCh(j, 1) * pSinmLambda); - a1Cw = a1Cw + (AnmBnm.getAnmCw(j, 1) * pCosmLambda + AnmBnm.getBnmCw(j, 1) * pSinmLambda); - - b1Bh = b1Bh + (AnmBnm.getAnmBh(j, 2) * pCosmLambda + AnmBnm.getBnmBh(j, 2) * pSinmLambda); - b1Bw = b1Bw + (AnmBnm.getAnmBw(j, 2) * pCosmLambda + AnmBnm.getBnmBw(j, 2) * pSinmLambda); - b1Ch = b1Ch + (AnmBnm.getAnmCh(j, 2) * pCosmLambda + AnmBnm.getBnmCh(j, 2) * pSinmLambda); - b1Cw = b1Cw + (AnmBnm.getAnmCw(j, 2) * pCosmLambda + AnmBnm.getBnmCw(j, 2) * pSinmLambda); - - a2Bh = a2Bh + (AnmBnm.getAnmBh(j, 3) * pCosmLambda + AnmBnm.getBnmBh(j, 3) * pSinmLambda); - a2Bw = a2Bw + (AnmBnm.getAnmBw(j, 3) * pCosmLambda + AnmBnm.getBnmBw(j, 3) * pSinmLambda); - a2Ch = a2Ch + (AnmBnm.getAnmCh(j, 3) * pCosmLambda + AnmBnm.getBnmCh(j, 3) * pSinmLambda); - a2Cw = a2Cw + (AnmBnm.getAnmCw(j, 3) * pCosmLambda + AnmBnm.getBnmCw(j, 3) * pSinmLambda); - - b2Bh = b2Bh + (AnmBnm.getAnmBh(j, 4) * pCosmLambda + AnmBnm.getBnmBh(j, 4) * pSinmLambda); - b2Bw = b2Bw + (AnmBnm.getAnmBw(j, 4) * pCosmLambda + AnmBnm.getBnmBw(j, 4) * pSinmLambda); - b2Ch = b2Ch + (AnmBnm.getAnmCh(j, 4) * pCosmLambda + AnmBnm.getBnmCh(j, 4) * pSinmLambda); - b2Cw = b2Cw + (AnmBnm.getAnmCw(j, 4) * pCosmLambda + AnmBnm.getBnmCw(j, 4) * pSinmLambda); - - j = j + 1; - } - } - - // Eq. 6 - final double bh = computeSeasonalFit(dofyear, a0Bh, a1Bh, a2Bh, b1Bh, b2Bh); - final double bw = computeSeasonalFit(dofyear, a0Bw, a1Bw, a2Bw, b1Bw, b2Bw); - final double ch = computeSeasonalFit(dofyear, a0Ch, a1Ch, a2Ch, b1Ch, b2Ch); - final double cw = computeSeasonalFit(dofyear, a0Cw, a1Cw, a2Cw, b1Cw, b2Cw); - - // Compute Mapping Function Eq. 4 - final double[] function = new double[2]; - function[0] = TroposphericModelUtils.mappingFunction(coefficientsA[0], bh, ch, elevation); - function[1] = TroposphericModelUtils.mappingFunction(coefficientsA[1], bw, cw, elevation); - - return function; + return mappingFactors(new TrackingCoordinates(0.0, elevation, 0.0), point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + date); } /** {@inheritDoc} */ @Override + @Deprecated public > T[] mappingFactors(final T elevation, final FieldGeodeticPoint point, - final FieldAbsoluteDate date) { - final Field field = date.getField(); - final T zero = field.getZero(); - - // Day of year computation - final DateTimeComponents dtc = date.getComponents(utc); - final int dofyear = dtc.getDate().getDayOfYear(); - - // Compute Legendre Polynomials Pnm(cos(0.5 * pi - phi)) - final int degree = 12; - final int order = 12; - final FieldLegendrePolynomials p = new FieldLegendrePolynomials<>(degree, order, FastMath.cos(point.getLatitude().negate().add(zero.getPi().multiply(0.5)))); - - // Compute coefficients bh, bw, ch and cw with spherical harmonics - T a0Bh = zero; - T a0Bw = zero; - T a0Ch = zero; - T a0Cw = zero; - T a1Bh = zero; - T a1Bw = zero; - T a1Ch = zero; - T a1Cw = zero; - T b1Bh = zero; - T b1Bw = zero; - T b1Ch = zero; - T b1Cw = zero; - T a2Bh = zero; - T a2Bw = zero; - T a2Ch = zero; - T a2Cw = zero; - T b2Bh = zero; - T b2Bw = zero; - T b2Ch = zero; - T b2Cw = zero; - final LegendreFunctions AnmBnm = new LegendreFunctions(); - int j = 0; - for (int n = 0; n <= 12; n++) { - for (int m = 0; m <= n; m++) { - final FieldSinCos sc = FastMath.sinCos(point.getLongitude().multiply(m)); - final T pCosmLambda = p.getPnm(n, m).multiply(sc.cos()); - final T pSinmLambda = p.getPnm(n, m).multiply(sc.sin()); - - a0Bh = a0Bh.add(pCosmLambda.multiply(AnmBnm.getAnmBh(j, 0)).add(pSinmLambda.multiply(AnmBnm.getBnmBh(j, 0)))); - a0Bw = a0Bw.add(pCosmLambda.multiply(AnmBnm.getAnmBw(j, 0)).add(pSinmLambda.multiply(AnmBnm.getBnmBw(j, 0)))); - a0Ch = a0Ch.add(pCosmLambda.multiply(AnmBnm.getAnmCh(j, 0)).add(pSinmLambda.multiply(AnmBnm.getBnmCh(j, 0)))); - a0Cw = a0Cw.add(pCosmLambda.multiply(AnmBnm.getAnmCw(j, 0)).add(pSinmLambda.multiply(AnmBnm.getBnmCw(j, 0)))); - - a1Bh = a1Bh.add(pCosmLambda.multiply(AnmBnm.getAnmBh(j, 1)).add(pSinmLambda.multiply(AnmBnm.getBnmBh(j, 1)))); - a1Bw = a1Bw.add(pCosmLambda.multiply(AnmBnm.getAnmBw(j, 1)).add(pSinmLambda.multiply(AnmBnm.getBnmBw(j, 1)))); - a1Ch = a1Ch.add(pCosmLambda.multiply(AnmBnm.getAnmCh(j, 1)).add(pSinmLambda.multiply(AnmBnm.getBnmCh(j, 1)))); - a1Cw = a1Cw.add(pCosmLambda.multiply(AnmBnm.getAnmCw(j, 1)).add(pSinmLambda.multiply(AnmBnm.getBnmCw(j, 1)))); - - b1Bh = b1Bh.add(pCosmLambda.multiply(AnmBnm.getAnmBh(j, 2)).add(pSinmLambda.multiply(AnmBnm.getBnmBh(j, 2)))); - b1Bw = b1Bw.add(pCosmLambda.multiply(AnmBnm.getAnmBw(j, 2)).add(pSinmLambda.multiply(AnmBnm.getBnmBw(j, 2)))); - b1Ch = b1Ch.add(pCosmLambda.multiply(AnmBnm.getAnmCh(j, 2)).add(pSinmLambda.multiply(AnmBnm.getBnmCh(j, 2)))); - b1Cw = b1Cw.add(pCosmLambda.multiply(AnmBnm.getAnmCw(j, 2)).add(pSinmLambda.multiply(AnmBnm.getBnmCw(j, 2)))); - - a2Bh = a2Bh.add(pCosmLambda.multiply(AnmBnm.getAnmBh(j, 3)).add(pSinmLambda.multiply(AnmBnm.getBnmBh(j, 3)))); - a2Bw = a2Bw.add(pCosmLambda.multiply(AnmBnm.getAnmBw(j, 3)).add(pSinmLambda.multiply(AnmBnm.getBnmBw(j, 3)))); - a2Ch = a2Ch.add(pCosmLambda.multiply(AnmBnm.getAnmCh(j, 3)).add(pSinmLambda.multiply(AnmBnm.getBnmCh(j, 3)))); - a2Cw = a2Cw.add(pCosmLambda.multiply(AnmBnm.getAnmCw(j, 3)).add(pSinmLambda.multiply(AnmBnm.getBnmCw(j, 3)))); - - b2Bh = b2Bh.add(pCosmLambda.multiply(AnmBnm.getAnmBh(j, 4)).add(pSinmLambda.multiply(AnmBnm.getBnmBh(j, 4)))); - b2Bw = b2Bw.add(pCosmLambda.multiply(AnmBnm.getAnmBw(j, 4)).add(pSinmLambda.multiply(AnmBnm.getBnmBw(j, 4)))); - b2Ch = b2Ch.add(pCosmLambda.multiply(AnmBnm.getAnmCh(j, 4)).add(pSinmLambda.multiply(AnmBnm.getBnmCh(j, 4)))); - b2Cw = b2Cw.add(pCosmLambda.multiply(AnmBnm.getAnmCw(j, 4)).add(pSinmLambda.multiply(AnmBnm.getBnmCw(j, 4)))); - - j = j + 1; - } - } - - // Eq. 6 - final T bh = computeSeasonalFit(dofyear, a0Bh, a1Bh, a2Bh, b1Bh, b2Bh); - final T bw = computeSeasonalFit(dofyear, a0Bw, a1Bw, a2Bw, b1Bw, b2Bw); - final T ch = computeSeasonalFit(dofyear, a0Ch, a1Ch, a2Ch, b1Ch, b2Ch); - final T cw = computeSeasonalFit(dofyear, a0Cw, a1Cw, a2Cw, b1Cw, b2Cw); - - // Compute Mapping Function Eq. 4 - final T[] function = MathArrays.buildArray(field, 2); - function[0] = TroposphericModelUtils.mappingFunction(zero.add(coefficientsA[0]), bh, ch, elevation); - function[1] = TroposphericModelUtils.mappingFunction(zero.add(coefficientsA[1]), bw, cw, elevation); - - return function; + final FieldAbsoluteDate date) { + return mappingFactors(new FieldTrackingCoordinates<>(date.getField().getZero(), elevation, date.getField().getZero()), + point, + new FieldPressureTemperatureHumidity<>(date.getField(), + TroposphericModelUtils.STANDARD_ATMOSPHERE), + date); } /** {@inheritDoc} */ @Override + @Deprecated public double pathDelay(final double elevation, final GeodeticPoint point, final double[] parameters, final AbsoluteDate date) { - // zenith delay - final double[] delays = computeZenithDelay(point, parameters, date); - // mapping function - final double[] mappingFunction = mappingFactors(elevation, point, date); - // Tropospheric path delay - return delays[0] * mappingFunction[0] + delays[1] * mappingFunction[1]; + return pathDelay(new TrackingCoordinates(0.0, elevation, 0.0), point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, parameters, date). + getDelay(); } /** {@inheritDoc} */ @Override + @Deprecated public > T pathDelay(final T elevation, final FieldGeodeticPoint point, - final T[] parameters, final FieldAbsoluteDate date) { - // zenith delay - final T[] delays = computeZenithDelay(point, parameters, date); - // mapping function - final T[] mappingFunction = mappingFactors(elevation, point, date); - // Tropospheric path delay - return delays[0].multiply(mappingFunction[0]).add(delays[1].multiply(mappingFunction[1])); + final T[] parameters, final FieldAbsoluteDate date) { + return pathDelay(new FieldTrackingCoordinates<>(date.getField().getZero(), elevation, date.getField().getZero()), + point, + new FieldPressureTemperatureHumidity<>(date.getField(), TroposphericModelUtils.STANDARD_ATMOSPHERE), + parameters, date). + getDelay(); } /** This method allows the computation of the zenith hydrostatic and @@ -324,902 +163,9 @@ public > T[] computeZenithDelay(final FieldGeo final Field field = date.getField(); final T zero = field.getZero(); final T[] delays = MathArrays.buildArray(field, 2); - delays[0] = zero.add(zenithDelay[0]); - delays[1] = zero.add(zenithDelay[1]); + delays[0] = zero.newInstance(zenithDelay[0]); + delays[1] = zero.newInstance(zenithDelay[1]); return delays; } - /** {@inheritDoc} */ - @Override - public List getParametersDrivers() { - return Collections.emptyList(); - } - - /** Computes the empirical temporal information for the mapping function - * coefficients b and c. A seasonal fit formula is performed. - * @param doy day of year - * @param A0 Mean value of the coefficient - * @param A1 Annual amplitude of the coefficient - * @param A2 Semi-annual amplitude of the coefficient - * @param B1 Annual amplitude of the coefficient - * @param B2 Semi-annual amplitude of the coefficient - * @return the mapping function coefficient at a given day. - */ - private double computeSeasonalFit(final int doy, final double A0, final double A1, - final double A2, final double B1, final double B2) { - - final double coef = (doy / 365.25) * 2 * FastMath.PI; - final SinCos sc1 = FastMath.sinCos(coef); - final SinCos sc2 = FastMath.sinCos(2.0 * coef); - - return A0 + - A1 * sc1.cos() + B1 * sc1.sin() + - A2 * sc2.cos() + B2 * sc2.sin(); - } - - /** Computes the empirical temporal information for the mapping function - * coefficients b and c. A seasonal fit formula is performed. - * @param type of the elements - * @param doy day of year - * @param A0 Mean value of the coefficient - * @param A1 Annual amplitude of the coefficient - * @param A2 Semi-annual amplitude of the coefficient - * @param B1 Annual amplitude of the coefficient - * @param B2 Semi-annual amplitude of the coefficient - * @return the mapping function coefficient at a given day. - */ - private > T computeSeasonalFit(final int doy, final T A0, final T A1, - final T A2, final T B1, final T B2) { - final T coef = A0.getPi().multiply(2.0).multiply(doy / 365.25); - final FieldSinCos sc1 = FastMath.sinCos(coef); - final FieldSinCos sc2 = FastMath.sinCos(coef.multiply(2.0)); - - return A0.add( - A1.multiply(sc1.cos())).add(B1.multiply(sc1.sin())).add( - A2.multiply(sc2.cos())).add(B2.multiply(sc2.sin())); - } - - /** Class for the values of the coefficients anm and bnm - * of the spherical harmonics expansion. - */ - private static class LegendreFunctions { - - /** Legendre functions anm for coefficient bh.*/ - private static final double[][] BH_A = { - {0.00271285863109945, -1.39197786008938e-06, 1.34955672002719e-06, 2.71686279717968e-07, 1.56659301773925e-06}, - {9.80476624811974e-06, -5.83922611260673e-05, -2.07307023860417e-05, 1.14628726961148e-06, 4.93610283608719e-06}, - {-1.03443106534268e-05, -2.05536138785961e-06, 2.09692641914244e-06, -1.55491034130965e-08, -1.89706404675801e-07}, - {-3.00353961749658e-05, 2.37284447073503e-05, 2.02236885378918e-05, 1.69276006349609e-06, 8.72156681243892e-07}, - {-7.99121077044035e-07, -5.39048313389504e-06, -4.21234502039861e-06, -2.70944149806894e-06, -6.80894455531746e-07}, - {7.51439609883296e-07, 3.85509708865520e-07, 4.41508016098164e-08, -2.07507808307757e-08, 4.95354985050743e-08}, - {2.21790962160087e-05, -5.56986238775212e-05, -1.81287885563308e-05, -4.41076013532589e-06, 4.93573223917278e-06}, - {-4.47639989737328e-06, -2.60452893072120e-06, 2.56376320011189e-06, 4.41600992220479e-07, 2.93437730332869e-07}, - {8.14992682244945e-07, 2.03945571424434e-07, 1.11832498659806e-08, 3.25756664234497e-08, 3.01029040414968e-08}, - {-7.96927680907488e-08, -3.66953150925865e-08, -6.74742632186619e-09, -1.30315731273651e-08, -2.00748924306947e-09}, - {-2.16138375166934e-05, 1.67350317962556e-05, 1.93768260076821e-05, 1.99595120161850e-06, -2.42463528222014e-06}, - {5.34360283708044e-07, -3.64189022040600e-06, -2.99935375194279e-06, -2.06880962903922e-06, -9.40815692626002e-07}, - {6.80235884441822e-07, 1.33023436079845e-07, -1.80349593705226e-08, 2.51276252565192e-08, -1.43240592002794e-09}, - {-7.13790897253802e-08, 7.81998506267559e-09, 1.13826909570178e-09, -5.89629600214654e-09, -4.20760865522804e-09}, - {-5.80109372399116e-09, 1.13702284491976e-09, 7.29046067602764e-10, -9.10468988754012e-10, -2.58814364808642e-10}, - {1.75558618192965e-05, -2.85579168876063e-05, -1.47442190284602e-05, -6.29300414335248e-06, -5.12204538913460e-07}, - {-1.90788558291310e-06, -1.62144845155361e-06, 7.57239241641566e-07, 6.93365788711348e-07, 6.88855644570695e-07}, - {2.27050351488552e-07, 1.03925791277660e-07, -3.31105076632079e-09, 2.88065761026675e-08, -8.00256848229136e-09}, - {-2.77028851807614e-08, -5.96251132206930e-09, 2.95987495527251e-10, -5.87644249625625e-09, -3.28803981542337e-09}, - {-1.89918479865558e-08, 3.54083436578857e-09, 8.10617835854935e-10, 4.99207055948336e-10, -1.52691648387663e-10}, - {1.04022499586096e-09, -2.36437143845013e-10, -2.25110813484842e-10, -7.39850069252329e-11, 7.95929405440911e-11}, - {-3.11579421267630e-05, -3.43576336877494e-06, 5.81663608263384e-06, 8.31534700351802e-07, 4.02619520312154e-06}, - {6.00037066879001e-07, -1.12538760056168e-07, -3.86745332115590e-07, -3.88218746020826e-07, -6.83764967176388e-07}, - {-9.79583981249316e-08, 9.14964449851003e-08, 4.77779838549237e-09, 2.44283811750703e-09, -6.26361079345158e-09}, - {-2.37742207548109e-08, -5.53336301671633e-09, -3.73625445257115e-09, -1.92304189572886e-09, -7.18681390197449e-09}, - {-6.58203463929583e-09, 9.28456148541896e-10, 2.47218904311077e-10, 1.10664919110218e-10, -4.20390976974043e-11}, - {9.45857603373426e-10, -3.29683402990254e-11, -8.15440375865127e-11, -1.21615589356628e-12, -9.70713008848085e-12}, - {1.61377382316176e-10, 6.84326027598147e-12, -4.66898885683671e-12, 2.31211355085535e-12, 2.39195112937346e-12}, - {2.99634365075821e-07, 8.14391615472128e-06, 6.70458490942443e-06, -9.92542646762000e-07, -3.04078064992750e-06}, - {-6.52697933801393e-07, 2.87255329776428e-07, -1.78227609772085e-08, 2.65525429849935e-07, 8.60650570551813e-08}, - {-1.62727164011710e-07, 1.09102479325892e-07, 4.97827431850001e-09, 7.86649963082937e-11, -6.67193813407656e-09}, - {-2.96370000987760e-09, 1.20008401576557e-09, 1.75885448022883e-09, -1.74756709684384e-09, 3.21963061454248e-09}, - {-9.91101697778560e-10, 7.54541713140752e-10, -2.95880967800875e-10, 1.81009160501278e-10, 8.31547411640954e-11}, - {1.21268051949609e-10, -5.93572774509587e-11, -5.03295034994351e-11, 3.05383430975252e-11, 3.56280438509939e-11}, - {6.92012970333794e-11, -9.02885345797597e-12, -3.44151832744880e-12, 2.03164894681921e-12, -5.44852265137606e-12}, - {5.56731263672800e-12, 3.57272150106101e-12, 2.25885622368678e-12, -2.44508240047675e-13, -6.83314378535235e-13}, - {3.96883487797254e-06, -4.57100506169608e-06, -3.30208117813256e-06, 3.32599719134845e-06, 4.26539325549339e-06}, - {1.10123151770973e-06, 4.58046760144882e-07, 1.86831972581926e-07, -1.60092770735081e-07, -5.58956114867062e-07}, - {-3.40344900506653e-08, 2.87649741373047e-08, -1.83929753066251e-08, -9.74179203885847e-09, -2.42064137485043e-09}, - {-6.49731596932566e-09, -3.07048108404447e-09, -2.84380614669848e-09, 1.55123146524283e-09, 4.53694984588346e-10}, - {5.45175793803325e-10, -3.73287624700125e-10, -1.16293122618336e-10, 7.25845618602690e-11, -4.34112440021627e-11}, - {1.89481447552805e-10, 3.67431482211078e-12, -1.72180065021194e-11, 1.47046319023226e-11, 1.31920481414062e-11}, - {2.10125915737167e-12, -3.08420783495975e-12, -4.87748712363020e-12, 1.16363599902490e-14, 1.26698255558605e-13}, - {-8.07894928696254e-12, 9.19344620512607e-13, 3.26929173307443e-13, 2.00438149416495e-13, -9.57035765212079e-15}, - {1.38737151773284e-12, 1.09340178371420e-13, 5.15714202449053e-14, -5.92156438588931e-14, -3.29586752336143e-14}, - {6.38137197198254e-06, 4.62426300749908e-06, 4.42334454191034e-06, 1.15374736092349e-06, -2.61859702227253e-06}, - {-2.25320619636149e-07, 3.21907705479353e-07, -3.34834530764823e-07, -4.82132753601810e-07, -3.22410936343355e-07}, - {3.48894515496995e-09, 3.49951261408458e-08, -6.01128959281142e-09, 4.78213900943443e-09, 1.46012816168576e-08}, - {-9.66682871952083e-11, 3.75806627535317e-09, 2.38984004956705e-09, 2.07545049877203e-09, 1.58573595632766e-09}, - {1.06834370693917e-09, -4.07975055112153e-10, -2.37598937943957e-10, 5.89327007480137e-11, 1.18891820437634e-10}, - {5.22433722695807e-11, 6.02011995016293e-12, -7.80605402956048e-12, 1.50873145627341e-11, -1.40550093106311e-12}, - {2.13396242187279e-13, -1.71939313965536e-12, -3.57625378660975e-14, -5.01675184988446e-14, -1.07805487368797e-12}, - {-1.24352330043311e-12, 8.26105883301606e-13, 4.63606970128517e-13, 6.39517888984486e-14, -7.35135439920086e-14}, - {-5.39023859065631e-13, 2.54188315588243e-14, 1.30933833278664e-14, 6.06153473304781e-15, -4.24722717533726e-14}, - {3.12767756884813e-14, -2.29517847871632e-15, 2.53117304424948e-16, 7.07504914138118e-16, -1.20089065310688e-15}, - {2.08311178819214e-06, -1.22179185044174e-06, -2.98842190131044e-06, 3.07310218974299e-06, 2.27100346036619e-06}, - {-3.94601643855452e-07, -5.44014825116083e-07, -6.16955333162507e-08, -2.31954821580670e-07, 1.14010813005310e-07}, - {6.11067575043044e-08, -3.93240193194272e-08, -1.62979132528933e-08, 1.01339204652581e-08, 1.97319601566071e-08}, - {2.57770508710055e-09, 1.87799543582899e-09, 1.95407654714372e-09, 1.15276419281270e-09, 2.25397005402120e-09}, - {7.16926338026236e-10, -3.65857693313858e-10, -1.54864067050915e-11, 6.50770211276549e-11, -7.85160007413546e-12}, - {4.90007693914221e-12, 3.31649396536340e-12, 4.81664871165640e-13, 7.26080745617085e-12, 2.30960953372164e-12}, - {9.75489202240545e-13, -1.68967954531421e-13, 7.38383391334110e-13, -3.58435515913239e-13, -3.01564710027450e-13}, - {-3.79533601922805e-13, 2.76681830946617e-13, 1.21480375553803e-13, -1.57729077644850e-14, -8.87664977818700e-14}, - {-3.96462845480288e-14, 2.94155690934610e-14, 6.78413205760717e-15, -4.12135802787361e-15, -1.46373307795619e-14}, - {-8.64941937408121e-15, -1.91822620970386e-15, -8.01725413560744e-16, 5.02941051180784e-16, -1.07572628474344e-15}, - {-4.13816294742758e-15, -7.43602019785880e-17, -5.54248556346072e-17, -4.83999456005158e-17, -1.19622559730466e-16}, - {-8.34852132750364e-07, -7.45794677612056e-06, -6.58132648865533e-06, -1.38608110346732e-06, 5.32326534882584e-07}, - {-2.75513802414150e-07, 3.64713745106279e-08, -7.12385417940442e-08, -7.86206067228882e-08, 2.28048393207161e-08}, - {-4.26696415431918e-08, -4.65599668635087e-09, 7.35037936327566e-09, 1.17098354115804e-08, 1.44594777658035e-08}, - {1.12407689274199e-09, 7.62142529563709e-10, -6.72563708415472e-10, -1.18094592485992e-10, -1.17043815733292e-09}, - {1.76612225246125e-10, -1.01188552503192e-10, 7.32546072616968e-11, 1.79542821801610e-11, -2.23264859965402e-11}, - {-9.35960722512375e-12, 1.90894283812231e-12, -6.34792824525760e-13, 3.98597963877826e-12, -4.47591409078971e-12}, - {-3.34623858556099e-12, 4.56384903915853e-14, 2.72561108521416e-13, -3.57942733300468e-15, 1.99794810657713e-13}, - {-6.16775522568954e-14, 8.25316968328823e-14, 7.19845814260518e-14, -2.92415710855106e-14, -5.49570017444031e-15}, - {-8.50728802453217e-15, 8.38161600916267e-15, 3.43651657459983e-15, -8.19429434115910e-16, -4.08905746461100e-15}, - {4.39042894275548e-15, -3.69440485320477e-16, 1.22249256876779e-16, -2.09359444520984e-16, -3.34211740264257e-16}, - {-5.36054548134225e-16, 3.29794204041989e-17, 2.13564354374585e-17, -1.37838993720865e-18, -1.29188342867753e-17}, - {-3.26421841529845e-17, 7.38235405234126e-18, 2.49291659676210e-18, 8.18252735459593e-19, 1.73824952279230e-20}, - {4.67237509268208e-06, 1.93611283787239e-06, 9.39035455627622e-07, -5.84565118072823e-07, -1.76198705802101e-07}, - {-3.33739157421993e-07, 4.12139555299163e-07, 1.58754695700856e-07, 1.37448753329669e-07, 1.04722936936873e-07}, - {6.64200603076386e-09, 1.45412222625734e-08, 1.82498796118030e-08, 2.86633517581614e-09, 1.06066984548100e-09}, - {5.25549696746655e-09, -1.33677183394083e-09, 7.60804375937931e-11, -1.07918624219037e-10, 8.09178898247941e-10}, - {1.89318454110039e-10, 9.23092164791765e-11, 5.51434573131180e-11, 3.86696392289240e-11, -1.15208165047149e-11}, - {-1.02252706006226e-12, -7.25921015411136e-13, -1.98110126887620e-12, -2.18964868282672e-13, -7.18834476685625e-13}, - {-2.69770025318548e-12, -2.17850340796321e-14, 4.73040820865871e-13, 1.57947421572149e-13, 1.86925164972766e-13}, - {1.07831718354771e-13, 2.26681841611017e-14, 2.56046087047783e-14, -1.14995851659554e-14, -2.27056907624485e-14}, - {6.29825154734712e-15, 8.04458225889001e-16, 9.53173540411138e-16, 1.16892301877735e-15, -1.04324684545047e-15}, - {-5.57345639727027e-16, -2.93949227634932e-16, 7.47621406284534e-18, -5.36416885470756e-17, -2.87213280230513e-16}, - {1.73219775047208e-16, 2.05017387523061e-17, 9.08873886345587e-18, -2.86881547225742e-18, -1.25303645304992e-17}, - {-7.30829109684568e-18, 2.03711261415353e-18, 7.62162636124024e-19, -7.54847922012517e-19, -8.85105098195030e-19}, - {5.62039968280587e-18, -1.38144206573507e-19, 1.68028711767211e-20, 1.81223858251981e-19, -8.50245194985878e-20} - }; - - /** Legendre functions anm for coefficient bw.*/ - private static final double[][] BW_A = { - {0.00136127467401223, -6.83476317823061e-07, -1.37211986707674e-06, 7.02561866200582e-07, -2.16342338010651e-07}, - {-9.53197486400299e-06, 6.58703762338336e-06, 2.42000663952044e-06, -6.04283463108935e-07, 2.02144424676990e-07}, - {-6.76728911259359e-06, 6.03830755085583e-07, -8.72568628835897e-08, 2.21750344140938e-06, 1.05146032931020e-06}, - {-3.21102832397338e-05, -7.88685357568093e-06, -2.55495673641049e-06, -1.99601934456719e-06, -4.62005252198027e-07}, - {-7.84639263523250e-07, 3.11624739733849e-06, 9.02170019697389e-07, 6.37066632506008e-07, -9.44485038780872e-09}, - {2.19476873575507e-06, -2.20580510638233e-07, 6.94761415598378e-07, 4.80770865279717e-07, -1.34357837196401e-07}, - {2.18469215148328e-05, -1.80674174262038e-06, -1.52754285605060e-06, -3.51212288219241e-07, 2.73741237656351e-06}, - {2.85579058479116e-06, 1.57201369332361e-07, -2.80599072875081e-07, -4.91267304946072e-07, -2.11648188821805e-07}, - {2.81729255594770e-06, 3.02487362536122e-07, -1.64836481475431e-07, -2.11607615408593e-07, -6.47817762225366e-08}, - {1.31809947620223e-07, -1.58289524114549e-07, -7.05580919885505e-08, 5.56781440550867e-08, 1.23403290710365e-08}, - {-1.29252282695869e-05, -1.07247072037590e-05, -3.31109519638196e-06, 2.13776673779736e-06, -1.49519398373391e-07}, - {1.81685152305722e-06, -1.17362204417861e-06, -3.19205277136370e-08, 4.09166457255416e-07, 1.53286667406152e-07}, - {1.63477723125362e-06, -2.68584775517243e-08, 4.94662064805191e-09, -7.09027987928288e-08, 4.44353430574937e-08}, - {-2.13090618917978e-07, 4.05836983493219e-08, 2.94495876336549e-08, -1.75005469063176e-08, -3.03015988647002e-09}, - {-2.16074435298006e-09, 9.37631708987675e-09, -2.05996036369828e-08, 6.97068002894092e-09, -8.90988987979604e-09}, - {1.38047798906967e-05, 2.05528261553901e-05, 1.59072148872708e-05, 7.34088731264443e-07, 1.28226710383580e-06}, - {7.08175753966264e-07, -9.27988276636505e-07, 1.60535820026081e-07, -3.27296675122065e-07, -2.20518321170684e-07}, - {1.90932483086199e-07, -7.44215272759193e-08, 1.81330673333187e-08, 4.37149649043616e-08, 4.18884335594172e-08}, - {-5.37009063880924e-08, 2.22870057779431e-08, 1.73740123037651e-08, -4.45137302235032e-09, 9.44721910524571e-09}, - {-6.83406949047909e-08, -1.95046676795923e-10, 2.57535903049686e-09, 4.82643164083020e-09, 3.37657333705158e-09}, - {3.96128688448981e-09, -6.63809403270686e-10, 2.44781464212534e-10, 5.92280853590699e-11, -4.78502591970721e-10}, - {1.75859399041414e-05, -2.81238050668481e-06, -2.43670534594848e-06, 3.58244562699714e-06, -1.76547446732691e-06}, - {-1.06451311473304e-07, 1.54336689617184e-06, -2.00690000442673e-07, 1.38790047911880e-09, -1.62490619890017e-07}, - {-2.72757421686155e-07, 1.71139266205398e-07, -2.55080309401917e-08, -8.40793079489831e-09, -1.01129447760167e-08}, - {2.92966025844079e-08, -2.07556718857313e-08, 5.45985315647905e-09, 8.76857690274150e-09, 1.06785510440474e-08}, - {-1.22059608941331e-08, 6.52491630264276e-09, -1.79332492326928e-10, 3.75921793745396e-10, -7.06416506254786e-10}, - {1.63224355776652e-09, 4.95586028736232e-10, -3.07879011759040e-10, -7.78354087544277e-11, 1.43959047067250e-10}, - {3.86319414653663e-10, -2.06467134617933e-10, 4.37330971382694e-11, -5.00421056263711e-11, -9.40237773015723e-12}, - {-1.23856142706451e-05, 7.61047394008415e-06, -1.99104114578138e-07, 6.86177748886858e-07, -1.09466747592827e-07}, - {2.99866062403128e-07, 1.87525561397390e-07, 4.99374806994715e-08, 4.86229763781404e-07, 4.46570575517658e-07}, - {-5.05748332368430e-07, 1.95523624722285e-08, -9.17535435911345e-08, -2.56671607433547e-08, -7.11896201616653e-08}, - {-2.66062200406494e-08, -5.40470019739274e-09, -2.29718660244954e-09, -3.73328592264404e-09, 3.38748313712376e-09}, - {5.30855327954894e-10, 5.28851845648032e-10, -2.22278913745418e-10, -5.52628653064771e-11, -9.24825145219684e-10}, - {6.03737227573716e-10, -3.52190673510919e-12, -1.30371720641414e-10, -9.12787239944822e-12, 6.42187285537238e-12}, - {1.78081862458539e-10, 2.93772078656037e-12, -1.04698379945322e-11, -2.82260024833024e-11, -5.61810459067525e-12}, - {9.35003092299580e-12, -8.23133834521577e-13, 5.54878414224198e-13, -3.62943215777181e-13, 2.38858933771653e-12}, - {-1.31216096107331e-05, -5.70451670731759e-06, -5.11598683573971e-06, -4.99990779887599e-06, 1.27389320221511e-07}, - {-1.23108260369048e-06, 5.53093245213587e-07, 8.60093183929302e-07, 2.65569700925696e-07, 1.95485134805575e-07}, - {-2.29647072638049e-07, -5.45266515081825e-08, 2.85298129762263e-08, 1.98167939680185e-08, 5.52227340898335e-09}, - {-2.73844745019857e-08, -4.48345173291362e-10, -1.93967347049382e-09, -1.41508853776629e-09, -1.75456962391145e-09}, - {-2.68863184376108e-11, -2.20546981683293e-09, 6.56116990576877e-10, 1.27129855674922e-10, -2.32334506413213e-10}, - {1.98303136881156e-10, 6.04782006047075e-11, 2.91291115431570e-11, 6.18098615782757e-11, -3.82682292530379e-11}, - {9.48294455071158e-12, -3.05873596453015e-13, 5.31539408055057e-13, -7.31016438665600e-12, -1.19921002209198e-11}, - {-2.25188050845725e-11, -3.91627574966393e-13, -6.80217235976769e-13, 5.91033607278405e-13, 5.02991534452191e-13}, - {1.29532063896247e-12, 1.66337285851564e-13, 3.25543028344555e-13, 1.89143357962363e-13, 3.32288378169726e-13}, - {-2.45864358781728e-06, 4.49460524898260e-06, 1.03890496648813e-06, -2.73783420376785e-06, 7.12695730642593e-07}, - {-9.27805078535168e-07, -4.97733876686731e-07, 9.18680298906510e-08, -2.47200617423980e-07, 6.16163630140379e-08}, - {-1.39623661883136e-08, -1.12580495666505e-07, 2.61821435950379e-08, -2.31875562002885e-08, 5.72679835033659e-08}, - {-9.52538983318497e-09, -5.40909215302433e-09, 1.88698793952475e-09, -4.08127746406372e-09, 1.09534895853812e-10}, - {3.79767457525741e-09, 1.11549801373366e-10, -6.45504957274111e-10, 3.05477141010356e-10, 1.26261210565856e-10}, - {5.08813577945300e-11, 1.43250547678637e-11, 8.81616572082448e-12, 2.58968878880804e-11, 3.83421818249954e-11}, - {8.95094368142044e-12, -3.26220304555971e-12, -1.28047847191896e-12, 2.67562170258942e-12, 2.72195031576670e-12}, - {-6.47181697409757e-12, 1.13776457455685e-12, 2.84856274334969e-13, -7.63667272085395e-14, -1.34451657758826e-13}, - {-1.25291265888343e-12, 8.63500441050317e-14, -1.21307856635548e-13, 5.12570529540511e-14, 3.32389276976573e-14}, - {3.73573418085813e-14, -5.37808783042784e-16, -4.23430408270850e-16, -4.75110565740493e-15, 6.02553212780166e-15}, - {8.95483987262751e-06, -3.90778212666235e-06, -1.12115019808259e-06, 1.78678942093383e-06, 1.46806344157962e-06}, - {-4.59185232678613e-07, 1.09497995905419e-07, 1.31663977640045e-07, 4.20525791073626e-08, -9.71470741607431e-08}, - {1.63399802579572e-07, 1.50909360648645e-08, -1.11480472593347e-08, -1.84000857674573e-08, 7.82124614794256e-09}, - {1.22887452385094e-08, -4.06647399822746e-10, -6.49120327585597e-10, 8.63651225791194e-10, -2.73440085913102e-09}, - {2.51748630889583e-09, 4.79895880425564e-10, -2.44908073860844e-10, 2.56735882664876e-10, -1.64815306286912e-10}, - {4.85671381736718e-11, -2.51742732115131e-11, -2.60819437993179e-11, 6.12728324086123e-12, 2.16833310896138e-11}, - {4.11389702320298e-12, -8.09433180989935e-13, -1.19812498226024e-12, 1.46885737888520e-12, 3.15807685137836e-12}, - {-1.47614580597013e-12, 4.66726413909320e-13, 1.72089709006255e-13, 1.13854935381418e-13, 2.77741161317003e-13}, - {-1.02257724967727e-13, 1.10394382923502e-13, -3.14153505370805e-15, 2.41103099110106e-14, 2.13853053149771e-14}, - {-3.19080885842786e-14, -9.53904307973447e-15, 2.74542788156379e-15, 2.33797859107844e-15, -2.53192474907304e-15}, - {-5.87702222126367e-15, -1.80133850930249e-15, -3.09793125614454e-16, -1.04197538975295e-16, 3.72781664701327e-16}, - {1.86187054729085e-06, 8.33098045333428e-06, 3.18277735484232e-06, -7.68273797022231e-07, -1.52337222261696e-06}, - {-5.07076646593648e-07, -8.61959553442156e-07, -3.51690005432816e-07, -4.20797082902431e-07, -3.07652993252673e-07}, - {-7.38992472164147e-08, -8.39473083080280e-08, -2.51587083298935e-08, 7.30691259725451e-09, -3.19457155958983e-08}, - {-1.99777182012924e-09, -3.21265085916022e-09, -4.84477421865675e-10, -1.82924814205799e-09, -3.46664344655997e-10}, - {-7.05788559634927e-11, 1.21840735569025e-10, 7.97347726425926e-11, 1.08275679614409e-10, -1.17891254809785e-10}, - {1.10299718947774e-11, -3.22958261390263e-11, -1.43535798209229e-11, 6.87096504209595e-12, -6.64963212272352e-12}, - {-6.47393639740084e-12, 1.03156978325120e-12, -9.20099775082358e-14, -2.40150316641949e-13, 1.14008812047857e-12}, - {-1.23957846397250e-13, 2.85996703969692e-13, 1.91579874982553e-13, 5.20597174693064e-14, -4.06741434883370e-14}, - {-2.35479068911236e-14, 1.97847338186993e-14, 1.58935977518516e-15, -2.32217195254742e-15, -8.48611789490575e-15}, - {1.03992320391626e-14, 1.54017082092642e-15, 1.05950035082788e-16, -1.17870898461353e-15, -1.10937420707372e-15}, - {-1.09011948374520e-15, -6.04168007633584e-16, -9.10901998157436e-17, 1.98379116989461e-16, -1.03715496658498e-16}, - {-1.38171942108278e-16, -6.33037999097522e-17, -1.38777695011470e-17, 1.94191397045401e-17, 5.70055906754485e-18}, - {1.92989406002085e-06, -3.82662130483128e-06, -4.60189561036048e-07, 2.24290587856309e-06, 1.40544379451550e-06}, - {6.49033717633394e-08, 2.41396114435326e-07, 2.73948898223321e-07, 1.10633664439332e-07, -3.19555270171075e-08}, - {-2.91988966963297e-08, -6.03828192816571e-09, 1.18462386444840e-08, 1.32095545004128e-08, -5.06572721528914e-09}, - {7.31079058474148e-09, -8.42775299751834e-10, 1.10190810090667e-09, 1.96592273424306e-09, -2.13135932785688e-09}, - {7.06656405314388e-11, 1.43441125783756e-10, 1.46962246686924e-10, 7.44592776425197e-11, -3.64331892799173e-11}, - {-2.52393942119372e-11, 1.07520964869263e-11, 5.84669886072094e-12, 6.52029744217103e-12, 1.82947123132059e-12}, - {-4.15669940115121e-12, -1.95963254053648e-13, 2.16977822834301e-13, -2.84701408462031e-13, 4.27194601040231e-13}, - {3.07891105454129e-13, 1.91523190672955e-13, 1.05367297580989e-13, -5.28136363920236e-14, -3.53364110005917e-14}, - {7.02156663274738e-15, 9.52230536780849e-15, -3.41019408682733e-15, -3.59825303352899e-15, -2.62576411636150e-15}, - {-1.75110277413804e-15, 5.29265220719483e-16, 4.45015980897919e-16, -3.80179856341347e-16, -4.32917763829695e-16}, - {1.16038609651443e-16, -6.69643574373352e-17, 2.65667154817303e-17, -9.76010333683956e-17, 4.07312981076655e-17}, - {5.72659246346386e-18, 1.30357528108671e-18, 2.49193258417535e-18, 1.76247014075584e-18, 7.59614374197688e-19}, - {1.03352170833303e-17, -2.30633516638829e-18, 2.84777940620193e-18, -7.72161347944693e-19, 6.07028034506380e-19} - }; - - /** Legendre functions anm for coefficient ch.*/ - private static final double[][] CH_A = { - {0.0571481238161787, 3.35402081801137e-05, 3.15988141788728e-05, -1.34477341887086e-05, -2.61831023577773e-07}, - {5.77367395845715e-05, -0.000669057185209558, -6.51057691648904e-05, -1.61830149147091e-06, 8.96771209464758e-05}, - {-8.50773002452907e-05, -4.87106614880272e-05, 4.03431160775277e-05, 2.54090162741464e-06, -5.59109319864264e-06}, - {0.00150536423187709, 0.000611682258892697, 0.000369730024614855, -1.95658439780282e-05, -3.46246726553700e-05}, - {-2.32168718433966e-05, -0.000127478686553809, -9.00292451740728e-05, -6.07834315901830e-05, -1.04628419422714e-05}, - {-1.38607250922551e-06, -3.97271603842309e-06, -8.16155320152118e-07, 5.73266706046665e-07, 2.00366060212696e-07}, - {6.52491559188663e-05, -0.00112224323460183, -0.000344967958304075, -7.67282640947300e-05, 0.000107907110551939}, - {-0.000138870461448036, -7.29995695401936e-05, 5.35986591445824e-05, 9.03804869703890e-06, 8.61370129482732e-06}, - {-9.98524443968768e-07, -6.84966792665998e-08, 1.47478021860771e-07, 1.94857794008064e-06, 7.17176852732910e-07}, - {1.27066367911720e-06, 1.12113289164288e-06, 2.71525688515375e-07, -2.76125723009239e-07, -1.05429690305013e-07}, - {-0.000377264999981652, 0.000262691217024294, 0.000183639785837590, 3.93177048515576e-06, -6.66187081899168e-06}, - {-4.93720951871921e-05, -0.000102820030405771, -5.69904376301748e-05, -3.79603438055116e-05, -3.96726017834930e-06}, - {-2.21881958961135e-06, -1.40207117987894e-06, 1.60956630798516e-07, 2.06121145135022e-06, 6.50944708093149e-07}, - {2.21876332411271e-07, 1.92272880430386e-07, -6.44016558013941e-09, -1.40954921332410e-07, -4.26742169137667e-07}, - {-3.51738525149881e-08, 2.89616194332516e-08, -3.40343352397886e-08, -2.89763392721812e-08, -6.40980581663785e-10}, - {3.51240856823468e-05, -0.000725895015345786, -0.000322514037108045, -0.000106143759981636, 4.08153152459337e-05}, - {-2.36269716929413e-05, -4.20691836557932e-05, 1.43926743222922e-05, 2.61811210631784e-05, 2.09610762194903e-05}, - {-7.91765756673890e-07, 1.64556789159745e-06, -9.43930166276555e-07, 6.46641738736139e-07, -5.91509547299176e-07}, - {3.92768838766879e-07, -1.98027731703690e-07, -5.41303590057253e-08, -4.21705797874207e-07, -6.06042329660681e-08}, - {-1.56650141024305e-08, 7.61808165752027e-08, -1.81900460250934e-08, 1.30196216971675e-08, 1.08616031342379e-08}, - {-2.80964779829242e-08, -7.25951488826103e-09, -2.59789823306225e-09, -2.79271942407154e-09, 4.10558774868586e-09}, - {-0.000638227857648286, -0.000154814045363391, 7.78518327501759e-05, -2.95961469342381e-05, 1.15965225055757e-06}, - {4.47833146915112e-06, 1.33712284237555e-05, 3.61048816552123e-06, -2.50717844073547e-06, -1.28100822021734e-05}, - {-2.26958070007455e-06, 2.57779960912242e-06, 1.08395653197976e-06, 1.29403393862805e-07, -1.04854652812567e-06}, - {-3.98954043463392e-07, -2.26931182815454e-07, -1.09169545045028e-07, -1.49509536031939e-07, -3.98376793949903e-07}, - {2.30418911071110e-08, 1.23098508481555e-08, -1.71161401463708e-08, 2.35829696577657e-09, 1.31136164162040e-08}, - {3.69423793101582e-09, 3.49231027561927e-10, -1.18581468768647e-09, 5.43180735828820e-10, 5.43192337651588e-10}, - {-1.38608847117992e-09, -1.86719145546559e-10, -8.13477384765498e-10, 2.01919878240491e-10, 1.00067892622287e-10}, - {-4.35499078415956e-05, 0.000450727967957804, 0.000328978494268850, -3.05249478582848e-05, -3.21914834544310e-05}, - {1.24887940973241e-05, 1.34275239548403e-05, 1.11275518344713e-06, 7.46733554562851e-06, -2.12458664760353e-06}, - {9.50250784948476e-07, 2.34367372695203e-06, -5.43099244798980e-07, -4.35196904508734e-07, -8.31852234345897e-07}, - {5.91775478636535e-09, -1.48970922508592e-07, 2.99840061173840e-08, -1.30595933407792e-07, 1.27136765045597e-07}, - {-1.78491083554475e-08, 1.76864919393085e-08, -1.96740493482011e-08, 1.21096708004261e-08, 2.95518703155064e-10}, - {1.75053510088658e-09, -1.31414287871615e-09, -1.44689439791928e-09, 1.14682483668460e-09, 1.74488616540169e-09}, - {1.08152964586251e-09, -3.85678162063266e-10, -2.77851016629979e-10, 3.89890578625590e-11, -2.54627365853495e-10}, - {-1.88340955578221e-10, 5.19645384002867e-11, 2.14131326027631e-11, 1.24027770392728e-11, -9.42818962431967e-12}, - {0.000359777729843898, -0.000111692619996219, -6.87103418744904e-05, 0.000115128973879551, 7.59796247722486e-05}, - {5.23717968000879e-05, 1.32279078116467e-05, -5.72277317139479e-07, -7.56326558610214e-06, -1.95749622214651e-05}, - {1.00109213210139e-06, -2.75515216592735e-07, -1.13393194050846e-06, -4.75049734870663e-07, -3.21499480530932e-07}, - {-2.07013716598890e-07, -7.31392258077707e-08, -3.96445714084160e-08, 3.21390452929387e-08, -1.43738764991525e-08}, - {2.03081434931767e-09, -1.35423687136122e-08, -4.47637454261816e-09, 2.18409121726643e-09, -3.74845286805217e-09}, - {3.17469255318367e-09, 2.44221027314129e-10, -2.46820614760019e-10, 7.55851003884434e-10, 6.98980592550891e-10}, - {9.89541493531067e-11, -2.78762878057315e-11, -2.10947962916771e-10, 3.77882267360636e-11, -1.20009542671532e-12}, - {5.01720575730940e-11, 1.66470417102135e-11, -7.50624817938091e-12, 9.97880221482238e-12, 4.87141864438892e-12}, - {2.53137945301589e-11, 1.93030083090772e-12, -1.44708804231290e-12, -1.77837100743423e-12, -8.10068935490951e-13}, - {0.000115735341520738, 0.000116910591048350, 8.36315620479475e-05, 1.61095702669207e-05, -7.53084853489862e-05}, - {-9.76879433427199e-06, 9.16968438003335e-06, -8.72755127288830e-06, -1.30077933880053e-05, -9.78841937993320e-06}, - {1.04902782517565e-07, 2.14036988364936e-07, -7.19358686652888e-07, 1.12529592946332e-07, 7.07316352860448e-07}, - {7.63177265285080e-08, 1.22781974434290e-07, 8.99971272969286e-08, 5.63482239352990e-08, 4.31054352285547e-08}, - {3.29855763107355e-09, -6.95004336734441e-09, -6.52491370576354e-09, 1.97749180391742e-09, 3.51941791940498e-09}, - {3.85373745846559e-10, 1.65754130924183e-10, -3.31326088103057e-10, 5.93256024580436e-10, 1.27725220636915e-10}, - {-1.08840956376565e-10, -4.56042860268189e-11, -4.77254322645633e-12, -2.94405398621875e-12, -3.07199979999475e-11}, - {2.07389879095010e-11, 1.51186798732451e-11, 9.28139802941848e-12, 5.92738269687687e-12, 9.70337402306505e-13}, - {-2.85879708060306e-12, 1.92164314717053e-13, 4.02664678967890e-14, 5.18246319204277e-13, -7.91438726419423e-13}, - {6.91890667590734e-13, -8.49442290988352e-14, -5.54404947212402e-15, 9.71093377538790e-15, -5.33714333415971e-14}, - {-5.06132972789792e-05, -4.28348772058883e-05, -6.90746551020305e-05, 8.48380415176836e-05, 7.04135614675053e-05}, - {-1.27945598849788e-05, -1.92362865537803e-05, -2.30971771867138e-06, -8.98515975724166e-06, 5.25675205004752e-06}, - {-8.71907027470177e-07, -1.02091512861164e-06, -1.69548051683864e-07, 4.87239045855761e-07, 9.13163249899837e-07}, - {-6.23651943425918e-08, 6.98993315829649e-08, 5.91597766733390e-08, 4.36227124230661e-08, 6.45321798431575e-08}, - {-1.46315079552637e-10, -7.85142670184337e-09, 1.48788168857903e-09, 2.16870499912160e-09, -1.16723047065545e-09}, - {3.31888494450352e-10, 1.90931898336457e-10, -3.13671901557599e-11, 2.60711798190524e-10, 8.45240112207997e-11}, - {1.36645682588537e-11, -5.68830303783976e-12, 1.57518923848140e-11, -1.61935794656758e-11, -4.16568077748351e-12}, - {9.44684950971905e-13, 7.30313977131995e-12, 3.14451447892684e-12, 6.49029875639842e-13, -9.66911019905919e-13}, - {-8.13097374090024e-13, 5.23351897822186e-13, 8.94349188113951e-14, -1.33327759673270e-13, -4.04549450989029e-13}, - {-3.76176467005839e-14, -6.19953702289713e-14, -3.74537190139726e-14, 1.71275486301958e-14, -3.81946773167132e-14}, - {-4.81393385544160e-14, 3.66084990006325e-15, 3.10432030972253e-15, -4.10964475657416e-15, -6.58644244242900e-15}, - {-7.81077363746945e-05, -0.000254773632197303, -0.000214538508009518, -3.80780934346726e-05, 1.83495359193990e-05}, - {5.89140224113144e-06, -3.17312632433258e-06, -3.81872516710791e-06, -2.27592226861647e-06, 1.57044619888023e-06}, - {-1.44272505088690e-06, -1.10236588903758e-07, 2.64336813084693e-07, 4.76074163332460e-07, 4.28623587694570e-07}, - {3.98889120733904e-08, -1.29638005554027e-08, -4.13668481273828e-08, 1.27686793719542e-09, -3.54202962042383e-08}, - {1.60726837551750e-09, -2.70750776726156e-09, 2.79387092681070e-09, -3.01419734793998e-10, -1.29101669438296e-10}, - {-2.55708290234943e-10, 2.27878015173471e-11, -6.43063443462716e-12, 1.26531554846856e-10, -1.65822147437220e-10}, - {-3.35886470557484e-11, -3.51895009091595e-12, 5.80698399963198e-12, -2.84881487149207e-12, 8.91708061745902e-12}, - {-3.12788523950588e-12, 3.35366912964637e-12, 2.52236848033838e-12, -8.12801050709184e-13, -2.63510394773892e-13}, - {6.83791881183142e-14, 2.41583263270381e-13, 8.58807794189356e-14, -5.12528492761045e-14, -1.40961725631276e-13}, - {-1.28585349115321e-14, -2.11049721804969e-14, 5.26409596614749e-15, -4.31736582588616e-15, -1.60991602619068e-14}, - {-9.35623261461309e-15, -3.94384886372442e-16, 5.04633016896942e-16, -5.40268998456055e-16, -1.07857944298104e-15}, - {8.79756791888023e-16, 4.52529935675330e-16, 1.36886341163227e-16, -1.12984402980452e-16, 6.30354561057224e-18}, - {0.000117829256884757, 2.67013591698442e-05, 2.57913446775250e-05, -4.40766244878807e-05, -1.60651761172523e-06}, - {-1.87058092029105e-05, 1.34371169060024e-05, 5.59131416451555e-06, 4.50960364635647e-06, 2.87612873904633e-06}, - {2.79835536517287e-07, 8.93092708148293e-07, 8.37294601021795e-07, -1.99029785860896e-08, -8.87240405168977e-08}, - {4.95854313394905e-08, -1.44694570735912e-08, 2.51662229339375e-08, -3.87086600452258e-09, 2.29741919071270e-08}, - {4.71497840986162e-09, 2.47509999454076e-09, 1.67323845102824e-09, 8.14196768283530e-10, -3.71467396944165e-10}, - {-1.07340743907054e-10, -8.07691657949326e-11, -5.99381660248133e-11, 2.33173929639378e-12, -2.26994195544563e-11}, - {-3.83130441984224e-11, -5.82499946138714e-12, 1.43286311435124e-11, 3.15150503353387e-12, 5.97891025146774e-12}, - {-5.64389191072230e-13, 9.57258316335954e-13, 1.12055192185939e-12, -4.42417706775420e-13, -9.93190361616481e-13}, - {1.78188860269677e-13, 7.82582024904950e-14, 5.18061650118009e-14, 2.13456507353387e-14, -5.26202113779510e-14}, - {-8.18481324740893e-15, -3.71256746886786e-15, 4.23508855164371e-16, -2.91292502923102e-15, -1.15454205389350e-14}, - {6.16578691696810e-15, 6.74087154080877e-16, 5.71628946437034e-16, -2.05251213979975e-16, -7.25999138903781e-16}, - {9.35481959699383e-17, 6.23535830498083e-17, 3.18076728802060e-18, -2.92353209354587e-17, 7.65216088665263e-19}, - {2.34173078531701e-17, -8.30342420281772e-18, -4.33602329912952e-18, 1.90226281379981e-18, -7.85507922718903e-19} - }; - - /** Legendre functions anm for coefficient cw.*/ - private static final double[][] CW_A = { - {0.0395329695826997, -0.000131114380761895, -0.000116331009006233, 6.23548420410646e-05, 5.72641113425116e-05}, - {-0.000441837640880650, 0.000701288648654908, 0.000338489802858270, 3.76700309908602e-05, -8.70889013574699e-06}, - {1.30418530496887e-05, -0.000185046547597376, 4.31032103066723e-05, 0.000105583334124319, 3.23045436993589e-05}, - {3.68918433448519e-05, -0.000219433014681503, 3.46768613485000e-06, -9.17185187163528e-05, -3.69243242456081e-05}, - {-6.50227201116778e-06, 2.07614874282187e-05, -5.09131314798362e-05, -3.08053225174359e-05, -4.18483655873918e-05}, - {2.67879176459056e-05, -6.89303730743691e-05, 2.11046783217168e-06, 1.93163912538178e-05, -1.97877143887704e-06}, - {0.000393937595007422, -0.000452948381236406, -0.000136517846073846, 0.000138239247989489, 0.000133175232977863}, - {5.00214539435002e-05, 3.57229726719727e-05, -9.38010547535432e-07, -3.52586798317563e-05, -7.01218677681254e-06}, - {3.91965314099929e-05, 1.02236686806489e-05, -1.95710695226022e-05, -5.93904795230695e-06, 3.24339769876093e-06}, - {6.68158778290653e-06, -8.10468752307024e-06, -9.91192994096109e-06, -1.89755520007723e-07, -3.26799467595579e-06}, - {0.000314196817753895, -0.000296548447162009, -0.000218410153263575, -1.57318389871000e-05, 4.69789570185785e-05}, - {0.000104597721123977, -3.31000119089319e-05, 5.60326793626348e-05, 4.71895007710715e-05, 3.57432326236664e-05}, - {8.95483021572039e-06, 1.44019305383365e-05, 4.87912790492931e-06, -3.45826387853503e-06, 3.23960320438157e-06}, - {-1.35249651009930e-05, -2.49349762695977e-06, -2.51509483521132e-06, -9.14254874104858e-07, -8.57897406100890e-07}, - {-1.68143325235195e-06, 1.72073417594235e-06, 1.38765993969565e-06, 4.09770982137530e-07, -6.60908742097123e-07}, - {-0.000639889366487161, 0.00120194042474696, 0.000753258598887703, 3.87356377414663e-05, 1.31231811175345e-05}, - {2.77062763606783e-05, -9.51425270178477e-06, -6.61068056107547e-06, -1.38713669012109e-05, 9.84662092961671e-06}, - {-2.69398078539471e-06, 6.50860676783123e-06, 3.80855926988090e-06, -1.98076068364785e-06, 1.17187335666772e-06}, - {-2.63719028151905e-06, 5.03149473656743e-07, 7.38964893399716e-07, -8.38892485369078e-07, 1.30943917775613e-06}, - {-1.56634992245479e-06, -2.97026487417045e-08, 5.06602801102463e-08, -4.60436007958792e-08, -1.62536449440997e-07}, - {-2.37493912770935e-07, 1.69781593069938e-08, 8.35178275224265e-08, -4.83564044549811e-08, -4.96448864199318e-08}, - {0.00134012259587597, -0.000250989369253194, -2.97647945512547e-05, -6.47889968094926e-05, 8.41302130716859e-05}, - {-0.000113287184900929, 4.78918993866293e-05, -3.14572113583139e-05, -2.10518256626847e-05, -2.03933633847417e-05}, - {-4.97413321312139e-07, 3.72599822034753e-06, -3.53221588399266e-06, -1.05232048036416e-06, -2.74821498198519e-06}, - {4.81988542428155e-06, 4.21400219782474e-07, 1.02814808667637e-06, 4.40299068486188e-09, 3.37103399036634e-09}, - {1.10140301678818e-08, 1.90257670180182e-07, -1.00831353341885e-08, 1.44860642389714e-08, -5.29882089987747e-08}, - {6.12420414245775e-08, -4.48953461152996e-09, -1.38837603709003e-08, -2.05533675904779e-08, 1.49517908802329e-09}, - {9.17090243673643e-10, -9.24878857867367e-09, -2.30856560363943e-09, -4.36348789716735e-09, -4.45808881183025e-10}, - {-0.000424912699609112, -0.000114365438471564, -0.000403200981827193, 4.19949560550194e-05, -3.02068483713739e-05}, - {3.85435472851225e-05, -5.70726887668306e-05, 4.96313706308613e-07, 1.02395703617082e-05, 5.85550000567006e-06}, - {-7.38204470183331e-06, -4.56638770109511e-06, -3.94007992121367e-06, -2.16666812189101e-06, -4.55694264113194e-06}, - {5.89841165408527e-07, 1.40862905173449e-08, 1.08149086563211e-07, -2.18592601537944e-07, -3.78927431428119e-07}, - {4.85164687450468e-08, 8.34273921293655e-08, 1.47489605513673e-08, 6.01494125001291e-08, 6.43812884159484e-09}, - {1.13055580655363e-08, 3.50568765400469e-09, -5.09396162501750e-09, -1.83362063152411e-09, -4.11227251553035e-09}, - {3.16454132867156e-09, -1.39634794131087e-09, -7.34085003895929e-10, -7.55541371271796e-10, -1.57568747643705e-10}, - {1.27572900992112e-09, -3.51625955080441e-10, -4.84132020565098e-10, 1.52427274930711e-10, 1.27466120431317e-10}, - {-0.000481655666236529, -0.000245423313903835, -0.000239499902816719, -0.000157132947351028, 5.54583099258017e-05}, - {-1.52987254785589e-05, 2.78383892116245e-05, 4.32299123991860e-05, 1.70981319744327e-05, -1.35090841769225e-06}, - {-8.65400907717798e-06, -6.51882656990376e-06, -2.43810171017369e-07, 8.54348785752623e-07, 2.98371863248143e-07}, - {-1.68155571776752e-06, -3.53602587563318e-07, -1.00404435881759e-07, -2.14162249012859e-08, -2.42131535531526e-07}, - {-1.08048603277187e-08, -9.78850785763030e-08, -2.32906554437417e-08, 2.22003630858805e-08, -2.27230368089683e-09}, - {-5.98864391551041e-09, 7.38970926486848e-09, 3.61322835311957e-09, 3.70037329172919e-09, -3.41121137081362e-09}, - {-7.33113754909726e-10, -9.08374249335220e-11, -1.78204392133739e-10, 8.28618491929026e-11, -1.32966817912373e-10}, - {-5.23340481314676e-10, 1.36403528233346e-10, -7.04478837151279e-11, -6.83175201536443e-12, -2.86040864071134e-12}, - {3.75347503578356e-11, -1.08518134138781e-11, -2.53583751744508e-12, 1.00168232812303e-11, 1.74929602713312e-11}, - {-0.000686805336370570, 0.000591849814585706, 0.000475117378328026, -2.59339398048415e-05, 3.74825110514968e-05}, - {3.35231363034093e-05, 2.38331521146909e-05, 7.43545963794093e-06, -3.41430817541849e-06, 7.20180957675353e-06}, - {3.60564374432978e-07, -3.13300039589662e-06, -6.38974746108020e-07, -8.63985524672024e-07, 2.43367665208655e-06}, - {-4.09605238516094e-07, -2.51158699554904e-07, -1.29359217235188e-07, -2.27744642483133e-07, 7.04065989970205e-08}, - {6.74886341820129e-08, -1.02009407061935e-08, -3.30790296448812e-08, 1.64959795655031e-08, 1.40641779998855e-08}, - {1.31706886235108e-09, -1.06243701278671e-09, -2.85573799673944e-09, 3.72566568681289e-09, 2.48402582003925e-09}, - {-3.68427463251097e-11, -1.90028122983781e-10, -3.98586561768697e-11, 1.14458831693287e-11, -2.27722300377854e-12}, - {-7.90029729611056e-11, 3.81213646526419e-11, 4.63303426711788e-11, 1.52294835905903e-11, -2.99094751490726e-12}, - {-2.36146602045017e-11, 1.03852674709985e-11, -4.47242126307100e-12, 5.30884113537806e-12, 1.68499023262969e-12}, - {-3.30107358134527e-13, -4.73989085379655e-13, 5.17199549822684e-13, 2.34951744478255e-13, 2.05931351608192e-13}, - {0.000430215687511780, -0.000132831373000014, -3.41830835017045e-05, 4.70312161436033e-06, -3.84807179340006e-05}, - {1.66861163032403e-05, -8.10092908523550e-06, 8.20658107437905e-06, 6.12399025026683e-06, -1.85536495631911e-06}, - {1.53552093641337e-06, 2.19486495660361e-06, -1.07253805120137e-06, -4.72141767909137e-07, 4.00744581573216e-07}, - {2.56647305130757e-07, -8.07492046592274e-08, -2.05858469296168e-07, 1.09784168930599e-07, -7.76823030181225e-08}, - {1.77744008115031e-08, 1.64134677817420e-08, 4.86163044879020e-09, 1.13334251800856e-08, -7.17260621115426e-09}, - {1.61133063219326e-09, -1.85414677057024e-09, -2.13798537812651e-09, 1.15255123229679e-09, 2.24504700129464e-09}, - {1.23344223096739e-10, -1.20385012169848e-10, -2.18038256346433e-12, 3.23033120628279e-11, 8.01179568213400e-11}, - {-6.55745274387847e-12, 1.22127104697198e-11, 5.83805016355883e-12, -8.31201582509817e-12, 1.90985373872656e-12}, - {-2.89199983667265e-12, 5.05962500506667e-12, 1.28092925110279e-12, 5.60353813743813e-13, 1.76753731968770e-12}, - {-1.61678729774956e-13, -3.92206170988615e-13, -9.04941327579237e-14, 1.89847694200763e-13, 4.10008676756463e-14}, - {-1.16808369005656e-13, -9.97464591430510e-14, 7.46366550245722e-15, 2.53398578153179e-14, 1.06510689748906e-14}, - {-0.000113716921384790, -0.000131902722651488, -0.000162844886485788, 7.90171538739454e-06, -0.000178768066961413}, - {-2.13146535366500e-06, -3.57818705543597e-05, -1.50825855069298e-05, -2.17909259570022e-05, -8.19332236308581e-06}, - {-2.88001138617357e-06, -2.09957465440793e-06, 6.81466526687552e-08, 3.58308906974448e-07, -4.18502067223724e-07}, - {-1.10761444317605e-07, 6.91773860777929e-08, 8.17125372450372e-08, -2.16476237959181e-08, 7.59221970502074e-08}, - {-9.56994224818941e-09, 6.64104921728432e-09, 6.33077902928348e-09, 2.85721181743727e-09, -6.39666681678123e-09}, - {4.62558627839842e-10, -1.69014863754621e-09, -2.80260429599733e-10, 4.27558937623863e-11, -1.66926133269027e-10}, - {-7.23385132663753e-11, 5.51961193545280e-11, 3.04070791942335e-11, 3.23227055919062e-12, 8.47312431934829e-11}, - {-1.61189613765486e-11, 1.66868155925172e-11, 1.05370341694715e-11, -4.41495859079592e-12, -2.24939051401750e-12}, - {-8.72229568056267e-13, 1.88613726203286e-12, 1.21711137534390e-14, -1.13342372297867e-12, -6.87151975256052e-13}, - {7.99311988544090e-15, 4.46150979586709e-14, 7.50406779454998e-14, -3.20385428942275e-14, -1.26543636054393e-14}, - {4.80503817699514e-14, -3.35545623603729e-14, -1.18546423610485e-14, 4.19419209985980e-15, -1.73525614436880e-14}, - {-1.20464898830163e-15, -8.80752065000456e-16, -1.22214298993313e-15, 1.69928513019657e-15, 1.93593051311405e-16}, - {1.68528879784841e-05, 3.57144412031081e-05, -1.65999910125077e-05, 5.40370336805755e-05, 0.000118138122851376}, - {-3.28151779115881e-05, 1.04231790790798e-05, -2.80761862890640e-06, 2.98996152515593e-06, -2.67641158709985e-06}, - {-2.08664816151978e-06, -1.64463884697475e-06, 6.79099429284834e-08, 7.23955842946495e-07, -6.86378427465657e-07}, - {-2.88205823027255e-09, 2.38319699493291e-09, 1.14169347509045e-07, 8.12981074994402e-08, -1.56957943666988e-07}, - {-7.09711403570189e-09, 6.29470515502988e-09, 3.50833306577579e-09, 8.31289199649054e-09, -2.14221463168338e-09}, - {-8.11910123910038e-10, 3.34047829618955e-10, 3.70619377446490e-10, 3.30426088213373e-10, 4.86297305597865e-11}, - {1.98628160424161e-11, -4.98557831380098e-12, -5.90523187802174e-12, -1.27027116925122e-12, 1.49982368570355e-11}, - {2.62289263262748e-12, 3.91242360693861e-12, 6.56035499387192e-12, -1.17412941089401e-12, -9.40878197853394e-13}, - {-3.37805010124487e-13, 5.39454874299593e-13, -2.41569839991525e-13, -2.41572016820792e-13, -3.01983673057198e-13}, - {-1.85034053857964e-13, 4.31132161871815e-14, 4.13497222026824e-15, -4.60075514595980e-14, -1.92454846400146e-14}, - {2.96113888929854e-15, -1.11688534391626e-14, 3.76275373238932e-15, -3.72593295948136e-15, 1.98205490249604e-16}, - {1.40074667864629e-15, -5.15564234798333e-16, 3.56287382196512e-16, 5.07242777691587e-16, -2.30405782826134e-17}, - {2.96822530176851e-16, -4.77029898301223e-17, 1.12782285532775e-16, 1.58443229778573e-18, 8.22141904662969e-17} - }; - - /** Legendre functions bnm for coefficient bh.*/ - private static final double[][] BH_B = { - {0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0}, - {-2.29210587053658e-06, -2.33805004374529e-06, -7.49312880102168e-07, -5.12022747852006e-07, 5.88926055066172e-07}, - {0, 0, 0, 0, 0}, - {-4.63382754843690e-06, -2.23853015662938e-06, 8.14830531656518e-07, 1.15453269407116e-06, -4.53555450927571e-07}, - {-6.92432096320778e-07, -2.98734455136141e-07, 1.48085153955641e-08, 1.37881746148773e-07, -6.92492118460215e-09}, - {0, 0, 0, 0, 0}, - {-1.91507979850310e-06, -1.83614825459598e-06, -7.46807436870647e-07, -1.28329122348007e-06, 5.04937180063059e-07}, - {-8.07527103916713e-07, 2.83997840574570e-08, -6.01890498063025e-08, -2.48339507554546e-08, 2.46284627824308e-08}, - {-2.82995069303093e-07, 1.38818274596408e-09, 3.22731214161408e-09, 2.87731153972404e-10, 1.53895537278496e-08}, - {0, 0, 0, 0, 0}, - {-6.68210270956800e-07, -2.19104833297845e-06, 1.30116691657253e-07, 4.78445730433450e-07, -4.40344300914051e-07}, - {-2.36946755740436e-07, -1.32730991878204e-07, 1.83669593693860e-08, 7.90218931983569e-08, -4.70161979232584e-08}, - {1.07746083292179e-07, -4.17088637760330e-09, -1.83296035841109e-09, -5.80243971371211e-09, -2.11682361167439e-09}, - {-5.44712355496109e-08, 1.89717032256923e-09, 2.27327316287804e-10, 7.78400728280038e-10, 8.82380487618991e-12}, - {0, 0, 0, 0, 0}, - {-5.61707049615673e-08, -1.09066447089585e-06, -2.25742250174119e-07, -8.64367795924377e-07, 1.06411275240680e-08}, - {2.41782935157918e-08, -3.65762298303819e-08, -6.93420659586875e-08, -3.97316214341991e-08, -2.08767816486390e-08}, - {6.38293030383436e-08, 1.11377936334470e-08, 6.91424941454782e-09, 1.39887159955004e-09, 5.25428749022906e-09}, - {1.09291268489958e-08, 1.23935926756516e-10, 3.92917259954515e-10, -1.79144682483562e-10, -9.11802874917597e-10}, - {-4.40957607823325e-09, 1.45751390560667e-10, 1.24641258165301e-10, -6.45810339804674e-11, -8.92894658893326e-12}, - {0, 0, 0, 0, 0}, - {1.54754294162102e-08, -1.60154742388847e-06, -4.08425188394881e-07, 6.18170290113531e-09, -2.58919765162122e-07}, - {1.37130642286873e-08, -6.67813955828458e-08, -7.01410996605609e-09, 3.82732572660461e-08, -2.73381870915135e-08}, - {2.19113155379218e-08, 4.11027496396868e-09, 6.33816020485226e-09, -1.49242411327524e-09, -6.14224941851705e-10}, - {6.26573021218961e-09, 5.17137416480052e-10, -3.49784328298676e-10, 1.13578756343208e-10, 2.80414613398411e-10}, - {1.65048133258794e-11, 1.00047239417239e-10, 1.05124654878499e-10, -3.03826002621926e-11, 4.57155388334682e-11}, - {6.20221691418381e-11, 9.75852610098156e-12, -5.46716005756984e-12, 1.31643349569537e-11, 3.61618775715470e-12}, - {0, 0, 0, 0, 0}, - {-1.03938913012708e-06, -1.78417431315664e-07, 2.86040141364439e-07, 1.83508599345952e-08, -1.34452220464346e-07}, - {-4.36557481393662e-08, 7.49780206868834e-09, -8.62829428674082e-09, 5.50577793039009e-09, -9.46897502333254e-09}, - {3.43193738406672e-10, 1.13545447306468e-08, 1.25242388852214e-09, 6.03221501959620e-10, 1.57172070361180e-09}, - {-4.73307591021391e-10, 1.70855824051391e-10, -2.62470421477037e-11, 2.04525835988874e-10, -1.17859695928164e-10}, - {-3.36185995299839e-10, 3.19243054562183e-11, 1.17589412418126e-10, -1.35478747434514e-12, 5.11192214558542e-11}, - {3.19640547592136e-11, 2.94297823804643e-12, -1.00651526276990e-11, -1.67028733953153e-12, 3.03938833625503e-12}, - {1.68928641118173e-11, -7.90032886682002e-13, -1.40899773539137e-12, 7.76937592393354e-13, 7.32539820298651e-13}, - {0, 0, 0, 0, 0}, - {2.32949756055277e-07, 1.46237594908093e-07, -1.07770884952484e-07, 1.26824870644476e-07, -2.36345735961108e-08}, - {8.89572676497766e-08, 7.24810004121931e-08, 2.67583556180119e-08, 2.48434796111361e-08, -3.55004782858686e-09}, - {-1.00823909773603e-08, 8.84433929029076e-10, -2.55502517594511e-10, -5.48034274059119e-10, -8.50241938494079e-10}, - {1.13259819566467e-09, 5.55186945221216e-10, 7.63679807785295e-11, -1.70067998092043e-11, 1.57081965572493e-10}, - {-2.37748192185353e-10, 2.45463764948000e-11, 3.23208414802860e-11, -2.72624834520723e-12, 8.14449183666500e-12}, - {-1.54977633126025e-11, 4.58754903157884e-12, -1.25864665839074e-12, 2.44139868157872e-12, -1.82827441958193e-12}, - {3.28285563794513e-12, -1.10072329225465e-12, -7.23470501810935e-13, 5.85309745620389e-13, 4.11317589687125e-13}, - {4.57596974384170e-13, 9.84198128213558e-14, 3.34503817702830e-14, 7.08431086558307e-15, 2.79891177268807e-14}, - {0, 0, 0, 0, 0}, - {-3.67820719155580e-07, 6.98497901205902e-07, 1.83397388750300e-07, 2.39730262495372e-07, -2.58441984368194e-07}, - {5.17793954077994e-08, 5.54614175977835e-08, 1.75026214305232e-09, -2.55518450411346e-09, -6.12272723006537e-09}, - {-7.94292648157198e-09, -1.01709107852895e-09, -1.49251241812310e-09, 9.32827213605682e-10, -8.24490722043118e-10}, - {1.36410408475679e-11, 2.16390220454971e-10, 1.24934806872235e-10, -6.82507825145903e-11, -4.01575177719668e-11}, - {-1.41619917600555e-11, -1.54733230409082e-11, 1.36792829351538e-11, 1.11157862104733e-12, 2.08548465892268e-11}, - {-3.56521723755846e-12, 4.47877185884557e-12, -6.34096209274637e-16, -1.13010624512348e-12, -2.82018136861041e-13}, - {2.22758955943441e-12, -4.63876465559380e-13, -5.80688019272507e-13, 2.45878690598655e-13, 1.49997666808106e-13}, - {-6.26833903786958e-14, 2.73416335780807e-14, 1.91842340758425e-14, 1.67405061129010e-14, -2.45268543953704e-17}, - {1.81972870222228e-14, 5.43036245069085e-15, 1.92476637107321e-15, 8.78498602508626e-17, -1.42581647227657e-15}, - {0, 0, 0, 0, 0}, - {9.74322164613392e-07, -5.23101820582724e-07, -2.81997898176227e-07, 4.54762451707384e-08, -3.34645078118827e-08}, - {-6.75813194549663e-09, 3.49744702199583e-08, -5.09170419895883e-09, 5.24359476874755e-09, 4.96664262534662e-09}, - {4.53858847892396e-10, -1.49347392165963e-09, -2.00939511362154e-09, 9.30987163387955e-10, 9.74450200826854e-11}, - {-4.92900885858693e-10, 5.34223033225688e-12, 1.08501839729368e-10, -6.43526142089173e-11, -3.11063319142619e-11}, - {1.38469246386690e-11, -7.91180584906922e-12, 2.26641656746936e-13, 4.55251515177956e-12, 6.05270575117769e-12}, - {4.02247935664225e-12, 1.82776657951829e-12, -1.28348801405445e-13, -2.16257301300350e-13, -5.54363979435025e-14}, - {4.15005914461687e-13, -2.00647573581168e-13, -1.67278251942946e-13, 1.30332398257985e-13, 1.52742363652434e-13}, - {6.36376500056974e-14, 1.65794532815776e-14, -3.80832559052662e-15, -6.40262894005341e-16, 2.42577181848072e-15}, - {-5.55273521249151e-15, 3.69725182221479e-15, 2.02114207545759e-15, -4.50870833392161e-16, 9.62950493696677e-17}, - {1.00935904205024e-17, 6.54751873609395e-17, -1.09138810997186e-16, -8.62396750098759e-17, -3.82788257844306e-17}, - {0, 0, 0, 0, 0}, - {4.21958510903678e-07, -8.30678271007705e-08, -3.47006439555247e-07, -3.36442823712421e-08, 9.90739768222027e-08}, - {2.64389033612742e-08, 2.65825090066479e-09, -1.28895513428522e-08, -7.07182694980098e-10, 7.10907165301180e-09}, - {6.31203524153492e-09, -1.67038260990134e-09, 1.33104703539822e-09, 8.34376495185149e-10, -2.52478613522612e-10}, - {1.18414896299279e-10, -2.57745052288455e-11, 2.88295935685818e-11, -3.27782977418354e-11, -1.05705000036156e-11}, - {-4.20826459055091e-12, -6.97430607432268e-12, -3.90660545970607e-12, -3.90449239948755e-13, -4.60384797517466e-13}, - {-9.47668356558200e-13, 6.53305025354881e-13, 2.63240185434960e-13, 1.40129115015734e-13, 3.85788887132074e-14}, - {2.23947810407291e-13, 7.35262771548253e-15, -3.83348211931292e-14, 4.20376514344176e-14, 4.26445836468461e-14}, - {-3.88008154470596e-16, 2.28561424667750e-15, -8.73599966653373e-16, 2.14321147947665e-15, 6.38631825071920e-16}, - {-8.62165565535721e-15, 1.79742912149810e-15, 1.01541125038661e-15, -7.91027655831866e-17, -4.06505132825230e-16}, - {-2.35355054392189e-16, -6.13997759731013e-17, -2.73490528665965e-17, 2.63895177155121e-17, -4.47531057245187e-18}, - {6.01909706823530e-17, 5.35520010856833e-18, -2.15530106132531e-18, -2.46778496746231e-18, -7.09947296442799e-19}, - {0, 0, 0, 0, 0}, - {-3.75005956318736e-07, -5.39872297906819e-07, -1.19929654883034e-07, 4.52771083775007e-08, 1.82790552943564e-07}, - {7.82606642505646e-09, -1.68890832383153e-08, -8.45995188378997e-09, 1.42958730598502e-09, 3.21075754133531e-09}, - {4.28818421913782e-09, -1.07501469928219e-09, 8.84086350297418e-10, 9.74171228764155e-10, 8.59877149602304e-12}, - {1.28983712172521e-10, -6.96375160373676e-11, -2.13481436408896e-11, 1.33516375568179e-11, -1.65864626508258e-11}, - {-4.48914384622368e-12, 9.68953616831263e-13, -1.61372463422897e-12, -2.09683563440448e-12, -1.90096826314068e-12}, - {-1.12626619779175e-13, 3.34903159106509e-14, -1.21721528343657e-13, 7.46246339290354e-14, 3.68424909859186e-13}, - {5.08294274367790e-14, 2.83036159977090e-14, 1.48074873486387e-14, -9.59633528834945e-15, -1.26231060951100e-14}, - {-4.01464098583541e-16, 1.97047929526674e-15, -5.29967950447497e-16, -3.59120406619931e-16, 1.69690933982683e-16}, - {-1.73919209873841e-15, 7.52792462841274e-16, 3.65589287101147e-16, -7.79247612043812e-17, -8.24599670368999e-17}, - {-4.61555616150128e-17, 4.94529746019753e-19, -1.09858157212270e-17, 3.95550811124928e-18, 3.23972399884100e-18}, - {-2.27040686655766e-17, -3.27855689001215e-18, -3.30649011116861e-19, 9.08748546536849e-19, 8.92197599890994e-19}, - {5.67241944733762e-18, 3.84449400209976e-19, 1.77668058015537e-19, 2.00432838283455e-20, -2.00801461564767e-19} - }; - - /** Legendre functions bnm for coefficient bw.*/ - private static final double[][] BW_B = { - {0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0}, - {-9.56715196386889e-06, -3.68040633020420e-08, 1.27846786489883e-07, 1.32525487755973e-06, 1.53075361125066e-06}, - {0, 0, 0, 0, 0}, - {-7.17682617983607e-06, 2.89994188119445e-06, -2.97763578173405e-07, 8.95742089134942e-07, 3.44416325304006e-07}, - {-8.02661132285210e-07, 3.66738692077244e-07, -3.02880965723280e-07, 3.54144282036103e-07, -1.68873066391463e-07}, - {0, 0, 0, 0, 0}, - {-2.89640569283461e-06, -7.83566373343614e-07, -8.36667214682577e-07, -7.41891843549121e-07, -9.23922655636489e-08}, - {-1.06144662284862e-06, 1.57709930505924e-07, 1.04203025714319e-07, 1.20783300488461e-07, -1.38726055821134e-07}, - {-4.16549018672265e-07, -1.35220897698872e-07, -6.40269964829901e-08, 1.63258283210837e-08, -2.57958025095959e-08}, - {0, 0, 0, 0, 0}, - {3.52324885892419e-06, -2.26705543513814e-07, 1.53835589488292e-06, -3.75263061267433e-07, 3.69384057396017e-07}, - {-2.06569149157664e-07, -9.36260183227175e-08, -3.55985284353048e-08, -9.13671163891094e-08, 6.93156256562600e-09}, - {1.32437594740782e-07, 4.44349887272663e-08, -3.38192451721674e-08, -3.97263855781102e-08, -1.93087822995800e-09}, - {-1.29595244818942e-07, -1.40852985547683e-08, 1.42587592939760e-09, 7.05779876554001e-09, -1.00996269264535e-08}, - {0, 0, 0, 0, 0}, - {4.06960756215938e-06, -1.97898540226986e-06, 7.21905857553588e-08, -1.19908881538755e-06, -5.67561861536903e-08}, - {6.53369660286999e-08, -2.42818687866392e-07, -1.66203004559493e-08, -2.41512414151897e-08, 4.45426333411018e-08}, - {1.44650670663281e-07, 8.50666367433859e-09, -4.61165612004307e-09, 4.88527987491045e-09, 1.06277326713172e-08}, - {1.86770937103513e-08, -6.44197940288930e-10, -7.60456736846174e-09, -9.97186468682689e-10, 8.73229752697716e-10}, - {-1.00206566229113e-08, 1.33934372663121e-09, 1.41691503439220e-09, 8.72352590578753e-10, -8.04561626629829e-10}, - {0, 0, 0, 0, 0}, - {3.07161843116618e-06, 1.82962085656470e-06, 1.87728623016069e-07, 7.10611617623261e-07, 2.26499092250481e-07}, - {4.50766403064905e-08, -1.67752393078256e-07, 2.47844723639070e-08, -3.56484348424869e-09, -1.56634836636584e-08}, - {3.77011651881090e-08, -7.23045828480496e-09, 5.22995988863761e-09, -1.03740320341306e-09, 4.57839777217789e-09}, - {8.09495635883121e-09, -3.01977244420529e-10, -2.30104544933093e-09, 3.63658580939428e-10, 4.39320811714867e-10}, - {9.37087629961269e-11, 1.00780920426635e-09, 1.28140539913350e-10, -6.65795285522138e-12, 4.71732796198631e-11}, - {-8.88504487069155e-11, -1.63253810435461e-10, 7.22669710644299e-11, 5.64715132584527e-11, -1.08949308197617e-12}, - {0, 0, 0, 0, 0}, - {-2.64054293284174e-07, -2.37611606117256e-06, -1.83671059706264e-06, -3.12199354841993e-07, -1.05598289276114e-07}, - {7.41706968747147e-08, -1.64359098062646e-08, -3.09750224040234e-08, -9.68640079410317e-09, -7.90399057863403e-08}, - {-1.00254376564271e-08, 1.12528248631191e-08, -2.67841549174100e-09, -2.69481819323647e-09, 1.56550607475331e-09}, - {-2.18568129350729e-09, 6.26422056977450e-10, 1.95007291427316e-09, 3.14226463591125e-10, -3.62000388344482e-10}, - {-9.30451291747549e-10, 5.62175549482704e-11, 1.01022849902012e-10, 5.18675856498499e-11, 5.37561696283235e-11}, - {5.33151334468794e-11, 1.07571307336725e-10, -1.31714567944652e-11, -4.17524405900018e-11, -2.16737797893502e-12}, - {4.69916869001309e-11, -4.34516364859583e-12, -6.61054225868897e-12, -5.75845818545368e-12, -2.32180293529175e-12}, - {0, 0, 0, 0, 0}, - {-3.50305843086926e-06, 1.76085131953403e-06, 8.16661224478572e-07, 4.09111042640801e-07, -9.85414469804995e-08}, - {1.44670876127274e-07, -1.41331228923029e-08, -3.06530152369269e-08, -1.46732098927996e-08, -2.30660839364244e-08}, - {-2.00043052422933e-08, 1.72145861031776e-09, 2.13714615094209e-09, 1.02982676689194e-09, -1.64945224692217e-10}, - {1.23552540016991e-09, 1.42028470911613e-09, 8.79622616627508e-10, -7.44465600265154e-10, -7.17124672589442e-11}, - {-6.67749524914644e-10, -5.77722874934050e-11, 3.40077806879472e-11, 4.26176076541840e-11, 8.23189659748212e-11}, - {-4.62771648935992e-11, -7.24005305716782e-13, 1.18233730497485e-12, 5.18156973532267e-12, -1.53329687155297e-12}, - {4.75581699468619e-12, -3.79782291469732e-12, 1.33077109836853e-12, -1.02426020107120e-12, 3.10385019249130e-13}, - {1.66486090578792e-12, 1.08573672403649e-12, 1.26268044166279e-13, -1.23509297742757e-13, -1.81842007284038e-13}, - {0, 0, 0, 0, 0}, - {9.93870680202303e-08, -1.85264736035628e-06, -5.58942734710854e-07, -5.54183448316270e-07, -3.95581289689398e-08}, - {7.88329069002365e-08, 2.04810091451078e-08, 3.74588851000076e-09, 3.42429296613803e-08, -2.00840228416712e-08}, - {-5.93700447329696e-10, -6.57499436973459e-10, -6.90560448220751e-09, 3.56586371051089e-09, 7.33310245621566e-11}, - {-6.38101662363634e-11, 4.23668020216529e-10, -2.43764895979202e-10, -9.31466610703172e-11, -3.17491457845975e-10}, - {1.50943725382470e-11, -6.11641188685078e-11, -4.37018785685645e-11, -2.32871158949602e-11, 4.19757251950526e-11}, - {-1.18165328825853e-11, -9.91299557532438e-13, 6.40908678055865e-14, 2.41049422936434e-12, -8.20746054454953e-14}, - {6.01892101914838e-12, -8.78487122873450e-13, -1.58887481332294e-12, -3.13556902469604e-13, 5.14523727801645e-14}, - {-1.50791729401891e-13, -1.45234807159695e-13, 1.65302377570887e-13, -5.77094211651483e-15, 9.22218953528393e-14}, - {-1.85618902787381e-14, 5.64333811864051e-14, -9.94311377945570e-15, -2.40992156199999e-15, -2.19196760659665e-14}, - {0, 0, 0, 0, 0}, - {-8.16252352075899e-08, 1.61725487723444e-06, 9.55522506715921e-07, 4.02436267433511e-07, -2.80682052597712e-07}, - {7.68684790328630e-09, -5.00940723761353e-09, -2.43640127974386e-08, -2.59119930503129e-08, 3.35015169182094e-08}, - {7.97903115186673e-09, 3.73803883416618e-09, 3.27888334636662e-09, 1.37481300578804e-09, -1.10677168734482e-10}, - {-1.67853012769912e-09, -1.61405252173139e-10, -1.98841576520056e-10, -1.46591506832192e-11, 9.35710487804660e-11}, - {4.08807084343221e-11, -3.74514169689568e-11, -3.03638493323910e-11, -5.02332555734577e-12, -8.03417498408344e-12}, - {6.48922619024579e-12, 1.96166891023817e-12, -1.96968755122868e-12, -5.20970156382361e-12, -1.62656885103402e-12}, - {1.28603518902875e-12, -4.88146958435109e-13, -3.37034886991840e-13, 1.37393696103000e-14, 4.41398325716943e-14}, - {1.48670014793021e-13, 4.41636026364555e-14, 2.06210477976005e-14, -3.43717583585390e-14, -1.21693704024213e-14}, - {-1.67624180330244e-14, 6.59317111144238e-15, 2.57238525440646e-15, -3.21568425020512e-17, 5.29659568026553e-15}, - {7.85453466393227e-16, 6.91252183915939e-16, -1.20540764178454e-15, -3.85803892583301e-16, 3.46606994632006e-16}, - {0, 0, 0, 0, 0}, - {2.86710087625579e-06, -1.68179842305865e-06, -8.48306772016870e-07, -7.08798062479598e-07, -1.27469453733635e-07}, - {2.11824305734993e-09, 2.02274279084379e-08, 1.61862253091554e-08, 3.25597167111807e-08, 3.40868964045822e-09}, - {1.21757111431438e-08, 1.68405530472906e-09, 1.55379338018638e-09, -3.81467795805531e-10, 2.53316405545058e-09}, - {-9.98413758659768e-11, 5.38382145421318e-10, 3.92629628330704e-10, -1.43067134097778e-10, 3.74959329667113e-12}, - {-1.57270407028909e-11, -9.02797202317592e-12, 8.45997059887690e-12, 4.71474382524218e-12, 5.41880986596427e-12}, - {-1.20658618702054e-12, 7.12940685593433e-13, 1.02148613026937e-12, 1.63063852348169e-13, 1.74048793197708e-13}, - {3.80559390991789e-13, 1.19678271353485e-13, 9.72859455604188e-14, 5.42642400031729e-14, 8.18796710714586e-14}, - {-4.69629218656902e-14, 5.59889038686206e-15, 2.05363292795059e-15, 5.38599403288686e-15, -2.68929559474202e-15}, - {-1.88759348081742e-14, 5.20975954705924e-15, -4.43585653096395e-16, 5.57436617793556e-16, -3.95922805817677e-16}, - {-9.80871456373282e-16, 2.50857658461759e-17, -1.24253000050963e-16, 6.00857065211394e-17, 3.53799635311500e-18}, - {2.49370713054872e-16, -1.49119714269816e-17, -3.12276052640583e-17, -2.42001662334001e-17, -1.69766504318143e-17}, - {0, 0, 0, 0, 0}, - {-1.69222102455713e-06, 1.64277906173064e-06, 5.28855114364096e-07, 4.28159853268650e-07, -1.57362445882665e-07}, - {1.67656782413678e-08, -3.77746114074055e-08, -2.21564555842165e-08, -3.37071806992217e-08, 1.47454008739800e-08}, - {1.06080499491408e-08, 3.21990403709678e-09, 3.87301757435359e-09, 2.92241827834347e-10, -1.86619473655742e-11}, - {1.62399669665839e-10, 3.51322865845172e-10, 2.67086377702958e-11, -1.31596563625491e-10, 3.14164569507034e-11}, - {-2.02180016657259e-11, 2.03305178342732e-11, 6.34969032565839e-12, 5.99522296668787e-12, -4.46275273451008e-12}, - {-9.88409290158885e-13, -1.47692750858224e-13, 3.14655550730530e-13, -2.41857189187879e-13, 4.47727504501486e-13}, - {1.71430777754854e-13, 1.73950835042486e-13, 5.92323956541558e-14, 8.06625710171825e-15, 2.33252485755634e-14}, - {-1.74184545690134e-15, -8.18003353124179e-16, -6.62369006497819e-16, 4.16303374396147e-15, 7.06513748014024e-15}, - {-6.02936238677014e-15, 1.89241084885229e-15, 1.99097881944270e-17, -6.99974290696640e-16, -2.69504942597709e-17}, - {-4.65632962602379e-16, 3.70281995445114e-18, -9.04232973763345e-17, 2.20847370761932e-17, 7.62909453726566e-17}, - {-6.25921477907943e-17, -2.10532795609842e-17, -1.03808073867183e-17, 1.15091380049019e-18, 4.66794445408388e-19}, - {9.39427013576903e-18, 9.17044662931859e-19, 2.04132745117549e-18, -1.72364063154625e-19, -1.18098896532163e-18} - }; - - /** Legendre functions bnm for coefficient ch.*/ - private static final double[][] CH_B = { - {0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0}, - {3.44092035729033e-05, -1.21876825440561e-05, -1.87490665238967e-05, -2.60980336247863e-05, 4.31639313264615e-06}, - {0, 0, 0, 0, 0}, - {-2.60125613000133e-05, 1.70570295762269e-05, 3.08331896996832e-05, 1.66256596588688e-05, -1.07841055501996e-05}, - {8.74011641844073e-06, -2.25874169896607e-06, 6.50985196673747e-07, 1.30424765493752e-06, -1.85081244549542e-07}, - {0, 0, 0, 0, 0}, - {3.77496505484964e-05, -1.08198973553337e-05, -1.67717574544937e-05, -3.22476096673598e-05, 1.12281888201134e-05}, - {-7.68623378647958e-07, -4.01400837153063e-06, -2.16390246700835e-06, -1.76912959937924e-06, -1.12740084951955e-06}, - {-2.37092815818895e-06, -9.52317223759653e-07, -2.22722065579131e-07, -6.25157619772530e-08, 1.86582003894639e-08}, - {0, 0, 0, 0, 0}, - {-6.10254317785872e-05, -2.51815503068494e-05, 2.01046207874667e-05, 7.21107723367308e-06, -1.30692058660457e-05}, - {-9.60655417241537e-06, -7.31381721742373e-06, -2.52767927589636e-06, 9.09039973214621e-07, -6.76454911344246e-07}, - {-2.25743206384908e-08, 2.33058746737575e-07, 2.24746779293445e-07, 6.78551351968876e-08, 1.25076011387284e-07}, - {-2.25744112770133e-07, -1.44429560891636e-07, -2.96810417448652e-08, -5.93858519742856e-08, -2.43210229455420e-08}, - {0, 0, 0, 0, 0}, - {7.45721015256308e-06, -3.81396821676410e-05, -1.41086198468687e-05, -2.28514517574713e-05, 7.28638705683277e-06}, - {-5.77517778169692e-06, -3.93061211403839e-06, -2.17369763310752e-06, -1.48060935583664e-07, -2.74200485662814e-07}, - {4.52962035878238e-07, 9.80990375495214e-07, 4.67492045269286e-07, -8.31032252212116e-09, 1.69426023427740e-07}, - {7.20536791795515e-10, 2.75612253452141e-09, 2.47772119382536e-09, 4.30621825021233e-09, -2.86498479499428e-08}, - {-2.46253956492716e-08, -3.10300833499669e-09, 8.06559148724445e-09, 2.98197408430123e-10, 6.32503656532846e-09}, - {0, 0, 0, 0, 0}, - {-6.01147094179306e-05, -3.16631758509869e-05, 4.10038115100010e-06, 3.55215057231403e-07, -2.23606515237408e-06}, - {-2.85937516921923e-06, -3.67775706610630e-06, -5.06445540401637e-07, 8.21776759711184e-07, -5.98690271725558e-07}, - {7.77122595418965e-07, 3.60896376754085e-07, 3.88610487893381e-07, -4.39533892679537e-08, -6.26882227849174e-08}, - {1.05759993661891e-07, 2.58009912408833e-08, -1.51356049060972e-08, -1.13335813107412e-09, 5.37470857850370e-10}, - {7.99831506181984e-09, 1.67423735327465e-09, 2.94736760548677e-09, -1.56727133704788e-09, 8.46186800849124e-10}, - {3.07727104043851e-09, 3.93584215798484e-10, 3.86721562770643e-11, 1.72181091277391e-10, -2.16915737920145e-10}, - {0, 0, 0, 0, 0}, - {-1.16335389078126e-05, -1.39864676661484e-05, 2.52546278407717e-06, -8.79152625440188e-06, -8.97665132187974e-06}, - {-3.95874550504316e-06, -1.17976262528730e-07, 7.03189926369300e-07, 3.38907065351535e-07, -3.67714052493558e-07}, - {2.29082449370440e-07, 5.72961531093329e-07, 4.21969662578894e-08, 1.24112958141431e-08, 9.56404486571888e-08}, - {1.44631865298671e-09, 6.19368473895584e-09, 1.67110424041236e-09, 2.57979463602951e-09, -6.90806907510366e-09}, - {1.77235802019153e-09, -8.14388846228970e-10, 4.50421956523579e-09, 5.67452314909707e-10, 2.47610443675560e-09}, - {4.85932343880617e-10, 2.24864117422804e-10, -2.22534534468511e-10, -7.96395824973477e-11, 3.12587399902493e-12}, - {-3.20173937255409e-11, -1.29872402028088e-11, -4.24092901203818e-11, 2.66570185704416e-11, -5.25164954403909e-12}, - {0, 0, 0, 0, 0}, - {-1.36010179191872e-05, 1.77873053642413e-05, 4.80988546657119e-06, 3.46859608161212e-06, -1.73247520896541e-06}, - {2.00020483116258e-06, 2.43393064079673e-06, 1.21478843695862e-06, 1.95582820041644e-07, -3.11847995109088e-07}, - {-8.13287218979310e-09, 1.05206830238665e-08, 6.54040136224164e-09, -1.96402660575990e-08, -1.40379796070732e-08}, - {4.01291020310740e-08, 2.92634301047947e-08, 6.04179709273169e-09, 8.61849065020545e-10, 5.98065429697245e-09}, - {-1.06149335032911e-09, -4.39748495862323e-10, 8.83040310269353e-10, 3.49392227277679e-10, 8.57722299002622e-10}, - {-1.25049888909390e-11, 2.05203288281631e-10, 1.37817670505319e-11, 6.82057794430145e-11, -9.41515631694254e-11}, - {7.47196022644130e-12, -2.51369898528782e-11, -2.12196687809200e-11, 1.55282119505201e-11, 9.99224438231805e-12}, - {-7.90534019004874e-13, 3.55824506982589e-12, 8.00835777767281e-13, 8.73460019069655e-13, 1.34176126600106e-12}, - {0, 0, 0, 0, 0}, - {3.12855262465316e-05, 1.31629386003608e-05, 2.65598119437581e-06, 8.68923340949135e-06, -7.51164082949678e-06}, - {1.56870792650533e-06, 1.89227301685370e-06, 4.15620385341985e-07, -2.74253787880603e-07, -4.28826210119200e-07}, - {-9.99176994565587e-08, -1.10785129426286e-07, -1.10318125091182e-07, 6.22726507350764e-09, -3.39214566386250e-08}, - {1.24872975018433e-08, 1.10663206077249e-08, 5.40658975901469e-09, -2.79119137105115e-09, -2.47500096192502e-09}, - {1.11518917154060e-10, -4.21965763244849e-10, 3.26786005211229e-10, 1.93488254914545e-10, 7.00774679999972e-10}, - {1.50889220040757e-10, 1.03130002661366e-10, -3.09481760816903e-11, -4.47656630703759e-11, -7.36245021803800e-12}, - {-1.91144562110285e-12, -1.11355583995978e-11, -1.76207323352556e-11, 8.15289793192265e-12, 3.45078925412654e-12}, - {-2.73248710476019e-12, -1.65089342283056e-13, -2.20125355220819e-13, 5.32589191504356e-13, 5.70008982140874e-13}, - {8.06636928368811e-13, 1.30893069976672e-13, 9.72079137767479e-14, 3.87410156264322e-14, -5.56410013263563e-14}, - {0, 0, 0, 0, 0}, - {2.02454485403216e-05, -9.77720471118669e-06, -4.35467548126223e-06, 2.19599868869063e-06, -3.26670819043690e-06}, - {-3.21839256310540e-08, 8.38760368015005e-07, -5.08058835724060e-07, 4.16177282491396e-08, 1.53842592762120e-07}, - {-1.57377633165313e-07, -7.86803586842404e-08, -7.40444711426898e-08, 3.15259864117954e-08, 5.60536231567172e-09}, - {-3.26080428920229e-10, -3.14576780695439e-09, 8.46796096612981e-10, -2.59329379174262e-09, -8.01054756588382e-10}, - {-4.58725236153576e-11, -6.87847958546571e-11, 8.18226480126754e-12, 1.81082075625897e-10, 1.74510532938256e-10}, - {7.60233505328792e-11, 4.76463939581321e-11, -2.47198455442033e-11, -8.83439688929965e-12, 5.93967446277316e-13}, - {-8.92919292558887e-12, -4.38524572312029e-12, -4.02709146060896e-12, 4.84344426425295e-12, 5.12869042781520e-12}, - {1.91518361809952e-12, 3.06846255371817e-13, -2.44830265306345e-13, 7.86297493099244e-14, 2.72347805801980e-13}, - {9.09936624159538e-14, 7.20650818861447e-15, 2.45383991578283e-14, -4.79580974186462e-15, 3.64604724046944e-14}, - {-4.63611142770709e-14, 1.73908246420636e-15, -4.41651410674801e-15, -6.61409045306922e-16, -1.60016049099639e-15}, - {0, 0, 0, 0, 0}, - {6.17105245892845e-06, -1.04342983738457e-05, -1.72711741097994e-05, -8.16815967888426e-07, 3.42789959967593e-06}, - {-2.44014060833825e-07, 2.06991837444652e-07, -3.85805819475679e-07, 1.67162359832166e-08, 4.15139610402483e-07}, - {8.18199006804020e-08, -3.20013409049159e-08, 5.94000906771151e-08, 2.24122167188946e-08, -1.33796186160409e-08}, - {7.66269294674338e-11, -6.07862178874828e-10, 4.95795757186248e-10, -3.07589245481422e-10, 3.44456287710689e-10}, - {-1.84076250254929e-10, -1.30985312312781e-10, -1.52547325533276e-10, -2.51000125929512e-11, -1.93924012590455e-11}, - {-2.93307452197665e-11, 2.88627386757582e-11, 5.58812021182217e-12, -1.68692874069187e-13, 1.80464313900575e-12}, - {-9.59053874473003e-13, 6.04803122874761e-13, -9.80015608958536e-13, 1.70530372034214e-12, 1.70458664160775e-12}, - {2.80169588226043e-13, 9.09573148053551e-14, 2.16449186617004e-14, 1.15550091496353e-13, 4.97772796761321e-14}, - {-3.04524400761371e-14, 3.42845631349694e-14, 2.44230630602064e-14, 5.76017546103056e-16, -9.74409465961093e-15}, - {5.98765340844291e-15, -2.63942474859535e-15, -1.80204805804437e-15, -1.84981819321183e-16, -5.85073392163660e-16}, - {-2.37069441910133e-15, 2.87429226086856e-16, -1.67055963193389e-16, 2.72110684914090e-18, 8.46646962667892e-17}, - {0, 0, 0, 0, 0}, - {-2.71386164105722e-05, -1.41834938338454e-05, -2.00777928859929e-07, 5.94329804681196e-07, 8.61856994375586e-06}, - {-3.93656495458664e-08, -6.36432821807576e-07, -2.47887475106438e-07, -2.64906446204966e-08, 1.10689794197004e-07}, - {5.25319489188562e-08, 9.00866357158695e-09, 5.00693379572512e-08, 2.47269011056404e-08, -7.27648556194598e-09}, - {1.87207107149043e-09, -1.46428282396138e-09, -2.71812237167257e-10, 8.44902265891466e-10, -5.62683870906027e-10}, - {-1.08295119666184e-10, 4.75553388543793e-11, -5.49429386495686e-11, -6.60907871731611e-11, -5.97347322824822e-11}, - {-4.95118306815571e-12, 5.31083735234970e-13, -1.93679746327378e-12, -1.61770521840510e-12, 1.23276727202510e-11}, - {6.68582682909900e-13, 7.38288575160449e-13, 5.47630483499201e-13, -1.00770258118914e-13, -1.65564928475981e-13}, - {5.80963409268471e-14, 6.93474288078737e-14, 6.60728092794315e-15, -5.21029056725202e-15, -1.11283532854883e-16}, - {-4.10567742688903e-15, 1.62252646805882e-14, 1.00774699865989e-14, -2.44793214897877e-16, -1.59283906414563e-15}, - {1.84669506619904e-17, 8.28473337813919e-17, -1.53400662078899e-16, -5.01060672199689e-17, -2.20727935766132e-16}, - {2.65355116203636e-16, -3.70233146147684e-17, 3.52689394451586e-18, -8.62215942516328e-18, 9.26909361974526e-18}, - {9.94266950643135e-17, 4.17028699663441e-18, -7.65153491125819e-21, -5.62131270981041e-18, -3.03732817297438e-18} - }; - - /** Legendre functions bnm for coefficient cw.*/ - private static final double[][] CW_B = { - {0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0}, - {-0.000209104872912563, -1.41530274973540e-05, 3.00318745764815e-05, -1.82864291318284e-05, -7.62965409959238e-06}, - {0, 0, 0, 0, 0}, - {-0.000186336519900275, 0.000191256553935638, 7.28356195304996e-05, 3.59637869639906e-05, -2.53927226167388e-05}, - {0.000108195343799485, -6.97050977217619e-05, -6.68037133871099e-05, 2.30387653190503e-05, -1.22735483925784e-05}, - {0, 0, 0, 0, 0}, - {0.000119941091277039, -7.70547844186875e-05, -8.15376297964528e-05, 1.06005789545203e-05, 2.31177232268720e-05}, - {-1.77494760217164e-05, -1.37061385686605e-05, -1.74805936475816e-05, -6.91745900867532e-07, -7.10231790947787e-06}, - {-1.47564103733219e-05, 2.08890785485260e-06, 3.19876879447867e-06, 9.43984664503715e-07, -4.90480527577521e-06}, - {0, 0, 0, 0, 0}, - {4.93300138389457e-05, -6.77641298460617e-05, -3.25043347246397e-05, 8.33226714911921e-06, 8.11499972792905e-06}, - {-2.80449863471272e-05, -1.04367606414606e-05, 1.64473584641163e-07, -3.57420965807816e-06, 2.95887156564038e-06}, - {1.88835280111533e-06, 5.69125761193702e-07, -2.22757382799409e-06, -1.96699131032252e-07, -2.91861219283659e-07}, - {-4.69918971436680e-06, -7.00778948636735e-07, 2.97544157334673e-09, 3.86100512544410e-07, 2.30939653701027e-07}, - {0, 0, 0, 0, 0}, - {1.77050610394149e-05, -3.18353071311574e-05, 3.04232260950316e-05, -6.26821316488169e-05, -1.75094810002378e-06}, - {9.25605901565775e-06, -8.25179123302247e-06, 6.74032752408358e-06, 3.22192289084524e-06, 6.09414500075259e-06}, - {4.28233825242200e-06, 2.10470570087927e-07, -4.75050074985668e-07, -4.89382663470592e-07, 8.75232347469207e-07}, - {8.50393520366934e-07, 1.58764911467186e-07, -2.16267638321210e-07, -7.43341300487416e-10, 1.75131729813230e-07}, - {-2.87064111623119e-07, 4.50393893102830e-08, 6.63315044416690e-08, 7.61199387418853e-08, -6.05694385243652e-09}, - {0, 0, 0, 0, 0}, - {-1.95692079507947e-05, 5.15486098887851e-05, 3.00852761598173e-05, 1.21485028343416e-05, -6.72450521493428e-06}, - {5.34496867088158e-06, 3.90973451680699e-06, 3.70148924718425e-06, 5.73731499938212e-08, 5.52258220288780e-07}, - {3.39950838185315e-07, -5.63443976772634e-07, 4.52082211980595e-07, -2.57094645806243e-07, -6.84885762924729e-08}, - {2.15793276880684e-07, 2.05911354090873e-07, 1.33747872341142e-08, -2.07997626478952e-08, -3.69812938736019e-08}, - {2.11952749403224e-09, 4.04317822544732e-08, 2.40972024883650e-09, 8.56289126938059e-09, 2.31035283490200e-08}, - {-2.08402298813248e-09, -8.50243600879112e-09, 2.60895410117768e-09, -6.69156841738591e-10, -5.16280278087006e-09}, - {0, 0, 0, 0, 0}, - {0.000124901291436683, -5.70770326719086e-05, -8.44887248105015e-05, -3.11442665354698e-05, -1.12982893252046e-05}, - {-8.38934444233944e-06, 1.56860091415414e-06, -1.77704563531825e-06, -5.70219068898717e-08, -4.30377735031244e-06}, - {3.72965318017681e-07, 6.98175439446187e-07, 1.75760544807919e-08, 1.59731284857151e-07, 3.62363848767891e-07}, - {-2.32148850787091e-07, -4.21888751852973e-08, 8.35926113952108e-08, -2.24572480575674e-08, -6.92114100904503e-08}, - {-2.92635642210745e-09, 3.38086229163415e-09, 4.72186694662901e-09, -8.32354437305758e-11, 4.19673890995627e-09}, - {-1.26452887692900e-09, 1.91309690886864e-09, 1.54755631983655e-09, -1.09865169400249e-09, 1.83645326319994e-10}, - {9.92539437011905e-10, -2.96318203488300e-10, 1.17466020823486e-10, -5.00185957995526e-10, -8.54777591408537e-11}, - {0, 0, 0, 0, 0}, - {-0.000182885335404854, 7.27424724520089e-05, 3.05286278023427e-05, 2.55324463432562e-05, -6.39859510763234e-06}, - {-5.21449265232557e-06, -6.70572386081398e-06, -3.95473351292738e-06, -6.41023334372861e-07, -3.11616331059009e-06}, - {2.37090789071727e-07, 3.58427517014705e-07, 2.55709192777007e-07, 8.44593804408541e-08, 9.27243162355359e-09}, - {7.24370898432057e-08, -7.43945120337710e-09, 8.61751911975683e-10, -2.34651212610623e-08, 2.94052921681456e-09}, - {-1.22127317934425e-08, -3.89758984276768e-09, 4.12890383904924e-11, 2.06528068002723e-09, 1.73488696972270e-09}, - {-5.44137406907620e-10, -4.81034553189921e-10, -2.56101759039694e-11, 3.21880564410154e-10, -2.70195343165250e-11}, - {1.08394225300546e-10, -7.99525492688661e-11, 1.73850287030654e-10, -8.06390014426271e-11, -7.63143364291160e-13}, - {-3.41446959267441e-11, 2.72675729042792e-11, 5.69674704865345e-12, -3.38402998344892e-12, -2.96732381931007e-12}, - {0, 0, 0, 0, 0}, - {2.91161315987250e-05, -7.24641166590735e-05, -8.58323519857884e-06, -1.14037444255820e-05, 1.32244819451517e-05}, - {1.24266748259826e-06, -4.13127038469802e-06, -8.47496394492885e-07, 5.48722958754267e-07, -1.98288551821205e-06}, - {-1.70671245196917e-08, 1.36891127083540e-08, -2.80901972249870e-07, -5.45369793946222e-09, -9.58796303763498e-08}, - {1.14115335901746e-08, 2.79308166429178e-08, -1.71144803132413e-08, 4.86116243565380e-09, -8.13061459952280e-09}, - {-1.19144311035824e-09, -1.28197815211763e-09, -1.22313592972373e-09, 6.23116336753674e-10, 2.11527825898689e-09}, - {4.94618645030426e-10, -1.01554483531252e-10, -3.58808808952276e-10, 1.23499783028794e-10, -1.21017599361833e-10}, - {1.33959569836451e-10, -1.87140898812283e-11, -3.04265350158941e-11, -1.42907553051431e-11, -1.09873858099638e-11}, - {1.30277419203512e-11, -4.95312627777245e-12, 2.23070215544358e-12, 1.66450226016423e-12, 6.26222944728474e-12}, - {-4.40721204874728e-12, 2.99575133064885e-12, -1.54917262009097e-12, 8.90015664527060e-14, -1.59135267012937e-12}, - {0, 0, 0, 0, 0}, - {-4.17667211323160e-05, 1.39005215116294e-05, 1.46521361817829e-05, 3.23485458024416e-05, -8.57936261085263e-06}, - {9.48491026524450e-07, 1.67749735481991e-06, 6.80159475477603e-07, -1.34558044496631e-06, 1.62108231492249e-06}, - {-2.67545753355631e-07, -3.31848493018159e-08, 1.05837219557465e-07, 1.55587655479400e-07, -2.84996014386667e-08}, - {-5.15113778734878e-08, 8.83630725241303e-09, 3.36579455982772e-09, -6.22350102096402e-09, 5.03959133095369e-09}, - {2.04635880823035e-11, -1.07923589059151e-09, -6.96482137669712e-10, -4.70238500452793e-10, -6.60277903598297e-10}, - {-2.41897168749189e-11, 1.33547763615216e-10, -5.13534673658908e-11, -8.32767177662817e-11, 5.72614717082428e-11}, - {7.55170562359940e-12, -1.57123461699055e-11, -1.48874069619124e-11, -7.10529462981252e-13, -7.99006335025107e-12}, - {2.41883156738960e-12, 2.97346980183361e-12, 1.28719977731450e-12, -2.49240876894143e-12, 6.71155595793198e-13}, - {4.16995565336914e-13, -1.71584521275288e-13, -7.23064067359978e-14, 2.45405880599037e-13, 4.43532934905830e-13}, - {3.56937508828997e-14, 2.43012511260300e-14, -7.96090778289326e-14, -1.59548529636358e-14, 8.99103763000507e-15}, - {0, 0, 0, 0, 0}, - {0.000117579258399489, -4.52648448635772e-05, -2.69130037097862e-05, -3.82266335794366e-05, -4.36549257701084e-06}, - {-1.43270371215502e-06, 1.21565440183855e-06, 8.53701136074284e-07, 1.52709810023665e-06, 1.22382663462904e-06}, - {3.06089147519664e-07, 9.79084123751975e-08, 7.96524661441178e-08, 4.54770947973458e-08, 2.22842369458882e-07}, - {-9.94254707745127e-09, 1.43251376378012e-08, 1.93911753685160e-08, -6.52214645690987e-09, -1.97114016452408e-09}, - {-9.20751919828404e-10, -9.44312829629076e-10, 7.24196738163952e-11, -6.71801072324561e-11, 2.33146774065873e-10}, - {-1.43544298956410e-11, 1.78464235318769e-10, 7.69950023012326e-11, -4.22390057304453e-12, 3.05176324574816e-11}, - {-7.88053753973990e-12, -3.20207793051003e-12, 1.01527407317625e-12, 6.02788185858449e-12, 1.14919530900453e-11}, - {-1.21558899266069e-12, 5.31300597882986e-13, 3.44023865079264e-13, -6.22598216726224e-14, -5.47031650765402e-14}, - {-4.15627948750943e-13, 2.77620907292721e-13, -8.99784134364011e-14, 1.07254247320864e-13, 6.85990080564196e-14}, - {-3.91837863922901e-14, 9.74714976816180e-15, 6.79982450963903e-15, -2.41420876658572e-15, -2.20889384455344e-15}, - {9.25912068402776e-15, -4.02621719248224e-15, -2.43952036351187e-15, -1.97006876049866e-15, 1.03065621527869e-16}, - {0, 0, 0, 0, 0}, - {-0.000103762036940193, 4.38145356960292e-05, 2.43406920349913e-05, 7.89103527673736e-06, -1.66841465339160e-05}, - {-1.18428449371744e-06, -1.30188721737259e-06, -1.88013557116650e-06, -1.01342046295303e-06, 9.21813037802502e-07}, - {1.51836068712460e-07, 1.11362553803933e-07, 1.55375052233052e-07, 1.94450910788747e-09, -1.73093755828342e-08}, - {-3.77758211813121e-09, 1.23323969583610e-08, 1.72510045250302e-09, -1.88609789458597e-09, 1.28937597985937e-09}, - {-1.07947760393523e-09, 5.26051570105365e-10, -3.67657536332496e-11, 3.16110123523840e-10, -3.24273198242170e-10}, - {-2.00385649209820e-12, 2.54703869682390e-11, 4.08563622440851e-12, -4.83350348928636e-11, -3.98153443845079e-13}, - {2.73094467727215e-12, 5.08900664114903e-12, -7.66669089075134e-13, 2.50015592643012e-12, 4.29763262853853e-12}, - {6.53946487537890e-13, -2.24958413781008e-13, 6.74638861781238e-15, 3.28537647613903e-14, 2.54199700290116e-13}, - {-1.09122051193505e-13, 8.36362392931501e-14, -3.90750153912300e-14, -5.44915910741950e-14, 2.43816947219217e-14}, - {-1.41882561550134e-14, 1.00455397812713e-14, 2.63347255121581e-15, 1.53043256823601e-15, 2.49081021428095e-15}, - {-1.17256193152654e-15, 1.05648985031971e-16, 1.31778372453016e-16, 1.44815198666577e-16, -3.72532768618480e-16}, - {2.66203457773766e-16, -7.67224608659658e-17, 3.51487351031864e-18, 4.10287131339291e-17, -6.72171711728514e-17} - }; - - /** Build a new instance. */ - LegendreFunctions() { - - } - - /** Get the value of the anm coefficient for bh. - * @param n index - * @param m index - * @return the anm coefficient for bh - */ - public double getAnmBh(final int n, final int m) { - return BH_A[n][m]; - } - - /** Get the value of the anm coefficient for bw. - * @param n index - * @param m index - * @return the anm coefficient for bw - */ - public double getAnmBw(final int n, final int m) { - return BW_A[n][m]; - } - - /** Get the value of the anm coefficient for ch. - * @param n index - * @param m index - * @return the anm coefficient for ch - */ - public double getAnmCh(final int n, final int m) { - return CH_A[n][m]; - } - - /** Get the value of the anm coefficient for cw. - * @param n index - * @param m index - * @return the anm coefficient for cw - */ - public double getAnmCw(final int n, final int m) { - return CW_A[n][m]; - } - - /** Get the value of the bnm coefficient for bh. - * @param n index - * @param m index - * @return the bnm coefficient for bh - */ - public double getBnmBh(final int n, final int m) { - return BH_B[n][m]; - } - - /** Get the value of the bnm coefficient for bw. - * @param n index - * @param m index - * @return the bnm coefficient for bw - */ - public double getBnmBw(final int n, final int m) { - return BW_B[n][m]; - } - - /** Get the value of the bnm coefficient for ch. - * @param n index - * @param m index - * @return the bnm coefficient for ch - */ - public double getBnmCh(final int n, final int m) { - return CH_B[n][m]; - } - - /** Get the value of the bnm coefficient for cw. - * @param n index - * @param m index - * @return the bnm coefficient for cw - */ - public double getBnmCw(final int n, final int m) { - return CW_B[n][m]; - } - - } } diff --git a/src/main/java/org/orekit/models/earth/weather/AbstractGlobalPressureTemperature.java b/src/main/java/org/orekit/models/earth/weather/AbstractGlobalPressureTemperature.java new file mode 100644 index 0000000000..cbeea75663 --- /dev/null +++ b/src/main/java/org/orekit/models/earth/weather/AbstractGlobalPressureTemperature.java @@ -0,0 +1,302 @@ +/* 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.models.earth.weather; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.util.FastMath; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.data.DataSource; +import org.orekit.models.earth.troposphere.AzimuthalGradientCoefficients; +import org.orekit.models.earth.troposphere.AzimuthalGradientProvider; +import org.orekit.models.earth.troposphere.FieldAzimuthalGradientCoefficients; +import org.orekit.models.earth.troposphere.FieldViennaACoefficients; +import org.orekit.models.earth.troposphere.TroposphericModelUtils; +import org.orekit.models.earth.troposphere.ViennaACoefficients; +import org.orekit.models.earth.troposphere.ViennaAProvider; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.TimeScale; +import org.orekit.utils.Constants; + +/** Base class for Global Pressure and Temperature 2, 2w and 3 models. + * These models are empirical models that provide the temperature, the pressure and the water vapor pressure + * of a site depending its latitude and longitude. These models also {@link ViennaACoefficients provide} + * the ah and aw coefficients for Vienna models. + *

        + * The requisite coefficients for the computation of the weather parameters are provided by the + * Department of Geodesy and Geoinformation of the Vienna University. They are based on an + * external grid file like "gpt2_1.grd" (1° x 1°), "gpt2_5.grd" (5° x 5°), "gpt2_1w.grd" (1° x 1°), + * "gpt2_5w.grd" (5° x 5°), "gpt3_1.grd" (1° x 1°), or "gpt3_5.grd" (5° x 5°) available at: + * link + *

        + *

        + * A bilinear interpolation is performed in order to obtained the correct values of the weather parameters. + *

        + *

        + * The format is always the same, with and example shown below for the pressure and the temperature. + * The "GPT2w" model (w stands for wet) also provide humidity parameters and the "GPT3" model also + * provides horizontal gradient, so the number of columns vary depending on the model. + *

        + * Example: + *

        + *
        + * %  lat    lon   p:a0    A1   B1   A2   B2  T:a0    A1   B1   A2   B2
        + *   87.5    2.5 101421    21  409 -217 -122 259.2 -13.2 -6.1  2.6  0.3
        + *   87.5    7.5 101416    21  411 -213 -120 259.3 -13.1 -6.1  2.6  0.3
        + *   87.5   12.5 101411    22  413 -209 -118 259.3 -13.1 -6.1  2.6  0.3
        + *   87.5   17.5 101407    23  415 -205 -116 259.4 -13.0 -6.1  2.6  0.3
        + *   ...
        + * 
        + * + * @see "K. Lagler, M. Schindelegger, J. Böhm, H. Krasna, T. Nilsson (2013), + * GPT2: empirical slant delay model for radio space geodetic techniques. Geophys + * Res Lett 40(6):1069–1073. doi:10.1002/grl.50288" + * + * @author Bryan Cazabonne + * @author Luc Maisonobe + * @since 12.1 + */ +public class AbstractGlobalPressureTemperature + implements ViennaAProvider, AzimuthalGradientProvider, PressureTemperatureHumidityProvider { + + /** Standard gravity constant [m/s²]. */ + private static final double G = Constants.G0_STANDARD_GRAVITY; + + /** Ideal gas constant for dry air [J/kg/K]. */ + private static final double R = 287.0; + + /** Loaded grid. */ + private final Grid grid; + + /** UTC time scale. */ + private final TimeScale utc; + + /** + * Constructor with source of GPTn auxiliary data given by user. + * + * @param source grid data source + * @param utc UTC time scale. + * @param expected expected seasonal models types + * @exception IOException if grid data cannot be read + */ + protected AbstractGlobalPressureTemperature(final DataSource source, final TimeScale utc, + final SeasonalModelType... expected) + throws IOException { + this.utc = utc; + + // load the grid data + try (InputStream is = source.getOpener().openStreamOnce(); + BufferedInputStream bis = new BufferedInputStream(is)) { + final GptNParser parser = new GptNParser(expected); + parser.loadData(bis, source.getName()); + grid = parser.getGrid(); + } + + } + + /** + * Constructor with already loaded grid. + * + * @param grid loaded grid + * @param utc UTC time scale. + * @deprecated as of 12.1 used only by {@link GlobalPressureTemperature2Model} + */ + @Deprecated + protected AbstractGlobalPressureTemperature(final Grid grid, final TimeScale utc) { + this.grid = grid; + this.utc = utc; + } + + /** {@inheritDoc} */ + @Override + public ViennaACoefficients getA(final GeodeticPoint location, final AbsoluteDate date) { + + // set up interpolation parameters + final CellInterpolator interpolator = grid.getInterpolator(location.getLatitude(), location.getLongitude()); + final int dayOfYear = date.getComponents(utc).getDate().getDayOfYear(); + + // ah and aw coefficients + return new ViennaACoefficients(interpolator.interpolate(e -> e.getModel(SeasonalModelType.AH).evaluate(dayOfYear)) * 0.001, + interpolator.interpolate(e -> e.getModel(SeasonalModelType.AW).evaluate(dayOfYear)) * 0.001); + + } + + /** {@inheritDoc} */ + @Override + public PressureTemperatureHumidity getWeatherParamerers(final GeodeticPoint location, final AbsoluteDate date) { + + // set up interpolation parameters + final CellInterpolator interpolator = grid.getInterpolator(location.getLatitude(), location.getLongitude()); + final int dayOfYear = date.getComponents(utc).getDate().getDayOfYear(); + + // Corrected height (can be negative) + final double undu = interpolator.interpolate(e -> e.getUndulation()); + final double correctedheight = location.getAltitude() - undu - interpolator.interpolate(e -> e.getHs()); + + // Temperature gradient [K/m] + final double dTdH = interpolator.interpolate(e -> e.getModel(SeasonalModelType.DT).evaluate(dayOfYear)) * 0.001; + + // Specific humidity + final double qv = interpolator.interpolate(e -> e.getModel(SeasonalModelType.QV).evaluate(dayOfYear)) * 0.001; + + // For the computation of the temperature and the pressure, we use + // the standard ICAO atmosphere formulas. + + // Temperature [K] + final double t0 = interpolator.interpolate(e -> e.getModel(SeasonalModelType.TEMPERATURE).evaluate(dayOfYear)); + final double temperature = t0 + dTdH * correctedheight; + + // Pressure [hPa] + final double p0 = interpolator.interpolate(e -> e.getModel(SeasonalModelType.PRESSURE).evaluate(dayOfYear)); + final double exponent = G / (dTdH * R); + final double pressure = p0 * FastMath.pow(1 - (dTdH / t0) * correctedheight, exponent) * 0.01; + + // Water vapor pressure [hPa] + final double e0 = qv * pressure / (0.622 + 0.378 * qv); + + // mean temperature weighted with water vapor pressure + final double tm = grid.hasModels(SeasonalModelType.TM) ? + interpolator.interpolate(e -> e.getModel(SeasonalModelType.TM).evaluate(dayOfYear)) : + Double.NaN; + + // water vapor decrease factor + final double lambda = grid.hasModels(SeasonalModelType.LAMBDA) ? + interpolator.interpolate(e -> e.getModel(SeasonalModelType.LAMBDA).evaluate(dayOfYear)) : + Double.NaN; + + return new PressureTemperatureHumidity(location.getAltitude(), + TroposphericModelUtils.HECTO_PASCAL.toSI(pressure), + temperature, + TroposphericModelUtils.HECTO_PASCAL.toSI(e0), + tm, + lambda); + + } + + /** {@inheritDoc} */ + @Override + public AzimuthalGradientCoefficients getGradientCoefficients(final GeodeticPoint location, final AbsoluteDate date) { + + if (grid.hasModels(SeasonalModelType.GN_H, SeasonalModelType.GE_H, SeasonalModelType.GN_W, SeasonalModelType.GE_W)) { + // set up interpolation parameters + final CellInterpolator interpolator = grid.getInterpolator(location.getLatitude(), location.getLongitude()); + final int dayOfYear = date.getComponents(utc).getDate().getDayOfYear(); + + return new AzimuthalGradientCoefficients(interpolator.interpolate(e -> e.getModel(SeasonalModelType.GN_H).evaluate(dayOfYear)), + interpolator.interpolate(e -> e.getModel(SeasonalModelType.GE_H).evaluate(dayOfYear)), + interpolator.interpolate(e -> e.getModel(SeasonalModelType.GN_W).evaluate(dayOfYear)), + interpolator.interpolate(e -> e.getModel(SeasonalModelType.GE_W).evaluate(dayOfYear))); + } else { + return null; + } + + } + + /** {@inheritDoc} */ + @Override + public > FieldViennaACoefficients getA(final FieldGeodeticPoint location, + final FieldAbsoluteDate date) { + + // set up interpolation parameters + final FieldCellInterpolator interpolator = grid.getInterpolator(location.getLatitude(), location.getLongitude()); + final int dayOfYear = date.getComponents(utc).getDate().getDayOfYear(); + + // ah and aw coefficients + return new FieldViennaACoefficients<>(interpolator.interpolate(e -> e.getModel(SeasonalModelType.AH).evaluate(dayOfYear)).multiply(0.001), + interpolator.interpolate(e -> e.getModel(SeasonalModelType.AW).evaluate(dayOfYear)).multiply(0.001)); + + } + + /** {@inheritDoc} */ + @Override + public > FieldPressureTemperatureHumidity getWeatherParamerers(final FieldGeodeticPoint location, + final FieldAbsoluteDate date) { + + // set up interpolation parameters + final FieldCellInterpolator interpolator = grid.getInterpolator(location.getLatitude(), location.getLongitude()); + final int dayOfYear = date.getComponents(utc).getDate().getDayOfYear(); + + // Corrected height (can be negative) + final T undu = interpolator.interpolate(e -> e.getUndulation()); + final T correctedheight = location.getAltitude().subtract(undu).subtract(interpolator.interpolate(e -> e.getHs())); + + // Temperature gradient [K/m] + final T dTdH = interpolator.interpolate(e -> e.getModel(SeasonalModelType.DT).evaluate(dayOfYear)).multiply(0.001); + + // Specific humidity + final T qv = interpolator.interpolate(e -> e.getModel(SeasonalModelType.QV).evaluate(dayOfYear)).multiply(0.001); + + // For the computation of the temperature and the pressure, we use + // the standard ICAO atmosphere formulas. + + // Temperature [K] + final T t0 = interpolator.interpolate(e -> e.getModel(SeasonalModelType.TEMPERATURE).evaluate(dayOfYear)); + final T temperature = correctedheight.multiply(dTdH).add(t0); + + // Pressure [hPa] + final T p0 = interpolator.interpolate(e -> e.getModel(SeasonalModelType.PRESSURE).evaluate(dayOfYear)); + final T exponent = dTdH.multiply(R).reciprocal().multiply(G); + final T pressure = FastMath.pow(correctedheight.multiply(dTdH.negate().divide(t0)).add(1), exponent).multiply(p0.multiply(0.01)); + + // Water vapor pressure [hPa] + final T e0 = pressure.multiply(qv.divide(qv.multiply(0.378).add(0.622 ))); + + // mean temperature weighted with water vapor pressure + final T tm = grid.hasModels(SeasonalModelType.TM) ? + interpolator.interpolate(e -> e.getModel(SeasonalModelType.TM).evaluate(dayOfYear)) : + date.getField().getZero().newInstance(Double.NaN); + + // water vapor decrease factor + final T lambda = grid.hasModels(SeasonalModelType.LAMBDA) ? + interpolator.interpolate(e -> e.getModel(SeasonalModelType.LAMBDA).evaluate(dayOfYear)) : + date.getField().getZero().newInstance(Double.NaN); + + return new FieldPressureTemperatureHumidity<>(location.getAltitude(), + TroposphericModelUtils.HECTO_PASCAL.toSI(pressure), + temperature, + TroposphericModelUtils.HECTO_PASCAL.toSI(e0), + tm, + lambda); + + } + + /** {@inheritDoc} */ + @Override + public > FieldAzimuthalGradientCoefficients getGradientCoefficients(final FieldGeodeticPoint location, + final FieldAbsoluteDate date) { + + if (grid.hasModels(SeasonalModelType.GN_H, SeasonalModelType.GE_H, SeasonalModelType.GN_W, SeasonalModelType.GE_W)) { + // set up interpolation parameters + final FieldCellInterpolator interpolator = grid.getInterpolator(location.getLatitude(), location.getLongitude()); + final int dayOfYear = date.getComponents(utc).getDate().getDayOfYear(); + + return new FieldAzimuthalGradientCoefficients<>(interpolator.interpolate(e -> e.getModel(SeasonalModelType.GN_H).evaluate(dayOfYear)), + interpolator.interpolate(e -> e.getModel(SeasonalModelType.GE_H).evaluate(dayOfYear)), + interpolator.interpolate(e -> e.getModel(SeasonalModelType.GN_W).evaluate(dayOfYear)), + interpolator.interpolate(e -> e.getModel(SeasonalModelType.GE_W).evaluate(dayOfYear))); + } else { + return null; + } + + } + +} diff --git a/src/main/java/org/orekit/models/earth/weather/CellInterpolator.java b/src/main/java/org/orekit/models/earth/weather/CellInterpolator.java new file mode 100644 index 0000000000..3a6ed7273f --- /dev/null +++ b/src/main/java/org/orekit/models/earth/weather/CellInterpolator.java @@ -0,0 +1,96 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.weather; + +import java.util.function.ToDoubleFunction; + +import org.hipparchus.analysis.interpolation.BilinearInterpolatingFunction; + +/** Interpolator within a grid cell. + * @author Luc Maisonobe + * @since 12.1 + */ +public class CellInterpolator { + + /** Latitude of point of interest. */ + private final double latitude; + + /** Longitude of point of interest. */ + private final double longitude; + + /** South-West grid entry. */ + private final GridEntry southWest; + + /** South-East grid entry. */ + private final GridEntry southEast; + + /** North-West grid entry. */ + private final GridEntry northWest; + + /** North-East grid entry. */ + private final GridEntry northEast; + + /** Simple constructor. + * @param latitude latitude of point of interest + * @param longitude longitude of point of interest + * @param southWest South-West grid entry + * @param southEast South-East grid entry + * @param northWest North-West grid entry + * @param northEast North-East grid entry + */ + CellInterpolator(final double latitude, final double longitude, + final GridEntry southWest, final GridEntry southEast, + final GridEntry northWest, final GridEntry northEast) { + this.latitude = latitude; + this.longitude = longitude; + this.southWest = southWest; + this.southEast = southEast; + this.northWest = northWest; + this.northEast = northEast; + } + + /** Interpolate a grid function. + * @param gridGetter getter for the grid function + * @return interpolated function" + */ + double interpolate(final ToDoubleFunction gridGetter) { + + // cell surrounding the point + final double[] xVal = new double[] { + southWest.getLongitude(), southEast.getLongitude() + }; + final double[] yVal = new double[] { + southWest.getLatitude(), northWest.getLatitude() + }; + + // evaluate grid points at specified day + final double[][] fval = new double[][] { + { + gridGetter.applyAsDouble(southWest), + gridGetter.applyAsDouble(northWest) + }, { + gridGetter.applyAsDouble(southEast), + gridGetter.applyAsDouble(northEast) + } + }; + + // perform interpolation in the grid + return new BilinearInterpolatingFunction(xVal, yVal, fval).value(longitude, latitude); + + } + +} diff --git a/src/main/java/org/orekit/models/earth/weather/ConstantPressureTemperatureHumidityProvider.java b/src/main/java/org/orekit/models/earth/weather/ConstantPressureTemperatureHumidityProvider.java new file mode 100644 index 0000000000..aa6adb740b --- /dev/null +++ b/src/main/java/org/orekit/models/earth/weather/ConstantPressureTemperatureHumidityProvider.java @@ -0,0 +1,55 @@ +/* Copyright 2002-2024 Thales Alenia Space + * Licensed to CS Communication & Systèmes (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.models.earth.weather; + +import org.hipparchus.CalculusFieldElement; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; + +/** Provider for constant weather parameters. + * @author Luc Maisonobe + * @since 12.1 + */ +public class ConstantPressureTemperatureHumidityProvider implements PressureTemperatureHumidityProvider { + + /** Constant parameters. */ + private final PressureTemperatureHumidity pth; + + /** Simple constructor. + * @param pth constant parameters + */ + public ConstantPressureTemperatureHumidityProvider(final PressureTemperatureHumidity pth) { + this.pth = pth; + } + + /** {@inheritDoc} */ + @Override + public PressureTemperatureHumidity getWeatherParamerers(final GeodeticPoint location, + final AbsoluteDate date) { + return pth; + } + + /** {@inheritDoc} */ + @Override + public > FieldPressureTemperatureHumidity getWeatherParamerers(final FieldGeodeticPoint location, + final FieldAbsoluteDate date) { + return new FieldPressureTemperatureHumidity<>(date.getField(), pth); + } + +} diff --git a/src/main/java/org/orekit/models/earth/weather/FieldCellInterpolator.java b/src/main/java/org/orekit/models/earth/weather/FieldCellInterpolator.java new file mode 100644 index 0000000000..7bf82796df --- /dev/null +++ b/src/main/java/org/orekit/models/earth/weather/FieldCellInterpolator.java @@ -0,0 +1,98 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.weather; + +import java.util.function.ToDoubleFunction; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.analysis.interpolation.BilinearInterpolatingFunction; + +/** Interpolator within a grid cell. + * @param type of the field elements + * @author Luc Maisonobe + * @since 12.1 + */ +public class FieldCellInterpolator> { + + /** Latitude of point of interest. */ + private final T latitude; + + /** Longitude of point of interest. */ + private final T longitude; + + /** South-West grid entry. */ + private final GridEntry southWest; + + /** South-East grid entry. */ + private final GridEntry southEast; + + /** North-West grid entry. */ + private final GridEntry northWest; + + /** North-East grid entry. */ + private final GridEntry northEast; + + /** Simple constructor. + * @param latitude latitude of point of interest + * @param longitude longitude of point of interest + * @param southWest South-West grid entry + * @param southEast South-East grid entry + * @param northWest North-West grid entry + * @param northEast North-East grid entry + */ + FieldCellInterpolator(final T latitude, final T longitude, + final GridEntry southWest, final GridEntry southEast, + final GridEntry northWest, final GridEntry northEast) { + this.latitude = latitude; + this.longitude = longitude; + this.southWest = southWest; + this.southEast = southEast; + this.northWest = northWest; + this.northEast = northEast; + } + + /** Interpolate a grid function. + * @param gridGetter getter for the grid function + * @return interpolated function" + */ + T interpolate(final ToDoubleFunction gridGetter) { + + // cell surrounding the point + final double[] xVal = new double[] { + southWest.getLongitude(), southEast.getLongitude() + }; + final double[] yVal = new double[] { + southWest.getLatitude(), northWest.getLatitude() + }; + + // evaluate grid points at specified day + final double[][] fval = new double[][] { + { + gridGetter.applyAsDouble(southWest), + gridGetter.applyAsDouble(northWest) + }, { + gridGetter.applyAsDouble(southEast), + gridGetter.applyAsDouble(northEast) + } + }; + + // perform interpolation in the grid + return new BilinearInterpolatingFunction(xVal, yVal, fval).value(longitude, latitude); + + } + +} diff --git a/src/main/java/org/orekit/models/earth/weather/FieldPressureTemperature.java b/src/main/java/org/orekit/models/earth/weather/FieldPressureTemperature.java new file mode 100644 index 0000000000..ffa500eeee --- /dev/null +++ b/src/main/java/org/orekit/models/earth/weather/FieldPressureTemperature.java @@ -0,0 +1,80 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.weather; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; + +/** Container for pressure and temperature. + * @param type of the field elements + * @author Luc Maisonobe + * @since 12.1 + */ +public class FieldPressureTemperature> { + + /** Altitude at which weather parameters have been computed. */ + private final T altitude; + + /** Pressure (Pa). */ + private final T pressure; + + /** Temperature (Kelvin). */ + private final T temperature; + + /** Simple constructor. + * @param altitude altitude at which weather parameters have been computed (m) + * @param pressure pressure (Pa) + * @param temperature temperature (Kelvin) + */ + public FieldPressureTemperature(final T altitude, final T pressure, final T temperature) { + this.altitude = altitude; + this.pressure = pressure; + this.temperature = temperature; + } + + /** Simple constructor. + * @param field field to which elements belong + * @param weather regular weather parameters + */ + public FieldPressureTemperature(final Field field, final PressureTemperatureHumidity weather) { + this.altitude = field.getZero().newInstance(weather.getAltitude()); + this.pressure = field.getZero().newInstance(weather.getPressure()); + this.temperature = field.getZero().newInstance(weather.getTemperature()); + } + + /** Get altitude at which weather parameters have been computed. + * @return altitude at which weather parameters have been computed (m) + */ + public T getAltitude() { + return altitude; + } + + /** Get pressure. + * @return pressure (Pa) + */ + public T getPressure() { + return pressure; + } + + /** Get temperature. + * @return temperature (Kelvin) + */ + public T getTemperature() { + return temperature; + } + +} diff --git a/src/main/java/org/orekit/models/earth/weather/FieldPressureTemperatureHumidity.java b/src/main/java/org/orekit/models/earth/weather/FieldPressureTemperatureHumidity.java new file mode 100644 index 0000000000..05172ffcb1 --- /dev/null +++ b/src/main/java/org/orekit/models/earth/weather/FieldPressureTemperatureHumidity.java @@ -0,0 +1,90 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.weather; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; + +/** Container for pressure, temperature, and humidity. + * @param type of the field elements + * @author Luc Maisonobe + * @since 12.1 + */ +public class FieldPressureTemperatureHumidity> extends FieldPressureTemperature { + + /** Humidity as water vapor pressure (Pa). */ + private final T waterVaporPressure; + + /** Mean temperature weighted with water vapor pressure. */ + private final T tm; + + /** Water vapor decrease factor. */ + private final T lambda; + + /** Simple constructor. + * @param altitude altitude at which weather parameters have been computed (m) + * @param pressure pressure (Pa) + * @param temperature temperature (Kelvin) + * @param waterVaporPressure humidity as water vapor pressure (Pa) + * @param tm mean temperature weighted with water vapor pressure + * @param lambda water vapor decrease factor + */ + public FieldPressureTemperatureHumidity(final T altitude, + final T pressure, + final T temperature, + final T waterVaporPressure, + final T tm, + final T lambda) { + super(altitude, pressure, temperature); + this.waterVaporPressure = waterVaporPressure; + this.tm = tm; + this.lambda = lambda; + } + + /** Simple constructor. + * @param field field to which elements belong + * @param weather regular weather parameters + */ + public FieldPressureTemperatureHumidity(final Field field, final PressureTemperatureHumidity weather) { + super(field, weather); + this.waterVaporPressure = field.getZero().newInstance(weather.getWaterVaporPressure()); + this.tm = field.getZero().newInstance(weather.getTm()); + this.lambda = field.getZero().newInstance(weather.getLambda()); + } + + /** Get humidity as water vapor pressure. + * @return humidity as water vapor pressure (Pa) + */ + public T getWaterVaporPressure() { + return waterVaporPressure; + } + + /** Get mean temperature weighted with water vapor pressure. + * @return mean temperature weighted with water vapor pressure + */ + public T getTm() { + return tm; + } + + /** Get water vapor decrease factor. + * @return water vapor decrease factor + */ + public T getLambda() { + return lambda; + } + +} diff --git a/src/main/java/org/orekit/models/earth/weather/GlobalPressureTemperature.java b/src/main/java/org/orekit/models/earth/weather/GlobalPressureTemperature.java new file mode 100644 index 0000000000..dc57c6a4a4 --- /dev/null +++ b/src/main/java/org/orekit/models/earth/weather/GlobalPressureTemperature.java @@ -0,0 +1,698 @@ +/* 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.models.earth.weather; + +import org.hipparchus.util.FastMath; +import org.hipparchus.util.SinCos; +import org.orekit.annotation.DefaultDataContext; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.data.DataContext; +import org.orekit.models.earth.Geoid; +import org.orekit.models.earth.troposphere.TroposphericModelUtils; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.DateTimeComponents; +import org.orekit.time.TimeScale; +import org.orekit.utils.LegendrePolynomials; + +/** The Global Pressure and Temperature model. + * This model is an empirical model that provides the temperature and the pressure depending + * the latitude and the longitude of the station. + *

        + * The Global Pressure and Temperature model is based on spherical harmonics up + * to degree and order of 9. The residual values ​​of this model can reach 20 hPa + * for pressure and 10 ° C for temperature. They are significant for higher latitudes and + * small near the equator (Böhm, 2007) + *

        + * + * @see "J. Böhm, R. Heinkelmann, and H. Schuh (2007), + * Short Note: A global model of pressure and temperature for geodetic applications. J Geod, + * doi:10.1007/s00190-007-0135-3." + * + * @author Bryan Cazabonne + * @author Luc Maisonobe + * @since 12.1 + */ +public class GlobalPressureTemperature { + + /** Temperature gradient (°C/m). */ + private static final double TEMPERATURE_GRADIENT = -6.5e-3; + + /** Spherical harmonics degree. */ + private static final int DEGREE = 9; + + /** Spherical harmonics order. */ + private static final int ORDER = 9; + + /** Geoid used to compute the undulations. */ + private final Geoid geoid; + + /** UTC time scale. */ + private final TimeScale utc; + + /** Build a new instance. + * + *

        This method uses the {@link DataContext#getDefault() default data context}. + * + * @param geoid level surface of the gravity potential of a body + * @see #GlobalPressureTemperature(Geoid, TimeScale) + */ + @DefaultDataContext + public GlobalPressureTemperature(final Geoid geoid) { + this(geoid, DataContext.getDefault().getTimeScales().getUTC()); + } + + /** Build a new instance. + * @param geoid level surface of the gravity potential of a body + * @param utc UTC time scale. + */ + public GlobalPressureTemperature(final Geoid geoid, final TimeScale utc) { + this.geoid = geoid; + this.utc = utc; + } + + /** Provide weather parameters. + * @param location location at which parameters are requested + * @param date date at which parameters are requested + * @return weather parameters + */ + public PressureTemperature getWeatherParameters(final GeodeticPoint location, final AbsoluteDate date) { + + // Day of year computation + final DateTimeComponents dtc = date.getComponents(utc); + final int dofyear = dtc.getDate().getDayOfYear(); + + // Reference day: 28 January 1980 (Niell, 1996) + final int t0 = 28; + final double coef = ((dofyear + 1 - t0) / 365.25) * 2 * FastMath.PI; + final double cosCoef = FastMath.cos(coef); + + // Compute Legendre Polynomials Pnm(sin(phi)) + final LegendrePolynomials p = new LegendrePolynomials(DEGREE, ORDER, + FastMath.sin(location.getLatitude())); + + // Corrected height + final double correctedheight = FastMath.max(0.0, + location.getAltitude() - geoid.getUndulation(location.getLatitude(), + location.getLongitude(), + date)); + + // Eq. 4 (Ref) + double meanT0 = 0.0; + double amplitudeT0 = 0.0; + double meanP0 = 0.0; + double amplitudeP0 = 0.0; + final ABCoefficients abCoef = new ABCoefficients(); + int j = 0; + for (int n = 0; n <= DEGREE; n++) { + for (int m = 0; m <= n; m++) { + final SinCos sc = FastMath.sinCos(m * location.getLongitude()); + final double pCosmLambda = p.getPnm(n, m) * sc.cos(); + final double pSinmLambda = p.getPnm(n, m) * sc.sin(); + + meanT0 = meanT0 + + (abCoef.getAnmTemperatureMean(j) * pCosmLambda + abCoef.getBnmTemperatureMean(j) * pSinmLambda); + amplitudeT0 = amplitudeT0 + + (abCoef.getAnmTemperatureAmpl(j) * pCosmLambda + abCoef.getBnmTemperatureAmpl(j) * pSinmLambda); + meanP0 = meanP0 + + (abCoef.getAnmPressureMean(j) * pCosmLambda + abCoef.getBnmPressureMean(j) * pSinmLambda); + amplitudeP0 = amplitudeP0 + + (abCoef.getAnmPressureAmpl(j) * pCosmLambda + abCoef.getBnmPressureAmpl(j) * pSinmLambda); + + j = j + 1; + } + } + + // Eq. 3 (Ref) + final double temp0 = meanT0 + amplitudeT0 * cosCoef; + final double pres0 = meanP0 + amplitudeP0 * cosCoef; + + // Compute pressure and temperature Eq. 1 and 2 (Ref) + final double degrees = temp0 + TEMPERATURE_GRADIENT * correctedheight; + final double temperature = degrees + 273.15; + final double pressure = pres0 * FastMath.pow(1.0 - correctedheight * 0.0000226, 5.225); + + return new PressureTemperature(location.getAltitude(), + TroposphericModelUtils.HECTO_PASCAL.toSI(pressure), + temperature); + + } + + private static class ABCoefficients { + + /** Mean Anm coefficients for the pressure. */ + private static final double[] A_PRESSURE_MEAN = { + 1.0108e+03, + 8.4886e+00, + 1.4799e+00, + -1.3897e+01, + 3.7516e-03, + -1.4936e-01, + 1.2232e+01, + -7.6615e-01, + -6.7699e-02, + 8.1002e-03, + -1.5874e+01, + 3.6614e-01, + -6.7807e-02, + -3.6309e-03, + 5.9966e-04, + 4.8163e+00, + -3.7363e-01, + -7.2071e-02, + 1.9998e-03, + -6.2385e-04, + -3.7916e-04, + 4.7609e+00, + -3.9534e-01, + 8.6667e-03, + 1.1569e-02, + 1.1441e-03, + -1.4193e-04, + -8.5723e-05, + 6.5008e-01, + -5.0889e-01, + -1.5754e-02, + -2.8305e-03, + 5.7458e-04, + 3.2577e-05, + -9.6052e-06, + -2.7974e-06, + 1.3530e+00, + -2.7271e-01, + -3.0276e-04, + 3.6286e-03, + -2.0398e-04, + 1.5846e-05, + -7.7787e-06, + 1.1210e-06, + 9.9020e-08, + 5.5046e-01, + -2.7312e-01, + 3.2532e-03, + -2.4277e-03, + 1.1596e-04, + 2.6421e-07, + -1.3263e-06, + 2.7322e-07, + 1.4058e-07, + 4.9414e-09 + }; + + /** Mean Bnm coefficients for the pressure. */ + private static final double[] B_PRESSURE_MEAN = { + 0.0000e+00, + 0.0000e+00, + -1.2878e+00, + 0.0000e+00, + 7.0444e-01, + 3.3222e-01, + 0.0000e+00, + -2.9636e-01, + 7.2248e-03, + 7.9655e-03, + 0.0000e+00, + 1.0854e+00, + 1.1145e-02, + -3.6513e-02, + 3.1527e-03, + 0.0000e+00, + -4.8434e-01, + 5.2023e-02, + -1.3091e-02, + 1.8515e-03, + 1.5422e-04, + 0.0000e+00, + 6.8298e-01, + 2.5261e-03, + -9.9703e-04, + -1.0829e-03, + +1.7688e-04, + -3.1418e-05, + +0.0000e+00, + -3.7018e-01, + 4.3234e-02, + 7.2559e-03, + 3.1516e-04, + 2.0024e-05, + -8.0581e-06, + -2.3653e-06, + 0.0000e+00, + 1.0298e-01, + -1.5086e-02, + 5.6186e-03, + 3.2613e-05, + 4.0567e-05, + -1.3925e-06, + -3.6219e-07, + -2.0176e-08, + 0.0000e+00, + -1.8364e-01, + 1.8508e-02, + 7.5016e-04, + -9.6139e-05, + -3.1995e-06, + 1.3868e-07, + -1.9486e-07, + 3.0165e-10, + -6.4376e-10 + }; + + /** Amplitude Anm coefficients for the pressure. */ + private static final double[] A_PRESSURE_AMPLITUDE = { + -1.0444e-01, + 1.6618e-01, + -6.3974e-02, + 1.0922e+00, + 5.7472e-01, + -3.0277e-01, + -3.5087e+00, + 7.1264e-03, + -1.4030e-01, + 3.7050e-02, + 4.0208e-01, + -3.0431e-01, + -1.3292e-01, + 4.6746e-03, + -1.5902e-04, + 2.8624e+00, + -3.9315e-01, + -6.4371e-02, + 1.6444e-02, + -2.3403e-03, + 4.2127e-05, + 1.9945e+00, + -6.0907e-01, + -3.5386e-02, + -1.0910e-03, + -1.2799e-04, + 4.0970e-05, + 2.2131e-05, + -5.3292e-01, + -2.9765e-01, + -3.2877e-02, + 1.7691e-03, + 5.9692e-05, + 3.1725e-05, + 2.0741e-05, + -3.7622e-07, + 2.6372e+00, + -3.1165e-01, + 1.6439e-02, + 2.1633e-04, + 1.7485e-04, + 2.1587e-05, + 6.1064e-06, + -1.3755e-08, + -7.8748e-08, + -5.9152e-01, + -1.7676e-01, + 8.1807e-03, + 1.0445e-03, + 2.3432e-04, + 9.3421e-06, + 2.8104e-06, + -1.5788e-07, + -3.0648e-08, + 2.6421e-10 + }; + + /** Amplitude Bnm coefficients for the pressure. */ + private static final double[] B_PRESSURE_AMPLITUDE = { + 0.0000e+00, + 0.0000e+00, + 9.3340e-01, + 0.0000e+00, + 8.2346e-01, + 2.2082e-01, + 0.0000e+00, + 9.6177e-01, + -1.5650e-02, + 1.2708e-03, + 0.0000e+00, + -3.9913e-01, + 2.8020e-02, + 2.8334e-02, + 8.5980e-04, + 0.0000e+00, + 3.0545e-01, + -2.1691e-02, + 6.4067e-04, + -3.6528e-05, + -1.1166e-04, + 0.0000e+00, + -7.6974e-02, + -1.8986e-02, + +5.6896e-03, + -2.4159e-04, + -2.3033e-04, + -9.6783e-06, + 0.0000e+00, + -1.0218e-01, + -1.3916e-02, + -4.1025e-03, + -5.1340e-05, + -7.0114e-05, + -3.3152e-07, + 1.6901e-06, + 0.0000e+00, + -1.2422e-02, + +2.5072e-03, + +1.1205e-03, + -1.3034e-04, + -2.3971e-05, + -2.6622e-06, + 5.7852e-07, + 4.5847e-08, + 0.0000e+00, + 4.4777e-02, + -3.0421e-03, + 2.6062e-05, + -7.2421e-05, + 1.9119e-06, + 3.9236e-07, + 2.2390e-07, + 2.9765e-09, + -4.6452e-09 + }; + + /** Mean Anm coefficients for the temperature. */ + private static final double[] A_TEMPERATURE_MEAN = { + 1.6257e+01, + 2.1224e+00, + 9.2569e-01, + -2.5974e+01, + 1.4510e+00, + 9.2468e-02, + -5.3192e-01, + 2.1094e-01, + -6.9210e-02, + -3.4060e-02, + -4.6569e+00, + 2.6385e-01, + -3.6093e-02, + 1.0198e-02, + -1.8783e-03, + 7.4983e-01, + 1.1741e-01, + 3.9940e-02, + 5.1348e-03, + 5.9111e-03, + 8.6133e-06, + 6.3057e-01, + 1.5203e-01, + 3.9702e-02, + 4.6334e-03, + 2.4406e-04, + 1.5189e-04, + 1.9581e-07, + 5.4414e-01, + 3.5722e-01, + 5.2763e-02, + 4.1147e-03, + -2.7239e-04, + -5.9957e-05, + 1.6394e-06, + -7.3045e-07, + -2.9394e+00, + 5.5579e-02, + 1.8852e-02, + 3.4272e-03, + -2.3193e-05, + -2.9349e-05, + 3.6397e-07, + 2.0490e-06, + -6.4719e-08, + -5.2225e-01, + 2.0799e-01, + 1.3477e-03, + 3.1613e-04, + -2.2285e-04, + -1.8137e-05, + -1.5177e-07, + 6.1343e-07, + 7.8566e-08, + 1.0749e-09 + }; + + /** Mean Bnm coefficients for the temperature. */ + private static final double[] B_TEMPERATURE_MEAN = { + 0.0000e+00, + 0.0000e+00, + 1.0210e+00, + 0.0000e+00, + 6.0194e-01, + 1.2292e-01, + 0.0000e+00, + -4.2184e-01, + 1.8230e-01, + 4.2329e-02, + 0.0000e+00, + 9.3312e-02, + 9.5346e-02, + -1.9724e-03, + 5.8776e-03, + 0.0000e+00, + -2.0940e-01, + 3.4199e-02, + -5.7672e-03, + -2.1590e-03, + 5.6815e-04, + 0.0000e+00, + 2.2858e-01, + 1.2283e-02, + -9.3679e-03, + -1.4233e-03, + -1.5962e-04, + 4.0160e-05, + 0.0000e+00, + 3.6353e-02, + -9.4263e-04, + -3.6762e-03, + 5.8608e-05, + -2.6391e-05, + 3.2095e-06, + -1.1605e-06, + 0.0000e+00, + 1.6306e-01, + 1.3293e-02, + -1.1395e-03, + 5.1097e-05, + 3.3977e-05, + 7.6449e-06, + -1.7602e-07, + -7.6558e-08, + 0.0000e+00, + -4.5415e-02, + -1.8027e-02, + 3.6561e-04, + -1.1274e-04, + 1.3047e-05, + 2.0001e-06, + -1.5152e-07, + -2.7807e-08, + 7.7491e-09 + }; + + /** Amplitude Anm coefficients for the temperature. */ + private static final double[] A_TEMPERATURE_AMPLITUDE = { + -1.8654e+00, + -9.0041e+00, + -1.2974e-01, + -3.6053e+00, + 2.0284e-02, + 2.1872e-01, + -1.3015e+00, + 4.0355e-01, + 2.2216e-01, + -4.0605e-03, + 1.9623e+00, + 4.2887e-01, + 2.1437e-01, + -1.0061e-02, + -1.1368e-03, + -6.9235e-02, + 5.6758e-01, + 1.1917e-01, + -7.0765e-03, + 3.0017e-04, + 3.0601e-04, + 1.6559e+00, + 2.0722e-01, + 6.0013e-02, + 1.7023e-04, + -9.2424e-04, + 1.1269e-05, + -6.9911e-06, + -2.0886e+00, + -6.7879e-02, + -8.5922e-04, + -1.6087e-03, + -4.5549e-05, + 3.3178e-05, + -6.1715e-06, + -1.4446e-06, + -3.7210e-01, + 1.5775e-01, + -1.7827e-03, + -4.4396e-04, + 2.2844e-04, + -1.1215e-05, + -2.1120e-06, + -9.6421e-07, + -1.4170e-08, + 7.8720e-01, + -4.4238e-02, + -1.5120e-03, + -9.4119e-04, + 4.0645e-06, + -4.9253e-06, + -1.8656e-06, + -4.0736e-07, + -4.9594e-08, + 1.6134e-09 + }; + + /** Amplitude Bnm coefficients for the temperature. */ + private static final double[] B_TEMPERATURE_AMPLITUDE = { + 0.0000e+00, + 0.0000e+00, + -8.9895e-01, + 0.0000e+00, + -1.0790e+00, + -1.2699e-01, + 0.0000e+00, + -5.9033e-01, + 3.4865e-02, + -3.2614e-02, + 0.0000e+00, + -2.4310e-02, + 1.5607e-02, + -2.9833e-02, + -5.9048e-03, + 0.0000e+00, + 2.8383e-01, + 4.0509e-02, + -1.8834e-02, + -1.2654e-03, + -1.3794e-04, + 0.0000e+00, + 1.3306e-01, + 3.4960e-02, + -3.6799e-03, + -3.5626e-04, + 1.4814e-04, + 3.7932e-06, + 0.0000e+00, + 2.0801e-01, + 6.5640e-03, + -3.4893e-03, + -2.7395e-04, + 7.4296e-05, + -7.9927e-06, + -1.0277e-06, + 0.0000e+00, + 3.6515e-02, + -7.4319e-03, + -6.2873e-04, + 8.2461e-05, + 3.1095e-05, + -5.3860e-07, + -1.2055e-07, + -1.1517e-07, + 0.0000e+00, + 3.1404e-02, + 1.5580e-02, + -1.1428e-03, + 3.3529e-05, + 1.0387e-05, + -1.9378e-06, + -2.7327e-07, + 7.5833e-09, + -9.2323e-09 + }; + + /** Build a new instance. */ + ABCoefficients() { + + } + + /** Get the value of the mean Anm pressure coefficient for the given index. + * @param index index + * @return the mean Anm pressure coefficient for the given index + */ + public double getAnmPressureMean(final int index) { + return A_PRESSURE_MEAN[index]; + } + + /** Get the value of the mean Bnm pressure coefficient for the given index. + * @param index index + * @return the mean Bnm pressure coefficient for the given index + */ + public double getBnmPressureMean(final int index) { + return B_PRESSURE_MEAN[index]; + } + + /** Get the value of the amplitude Anm pressure coefficient for the given index. + * @param index index + * @return the amplitude Anm pressure coefficient for the given index. + */ + public double getAnmPressureAmpl(final int index) { + return A_PRESSURE_AMPLITUDE[index]; + } + + /** Get the value of the amplitude Bnm pressure coefficient for the given index. + * @param index index + * @return the amplitude Bnm pressure coefficient for the given index + */ + public double getBnmPressureAmpl(final int index) { + return B_PRESSURE_AMPLITUDE[index]; + } + + /** Get the value of the mean Anm temperature coefficient for the given index. + * @param index index + * @return the mean Anm temperature coefficient for the given index + */ + public double getAnmTemperatureMean(final int index) { + return A_TEMPERATURE_MEAN[index]; + } + + /** Get the value of the mean Bnm temperature coefficient for the given index. + * @param index index + * @return the mean Bnm temperature coefficient for the given index + */ + public double getBnmTemperatureMean(final int index) { + return B_TEMPERATURE_MEAN[index]; + } + + /** Get the value of the amplitude Anm temperature coefficient for the given index. + * @param index index + * @return the amplitude Anm temperature coefficient for the given index. + */ + public double getAnmTemperatureAmpl(final int index) { + return A_TEMPERATURE_AMPLITUDE[index]; + } + + /** Get the value of the amplitude Bnm temperature coefficient for the given index. + * @param index index + * @return the amplitude Bnm temperature coefficient for the given index + */ + public double getBnmTemperatureAmpl(final int index) { + return B_TEMPERATURE_AMPLITUDE[index]; + } + } + +} diff --git a/src/main/java/org/orekit/models/earth/weather/GlobalPressureTemperature2.java b/src/main/java/org/orekit/models/earth/weather/GlobalPressureTemperature2.java new file mode 100644 index 0000000000..57dfacf613 --- /dev/null +++ b/src/main/java/org/orekit/models/earth/weather/GlobalPressureTemperature2.java @@ -0,0 +1,84 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.weather; + +import java.io.IOException; + +import org.orekit.data.DataProvidersManager; +import org.orekit.data.DataSource; +import org.orekit.time.TimeScale; + +/** The Global Pressure and Temperature 2 (GPT2) model. + * @author Luc Maisonobe + * @since 12.1 + */ +public class GlobalPressureTemperature2 extends AbstractGlobalPressureTemperature { + + /** + * Constructor with source of GPT2 auxiliary data given by user. + * + * @param source grid data source (files with extra columns like GPT2w or GPT3 can be used here) + * @param utc UTC time scale. + * @exception IOException if grid data cannot be read + */ + public GlobalPressureTemperature2(final DataSource source, final TimeScale utc) + throws IOException { + super(source, utc, + SeasonalModelType.PRESSURE, + SeasonalModelType.TEMPERATURE, + SeasonalModelType.QV, + SeasonalModelType.DT, + SeasonalModelType.AH, + SeasonalModelType.AW); + } + + /** + * Constructor with supported names and source of GPT2 auxiliary data given by user. + * + * @param supportedNames supported names (files with extra columns like GPT2w or GPT3 can be used here) + * @param dataProvidersManager provides access to auxiliary data. + * @param utc UTC time scale. + * @deprecated as of 12.1 used only by {@link GlobalPressureTemperature2Model} + */ + @Deprecated + protected GlobalPressureTemperature2(final String supportedNames, + final DataProvidersManager dataProvidersManager, + final TimeScale utc) { + super(buildGrid(supportedNames, dataProvidersManager), utc); + } + + /** Builder for grid provided as supported names and source of GPT2 auxiliary data given by user. + * + * @param supportedNames supported names (files with extra columns like GPT2w or GPT3 can be used here) + * @param dataProvidersManager provides access to auxiliary data. + * @return built grid + * @deprecated as of 12.1 used only by {@link GlobalPressureTemperature2Model} + */ + @Deprecated + private static Grid buildGrid(final String supportedNames, + final DataProvidersManager dataProvidersManager) { + final GptNParser parser = new GptNParser(SeasonalModelType.PRESSURE, + SeasonalModelType.TEMPERATURE, + SeasonalModelType.QV, + SeasonalModelType.DT, + SeasonalModelType.AH, + SeasonalModelType.AW); + dataProvidersManager.feed(supportedNames, parser); + return parser.getGrid(); + } + +} diff --git a/src/main/java/org/orekit/models/earth/weather/GlobalPressureTemperature2Model.java b/src/main/java/org/orekit/models/earth/weather/GlobalPressureTemperature2Model.java index 7d749c0531..5fcde946d1 100644 --- a/src/main/java/org/orekit/models/earth/weather/GlobalPressureTemperature2Model.java +++ b/src/main/java/org/orekit/models/earth/weather/GlobalPressureTemperature2Model.java @@ -16,40 +16,21 @@ */ package org.orekit.models.earth.weather; -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.SortedSet; -import java.util.TreeSet; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.ToDoubleFunction; -import java.util.regex.Pattern; - -import org.hipparchus.analysis.interpolation.BilinearInterpolatingFunction; -import org.hipparchus.util.FastMath; -import org.hipparchus.util.MathUtils; -import org.hipparchus.util.SinCos; import org.orekit.annotation.DefaultDataContext; +import org.orekit.bodies.GeodeticPoint; import org.orekit.data.DataContext; -import org.orekit.data.DataLoader; import org.orekit.data.DataProvidersManager; -import org.orekit.errors.OrekitException; -import org.orekit.errors.OrekitMessages; import org.orekit.models.earth.Geoid; -import org.orekit.models.earth.troposphere.ViennaOneModel; +import org.orekit.models.earth.troposphere.TroposphericModelUtils; +import org.orekit.models.earth.troposphere.ViennaACoefficients; import org.orekit.time.AbsoluteDate; import org.orekit.time.TimeScale; -import org.orekit.utils.Constants; /** The Global Pressure and Temperature 2 (GPT2) model. * This model is an empirical model that provides the temperature, the pressure and the water vapor pressure * of a site depending its latitude and longitude. This model also provides the ah - * and aw coefficients used for the {@link ViennaOneModel Vienna 1} model. + * and aw coefficients used for the {@link + * org.orekit.models.earth.troposphere.ViennaOneModel Vienna 1} model. *

        * The requisite coefficients for the computation of the weather parameters are provided by the * Department of Geodesy and Geoinformation of the Vienna University. They are based on an @@ -78,48 +59,22 @@ * Res Lett 40(6):1069–1073. doi:10.1002/grl.50288" * * @author Bryan Cazabonne - * + * as of 12.1, replaced by {@link GlobalPressureTemperature2} */ -public class GlobalPressureTemperature2Model implements WeatherModel { +@Deprecated +public class GlobalPressureTemperature2Model extends GlobalPressureTemperature2 implements WeatherModel { /** Default supported files name pattern. */ public static final String DEFAULT_SUPPORTED_NAMES = "gpt2_\\d+.grd"; - /** Pattern for delimiting regular expressions. */ - private static final Pattern SEPARATOR = Pattern.compile("\\s+"); - - /** Standard gravity constant [m/s²]. */ - private static final double G = Constants.G0_STANDARD_GRAVITY; - - /** Ideal gas constant for dry air [J/kg/K]. */ - private static final double R = 287.0; - - /** Conversion factor from degrees to mill arcseconds. */ - private static final int DEG_TO_MAS = 3600000; - - /** Shared lazily loaded grid. */ - private static final AtomicReference SHARED_GRID = new AtomicReference<>(null); - - /** South-West grid entry. */ - private final GridEntry southWest; - - /** South-East grid entry. */ - private final GridEntry southEast; - - /** North-West grid entry. */ - private final GridEntry northWest; - - /** North-East grid entry. */ - private final GridEntry northEast; - /** The hydrostatic and wet a coefficients loaded. */ private double[] coefficientsA; /** Geodetic site latitude, radians.*/ - private double latitude; + private final double latitude; /** Geodetic site longitude, radians.*/ - private double longitude; + private final double longitude; /** Temperature site, in kelvins. */ private double temperature; @@ -130,20 +85,14 @@ public class GlobalPressureTemperature2Model implements WeatherModel { /** water vapour pressure, in hPa. */ private double e0; - /** Geoid used to compute the undulations. */ - private final Geoid geoid; - - /** UTC time scale. */ - private final TimeScale utc; - /** * Constructor with supported names given by user. This constructor uses the {@link * DataContext#getDefault() default data context}. * - * @param supportedNames supported names + * @param supportedNames supported names (files with extra columns like GPT2w or GPT3 can be used here) * @param latitude geodetic latitude of the station, in radians * @param longitude longitude geodetic longitude of the station, in radians - * @param geoid level surface of the gravity potential of a body + * @param geoid level surface of the gravity potential of a body (ignored since 12.1) * @see #GlobalPressureTemperature2Model(String, double, double, Geoid, * DataProvidersManager, TimeScale) */ @@ -151,17 +100,17 @@ public class GlobalPressureTemperature2Model implements WeatherModel { public GlobalPressureTemperature2Model(final String supportedNames, final double latitude, final double longitude, final Geoid geoid) { this(supportedNames, latitude, longitude, geoid, - DataContext.getDefault().getDataProvidersManager(), - DataContext.getDefault().getTimeScales().getUTC()); + DataContext.getDefault().getDataProvidersManager(), + DataContext.getDefault().getTimeScales().getUTC()); } /** * Constructor with supported names and source of GPT2 auxiliary data given by user. * - * @param supportedNames supported names + * @param supportedNames supported names (files with extra columns like GPT2w or GPT3 can be used here) * @param latitude geodetic latitude of the station, in radians * @param longitude longitude geodetic longitude of the station, in radians - * @param geoid level surface of the gravity potential of a body + * @param ignoredGeoid level surface of the gravity potential of a body (ignored since 12.1) * @param dataProvidersManager provides access to auxiliary data. * @param utc UTC time scale. * @since 10.1 @@ -169,37 +118,16 @@ public GlobalPressureTemperature2Model(final String supportedNames, final double public GlobalPressureTemperature2Model(final String supportedNames, final double latitude, final double longitude, - final Geoid geoid, + final Geoid ignoredGeoid, final DataProvidersManager dataProvidersManager, final TimeScale utc) { + super(supportedNames, dataProvidersManager, utc); this.coefficientsA = null; this.temperature = Double.NaN; this.pressure = Double.NaN; this.e0 = Double.NaN; - this.geoid = geoid; this.latitude = latitude; - this.utc = utc; - - // get the lazily loaded shared grid - Grid grid = SHARED_GRID.get(); - if (grid == null) { - // this is the first instance we create, we need to load the grid data - final Parser parser = new Parser(); - dataProvidersManager.feed(supportedNames, parser); - SHARED_GRID.compareAndSet(null, parser.grid); - grid = parser.grid; - } - - // Normalize longitude according to the grid - this.longitude = MathUtils.normalizeAngle(longitude, grid.entries[0][0].longitude + FastMath.PI); - - final int southIndex = grid.getSouthIndex(this.latitude); - final int westIndex = grid.getWestIndex(this.longitude); - this.southWest = grid.entries[southIndex ][westIndex ]; - this.southEast = grid.entries[southIndex ][westIndex + 1]; - this.northWest = grid.entries[southIndex + 1][westIndex ]; - this.northEast = grid.entries[southIndex + 1][westIndex + 1]; - + this.longitude = longitude; } /** @@ -208,7 +136,7 @@ public GlobalPressureTemperature2Model(final String supportedNames, * * @param latitude geodetic latitude of the station, in radians * @param longitude geodetic latitude of the station, in radians - * @param geoid level surface of the gravity potential of a body + * @param geoid level surface of the gravity potential of a body (ignored since 12.1) * @see #GlobalPressureTemperature2Model(String, double, double, Geoid, * DataProvidersManager, TimeScale) */ @@ -252,326 +180,19 @@ public double getWaterVaporPressure() { @Override public void weatherParameters(final double stationHeight, final AbsoluteDate currentDate) { - final int dayOfYear = currentDate.getComponents(utc).getDate().getDayOfYear(); + final GeodeticPoint location = new GeodeticPoint(latitude, longitude, stationHeight); // ah and aw coefficients + final ViennaACoefficients aC = getA(location, currentDate); coefficientsA = new double[] { - interpolate(e -> evaluate(dayOfYear, e.ah)) * 0.001, - interpolate(e -> evaluate(dayOfYear, e.aw)) * 0.001 - }; - - // Corrected height (can be negative) - final double undu = geoid.getUndulation(latitude, longitude, currentDate); - final double correctedheight = stationHeight - undu - interpolate(e -> e.hS); - - // Temperature gradient [K/m] - final double dTdH = interpolate(e -> evaluate(dayOfYear, e.dT)) * 0.001; - - // Specific humidity - final double qv = interpolate(e -> evaluate(dayOfYear, e.qv0)) * 0.001; - - // For the computation of the temperature and the pressure, we use - // the standard ICAO atmosphere formulas. - - // Temperature [K] - final double t0 = interpolate(e -> evaluate(dayOfYear, e.temperature0)); - this.temperature = t0 + dTdH * correctedheight; - - // Pressure [hPa] - final double p0 = interpolate(e -> evaluate(dayOfYear, e.pressure0)); - final double exponent = G / (dTdH * R); - this.pressure = p0 * FastMath.pow(1 - (dTdH / t0) * correctedheight, exponent) * 0.01; - - // Water vapor pressure [hPa] - this.e0 = qv * pressure / (0.622 + 0.378 * qv); - - } - - /** Interpolate a grid function. - * @param gridGetter getter for the grid function - * @return interpolated function" - */ - private double interpolate(final ToDoubleFunction gridGetter) { - - // cell surrounding the point - final double[] xVal = new double[] { - southWest.longitude, southEast.longitude + aC.getAh(), aC.getAw() }; - final double[] yVal = new double[] { - southWest.latitude, northWest.latitude - }; - - // evaluate grid points at specified day - final double[][] fval = new double[][] { - { - gridGetter.applyAsDouble(southWest), - gridGetter.applyAsDouble(northWest) - }, { - gridGetter.applyAsDouble(southEast), - gridGetter.applyAsDouble(northEast) - } - }; - - // perform interpolation in the grid - return new BilinearInterpolatingFunction(xVal, yVal, fval).value(longitude, latitude); - - } - - /** Evaluate a model for some day. - * @param dayOfYear day to evaluate - * @param model model array - * @return model value at specified day - */ - private double evaluate(final int dayOfYear, final double[] model) { - - final double coef = (dayOfYear / 365.25) * 2 * FastMath.PI; - final SinCos sc1 = FastMath.sinCos(coef); - final SinCos sc2 = FastMath.sinCos(2.0 * coef); - - return model[0] + - model[1] * sc1.cos() + model[2] * sc1.sin() + - model[3] * sc2.cos() + model[4] * sc2.sin(); - - } - - /** Parser for GPT2 grid files. */ - private static class Parser implements DataLoader { - - /** Grid entries. */ - private Grid grid; - - @Override - public boolean stillAcceptsData() { - return grid == null; - } - - @Override - public void loadData(final InputStream input, final String name) - throws IOException, ParseException { - - final SortedSet latSample = new TreeSet<>(); - final SortedSet lonSample = new TreeSet<>(); - final List entries = new ArrayList<>(); - - // Open stream and parse data - int lineNumber = 0; - String line = null; - try (InputStreamReader isr = new InputStreamReader(input, StandardCharsets.UTF_8); - BufferedReader br = new BufferedReader(isr)) { - - for (line = br.readLine(); line != null; line = br.readLine()) { - ++lineNumber; - line = line.trim(); - - // read grid data - if (line.length() > 0 && !line.startsWith("%")) { - final GridEntry entry = new GridEntry(SEPARATOR.split(line)); - latSample.add(entry.latKey); - lonSample.add(entry.lonKey); - entries.add(entry); - } - - } - } catch (NumberFormatException nfe) { - throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, - lineNumber, name, line); - } - - // organize entries in a grid that wraps arouns Earth in longitude - grid = new Grid(latSample, lonSample, entries, name); - - } - - } - - /** Container for complete grid. */ - private static class Grid { - - /** Latitude sample. */ - private final SortedSet latitudeSample; - - /** Longitude sample. */ - private final SortedSet longitudeSample; - - /** Grid entries. */ - private final GridEntry[][] entries; - - /** Simple constructor. - * @param latitudeSample latitude sample - * @param longitudeSample longitude sample - * @param loadedEntries loaded entries, organized as a simple list - * @param name file name - */ - Grid(final SortedSet latitudeSample, final SortedSet longitudeSample, - final List loadedEntries, final String name) { - - final int nA = latitudeSample.size(); - final int nO = longitudeSample.size() + 1; // we add one here for wrapping the grid - this.entries = new GridEntry[nA][nO]; - this.latitudeSample = latitudeSample; - this.longitudeSample = longitudeSample; - - // organize entries in the regular grid - for (final GridEntry entry : loadedEntries) { - final int latitudeIndex = latitudeSample.headSet(entry.latKey + 1).size() - 1; - final int longitudeIndex = longitudeSample.headSet(entry.lonKey + 1).size() - 1; - entries[latitudeIndex][longitudeIndex] = entry; - } - - // finalize the grid - for (final GridEntry[] row : entries) { - - // check for missing entries - for (int longitudeIndex = 0; longitudeIndex < nO - 1; ++longitudeIndex) { - if (row[longitudeIndex] == null) { - throw new OrekitException(OrekitMessages.IRREGULAR_OR_INCOMPLETE_GRID, name); - } - } - - // wrap the grid around the Earth in longitude - row[nO - 1] = new GridEntry(row[0].latitude, row[0].latKey, - row[0].longitude + 2 * FastMath.PI, - row[0].lonKey + DEG_TO_MAS * 360, - row[0].hS, row[0].pressure0, row[0].temperature0, - row[0].qv0, row[0].dT, row[0].ah, row[0].aw); - - } - - } - - /** Get index of South entries in the grid. - * @param latitude latitude to locate (radians) - * @return index of South entries in the grid - */ - public int getSouthIndex(final double latitude) { - - final int latKey = (int) FastMath.rint(FastMath.toDegrees(latitude) * DEG_TO_MAS); - final int index = latitudeSample.headSet(latKey + 1).size() - 1; - - // make sure we have at least one point remaining on North by clipping to size - 2 - return FastMath.min(index, latitudeSample.size() - 2); - - } - - /** Get index of West entries in the grid. - * @param longitude longitude to locate (radians) - * @return index of West entries in the grid - */ - public int getWestIndex(final double longitude) { - - final int lonKey = (int) FastMath.rint(FastMath.toDegrees(longitude) * DEG_TO_MAS); - final int index = longitudeSample.headSet(lonKey + 1).size() - 1; - - // we don't do clipping in longitude because we have added a row to wrap around the Earth - return index; - - } - - } - - /** Container for grid entries. */ - private static class GridEntry { - - /** Latitude (radian). */ - private final double latitude; - - /** Latitude key (mas). */ - private final int latKey; - - /** Longitude (radian). */ - private final double longitude; - - /** Longitude key (mas). */ - private final int lonKey; - - /** Height correction. */ - private final double hS; - - /** Pressure model. */ - private final double[] pressure0; - - /** Temperature model. */ - private final double[] temperature0; - - /** Specific humidity model. */ - private final double[] qv0; - - /** Temperature gradient model. */ - private final double[] dT; - - /** ah coefficient model. */ - private final double[] ah; - - /** aw coefficient model. */ - private final double[] aw; - - /** Build an entry from a parsed line. - * @param fields line fields - */ - GridEntry(final String[] fields) { - - final double latDegree = Double.parseDouble(fields[0]); - final double lonDegree = Double.parseDouble(fields[1]); - latitude = FastMath.toRadians(latDegree); - longitude = FastMath.toRadians(lonDegree); - latKey = (int) FastMath.rint(latDegree * DEG_TO_MAS); - lonKey = (int) FastMath.rint(lonDegree * DEG_TO_MAS); - - hS = Double.parseDouble(fields[23]); - - pressure0 = createModel(fields, 2); - temperature0 = createModel(fields, 7); - qv0 = createModel(fields, 12); - dT = createModel(fields, 17); - ah = createModel(fields, 24); - aw = createModel(fields, 29); - - } - - /** Build an entry from its components. - * @param latitude latitude (radian) - * @param latKey latitude key (mas) - * @param longitude longitude (radian) - * @param lonKey longitude key (mas) - * @param hS height correction - * @param pressure0 pressure model - * @param temperature0 temperature model - * @param qv0 specific humidity model - * @param dT temperature gradient model - * @param ah ah coefficient model - * @param aw aw coefficient model - */ - GridEntry(final double latitude, final int latKey, final double longitude, final int lonKey, - final double hS, final double[] pressure0, final double[] temperature0, - final double[] qv0, final double[] dT, final double[] ah, final double[] aw) { - - this.latitude = latitude; - this.latKey = latKey; - this.longitude = longitude; - this.lonKey = lonKey; - this.hS = hS; - this.pressure0 = pressure0.clone(); - this.temperature0 = temperature0.clone(); - this.qv0 = qv0.clone(); - this.dT = dT.clone(); - this.ah = ah.clone(); - this.aw = aw.clone(); - } - /** Create a time model array. - * @param fields line fields - * @param first index of the first component of the model - * @return time model array - */ - private double[] createModel(final String[] fields, final int first) { - return new double[] { - Double.parseDouble(fields[first ]), - Double.parseDouble(fields[first + 1]), - Double.parseDouble(fields[first + 2]), - Double.parseDouble(fields[first + 3]), - Double.parseDouble(fields[first + 4]) - }; - } + // Pressure, temperature, humidity + final PressureTemperatureHumidity pth = getWeatherParamerers(location, currentDate); + this.temperature = pth.getTemperature(); + this.pressure = TroposphericModelUtils.HECTO_PASCAL.fromSI(pth.getPressure()); + this.e0 = TroposphericModelUtils.HECTO_PASCAL.fromSI(pth.getWaterVaporPressure()); } diff --git a/src/main/java/org/orekit/models/earth/weather/GlobalPressureTemperature2w.java b/src/main/java/org/orekit/models/earth/weather/GlobalPressureTemperature2w.java new file mode 100644 index 0000000000..3f2627b30f --- /dev/null +++ b/src/main/java/org/orekit/models/earth/weather/GlobalPressureTemperature2w.java @@ -0,0 +1,53 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.weather; + +import java.io.IOException; + +import org.orekit.data.DataSource; +import org.orekit.time.TimeScale; + +/** The Global Pressure and Temperature 2w (GPT2w) model. + *

        + * This model adds humidity data to {@link GlobalPressureTemperature2 GPT2}. + *

        + * @author Luc Maisonobe + * @since 12.1 + */ +public class GlobalPressureTemperature2w extends AbstractGlobalPressureTemperature { + + /** + * Constructor with supported names and source of GPT2w auxiliary data given by user. + * + * @param source grid data source (files with extra columns like GPT3 can be used here) + * @param utc UTC time scale. + * @exception IOException if grid data cannot be read + */ + public GlobalPressureTemperature2w(final DataSource source, final TimeScale utc) + throws IOException { + super(source, utc, + SeasonalModelType.PRESSURE, + SeasonalModelType.TEMPERATURE, + SeasonalModelType.QV, + SeasonalModelType.DT, + SeasonalModelType.AH, + SeasonalModelType.AW, + SeasonalModelType.LAMBDA, + SeasonalModelType.TM); + } + +} diff --git a/src/main/java/org/orekit/models/earth/weather/GlobalPressureTemperature3.java b/src/main/java/org/orekit/models/earth/weather/GlobalPressureTemperature3.java new file mode 100644 index 0000000000..0a21c74b36 --- /dev/null +++ b/src/main/java/org/orekit/models/earth/weather/GlobalPressureTemperature3.java @@ -0,0 +1,57 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.weather; + +import java.io.IOException; + +import org.orekit.data.DataSource; +import org.orekit.time.TimeScale; + +/** The Global Pressure and Temperature 3 (GPT3) model. + *

        + * This model adds horizontal gradient data to {@link GlobalPressureTemperature2w GPT2w}. + *

        + * @author Luc Maisonobe + * @since 12.1 + */ +public class GlobalPressureTemperature3 extends AbstractGlobalPressureTemperature { + + /** + * Constructor with source of GPT3 auxiliary data given by user. + * + * @param source grid data source + * @param utc UTC time scale. + * @exception IOException if grid data cannot be read + */ + public GlobalPressureTemperature3(final DataSource source, final TimeScale utc) + throws IOException { + super(source, utc, + SeasonalModelType.PRESSURE, + SeasonalModelType.TEMPERATURE, + SeasonalModelType.QV, + SeasonalModelType.DT, + SeasonalModelType.AH, + SeasonalModelType.AW, + SeasonalModelType.LAMBDA, + SeasonalModelType.TM, + SeasonalModelType.GN_H, + SeasonalModelType.GE_H, + SeasonalModelType.GN_W, + SeasonalModelType.GE_W); + } + +} diff --git a/src/main/java/org/orekit/models/earth/weather/GlobalPressureTemperatureModel.java b/src/main/java/org/orekit/models/earth/weather/GlobalPressureTemperatureModel.java index 423597c5d5..43cab70a47 100644 --- a/src/main/java/org/orekit/models/earth/weather/GlobalPressureTemperatureModel.java +++ b/src/main/java/org/orekit/models/earth/weather/GlobalPressureTemperatureModel.java @@ -16,16 +16,14 @@ */ package org.orekit.models.earth.weather; -import org.hipparchus.util.FastMath; -import org.hipparchus.util.SinCos; import org.orekit.annotation.DefaultDataContext; +import org.orekit.bodies.GeodeticPoint; import org.orekit.data.DataContext; import org.orekit.frames.Frame; import org.orekit.models.earth.Geoid; import org.orekit.models.earth.ReferenceEllipsoid; +import org.orekit.models.earth.troposphere.TroposphericModelUtils; import org.orekit.time.AbsoluteDate; -import org.orekit.time.DateTimeComponents; -import org.orekit.utils.LegendrePolynomials; /** The Global Pressure and Temperature model. * This model is an empirical model that provides the temperature and the pressure depending @@ -42,12 +40,16 @@ * doi:10.1007/s00190-007-0135-3." * * @author Bryan Cazabonne - * + * @deprecated as of 12.1, replaced by {@link GlobalPressureTemperature} */ -public class GlobalPressureTemperatureModel implements WeatherModel { +@Deprecated +public class GlobalPressureTemperatureModel extends GlobalPressureTemperature implements WeatherModel { + + /** Spherical harmonics degree. */ + private static final int DEGREE = 9; - /** Temperature gradient (°C/m). */ - private static final double TEMPERATURE_GRADIENT = -6.5e-3; + /** Spherical harmonics order. */ + private static final int ORDER = 9; /** Geodetic latitude, in radians. */ private final double latitude; @@ -61,12 +63,6 @@ public class GlobalPressureTemperatureModel implements WeatherModel { /** Pressure site, in hPa. */ private double pressure; - /** Body frame related to body shape. */ - private final Frame bodyFrame; - - /** Data context for time and gravity. */ - private final DataContext dataContext; - /** Build a new instance. *

        * At the initialization the values of the pressure and the temperature are set to NaN. @@ -103,12 +99,13 @@ public GlobalPressureTemperatureModel(final double latitude, final double longitude, final Frame bodyFrame, final DataContext dataContext) { - this.bodyFrame = bodyFrame; + super(new Geoid(dataContext.getGravityFields().getNormalizedProvider(DEGREE, ORDER), + ReferenceEllipsoid.getWgs84(bodyFrame)), + dataContext.getTimeScales().getUTC()); this.latitude = latitude; this.longitude = longitude; this.temperature = Double.NaN; this.pressure = Double.NaN; - this.dataContext = dataContext; } /** Get the atmospheric temperature of the station depending its position. @@ -126,610 +123,15 @@ public double getPressure() { } @Override - @DefaultDataContext public void weatherParameters(final double height, final AbsoluteDate date) { - // Day of year computation - final DateTimeComponents dtc = - date.getComponents(dataContext.getTimeScales().getUTC()); - final int dofyear = dtc.getDate().getDayOfYear(); - - // Reference day: 28 January 1980 (Niell, 1996) - final int t0 = 28; - final double coef = ((dofyear + 1 - t0) / 365.25) * 2 * FastMath.PI; - final double cosCoef = FastMath.cos(coef); - - // Compute Legendre Polynomials Pnm(sin(phi)) - final int degree = 9; - final int order = 9; - final LegendrePolynomials p = new LegendrePolynomials(degree, order, FastMath.sin(latitude)); - - // Geoid for height computation - final Geoid geoid = new Geoid( - dataContext.getGravityFields().getNormalizedProvider(degree, order), - ReferenceEllipsoid.getWgs84(bodyFrame)); - - // Corrected height - final double correctedheight = FastMath.max(0.0, height - geoid.getUndulation(latitude, longitude, date)); - - // Eq. 4 (Ref) - double meanT0 = 0.0; - double amplitudeT0 = 0.0; - double meanP0 = 0.0; - double amplitudeP0 = 0.0; - final ABCoefficients abCoef = new ABCoefficients(); - int j = 0; - for (int n = 0; n <= 9; n++) { - for (int m = 0; m <= n; m++) { - final SinCos sc = FastMath.sinCos(m * longitude); - final double pCosmLambda = p.getPnm(n, m) * sc.cos(); - final double pSinmLambda = p.getPnm(n, m) * sc.sin(); - - meanT0 = meanT0 + - (abCoef.getAnmTemperatureMean(j) * pCosmLambda + abCoef.getBnmTemperatureMean(j) * pSinmLambda); - amplitudeT0 = amplitudeT0 + - (abCoef.getAnmTemperatureAmpl(j) * pCosmLambda + abCoef.getBnmTemperatureAmpl(j) * pSinmLambda); - meanP0 = meanP0 + - (abCoef.getAnmPressureMean(j) * pCosmLambda + abCoef.getBnmPressureMean(j) * pSinmLambda); - amplitudeP0 = amplitudeP0 + - (abCoef.getAnmPressureAmpl(j) * pCosmLambda + abCoef.getBnmPressureAmpl(j) * pSinmLambda); - - j = j + 1; - } - } - - // Eq. 3 (Ref) - final double temp0 = meanT0 + amplitudeT0 * cosCoef; - final double pres0 = meanP0 + amplitudeP0 * cosCoef; - - // Compute pressure and temperature Eq. 1 and 2 (Ref) - final double degrees = temp0 + TEMPERATURE_GRADIENT * correctedheight; - this.temperature = degrees + 273.15; - this.pressure = pres0 * FastMath.pow(1.0 - correctedheight * 0.0000226, 5.225); - } - - private static class ABCoefficients { - - /** Mean Anm coefficients for the pressure. */ - private static final double[] A_PRESSURE_MEAN = { - 1.0108e+03, - 8.4886e+00, - 1.4799e+00, - -1.3897e+01, - 3.7516e-03, - -1.4936e-01, - 1.2232e+01, - -7.6615e-01, - -6.7699e-02, - 8.1002e-03, - -1.5874e+01, - 3.6614e-01, - -6.7807e-02, - -3.6309e-03, - 5.9966e-04, - 4.8163e+00, - -3.7363e-01, - -7.2071e-02, - 1.9998e-03, - -6.2385e-04, - -3.7916e-04, - 4.7609e+00, - -3.9534e-01, - 8.6667e-03, - 1.1569e-02, - 1.1441e-03, - -1.4193e-04, - -8.5723e-05, - 6.5008e-01, - -5.0889e-01, - -1.5754e-02, - -2.8305e-03, - 5.7458e-04, - 3.2577e-05, - -9.6052e-06, - -2.7974e-06, - 1.3530e+00, - -2.7271e-01, - -3.0276e-04, - 3.6286e-03, - -2.0398e-04, - 1.5846e-05, - -7.7787e-06, - 1.1210e-06, - 9.9020e-08, - 5.5046e-01, - -2.7312e-01, - 3.2532e-03, - -2.4277e-03, - 1.1596e-04, - 2.6421e-07, - -1.3263e-06, - 2.7322e-07, - 1.4058e-07, - 4.9414e-09 - }; - - /** Mean Bnm coefficients for the pressure. */ - private static final double[] B_PRESSURE_MEAN = { - 0.0000e+00, - 0.0000e+00, - -1.2878e+00, - 0.0000e+00, - 7.0444e-01, - 3.3222e-01, - 0.0000e+00, - -2.9636e-01, - 7.2248e-03, - 7.9655e-03, - 0.0000e+00, - 1.0854e+00, - 1.1145e-02, - -3.6513e-02, - 3.1527e-03, - 0.0000e+00, - -4.8434e-01, - 5.2023e-02, - -1.3091e-02, - 1.8515e-03, - 1.5422e-04, - 0.0000e+00, - 6.8298e-01, - 2.5261e-03, - -9.9703e-04, - -1.0829e-03, - +1.7688e-04, - -3.1418e-05, - +0.0000e+00, - -3.7018e-01, - 4.3234e-02, - 7.2559e-03, - 3.1516e-04, - 2.0024e-05, - -8.0581e-06, - -2.3653e-06, - 0.0000e+00, - 1.0298e-01, - -1.5086e-02, - 5.6186e-03, - 3.2613e-05, - 4.0567e-05, - -1.3925e-06, - -3.6219e-07, - -2.0176e-08, - 0.0000e+00, - -1.8364e-01, - 1.8508e-02, - 7.5016e-04, - -9.6139e-05, - -3.1995e-06, - 1.3868e-07, - -1.9486e-07, - 3.0165e-10, - -6.4376e-10 - }; - - /** Amplitude Anm coefficients for the pressure. */ - private static final double[] A_PRESSURE_AMPLITUDE = { - -1.0444e-01, - 1.6618e-01, - -6.3974e-02, - 1.0922e+00, - 5.7472e-01, - -3.0277e-01, - -3.5087e+00, - 7.1264e-03, - -1.4030e-01, - 3.7050e-02, - 4.0208e-01, - -3.0431e-01, - -1.3292e-01, - 4.6746e-03, - -1.5902e-04, - 2.8624e+00, - -3.9315e-01, - -6.4371e-02, - 1.6444e-02, - -2.3403e-03, - 4.2127e-05, - 1.9945e+00, - -6.0907e-01, - -3.5386e-02, - -1.0910e-03, - -1.2799e-04, - 4.0970e-05, - 2.2131e-05, - -5.3292e-01, - -2.9765e-01, - -3.2877e-02, - 1.7691e-03, - 5.9692e-05, - 3.1725e-05, - 2.0741e-05, - -3.7622e-07, - 2.6372e+00, - -3.1165e-01, - 1.6439e-02, - 2.1633e-04, - 1.7485e-04, - 2.1587e-05, - 6.1064e-06, - -1.3755e-08, - -7.8748e-08, - -5.9152e-01, - -1.7676e-01, - 8.1807e-03, - 1.0445e-03, - 2.3432e-04, - 9.3421e-06, - 2.8104e-06, - -1.5788e-07, - -3.0648e-08, - 2.6421e-10 - }; - - /** Amplitude Bnm coefficients for the pressure. */ - private static final double[] B_PRESSURE_AMPLITUDE = { - 0.0000e+00, - 0.0000e+00, - 9.3340e-01, - 0.0000e+00, - 8.2346e-01, - 2.2082e-01, - 0.0000e+00, - 9.6177e-01, - -1.5650e-02, - 1.2708e-03, - 0.0000e+00, - -3.9913e-01, - 2.8020e-02, - 2.8334e-02, - 8.5980e-04, - 0.0000e+00, - 3.0545e-01, - -2.1691e-02, - 6.4067e-04, - -3.6528e-05, - -1.1166e-04, - 0.0000e+00, - -7.6974e-02, - -1.8986e-02, - +5.6896e-03, - -2.4159e-04, - -2.3033e-04, - -9.6783e-06, - 0.0000e+00, - -1.0218e-01, - -1.3916e-02, - -4.1025e-03, - -5.1340e-05, - -7.0114e-05, - -3.3152e-07, - 1.6901e-06, - 0.0000e+00, - -1.2422e-02, - +2.5072e-03, - +1.1205e-03, - -1.3034e-04, - -2.3971e-05, - -2.6622e-06, - 5.7852e-07, - 4.5847e-08, - 0.0000e+00, - 4.4777e-02, - -3.0421e-03, - 2.6062e-05, - -7.2421e-05, - 1.9119e-06, - 3.9236e-07, - 2.2390e-07, - 2.9765e-09, - -4.6452e-09 - }; - - /** Mean Anm coefficients for the temperature. */ - private static final double[] A_TEMPERATURE_MEAN = { - 1.6257e+01, - 2.1224e+00, - 9.2569e-01, - -2.5974e+01, - 1.4510e+00, - 9.2468e-02, - -5.3192e-01, - 2.1094e-01, - -6.9210e-02, - -3.4060e-02, - -4.6569e+00, - 2.6385e-01, - -3.6093e-02, - 1.0198e-02, - -1.8783e-03, - 7.4983e-01, - 1.1741e-01, - 3.9940e-02, - 5.1348e-03, - 5.9111e-03, - 8.6133e-06, - 6.3057e-01, - 1.5203e-01, - 3.9702e-02, - 4.6334e-03, - 2.4406e-04, - 1.5189e-04, - 1.9581e-07, - 5.4414e-01, - 3.5722e-01, - 5.2763e-02, - 4.1147e-03, - -2.7239e-04, - -5.9957e-05, - 1.6394e-06, - -7.3045e-07, - -2.9394e+00, - 5.5579e-02, - 1.8852e-02, - 3.4272e-03, - -2.3193e-05, - -2.9349e-05, - 3.6397e-07, - 2.0490e-06, - -6.4719e-08, - -5.2225e-01, - 2.0799e-01, - 1.3477e-03, - 3.1613e-04, - -2.2285e-04, - -1.8137e-05, - -1.5177e-07, - 6.1343e-07, - 7.8566e-08, - 1.0749e-09 - }; - - /** Mean Bnm coefficients for the temperature. */ - private static final double[] B_TEMPERATURE_MEAN = { - 0.0000e+00, - 0.0000e+00, - 1.0210e+00, - 0.0000e+00, - 6.0194e-01, - 1.2292e-01, - 0.0000e+00, - -4.2184e-01, - 1.8230e-01, - 4.2329e-02, - 0.0000e+00, - 9.3312e-02, - 9.5346e-02, - -1.9724e-03, - 5.8776e-03, - 0.0000e+00, - -2.0940e-01, - 3.4199e-02, - -5.7672e-03, - -2.1590e-03, - 5.6815e-04, - 0.0000e+00, - 2.2858e-01, - 1.2283e-02, - -9.3679e-03, - -1.4233e-03, - -1.5962e-04, - 4.0160e-05, - 0.0000e+00, - 3.6353e-02, - -9.4263e-04, - -3.6762e-03, - 5.8608e-05, - -2.6391e-05, - 3.2095e-06, - -1.1605e-06, - 0.0000e+00, - 1.6306e-01, - 1.3293e-02, - -1.1395e-03, - 5.1097e-05, - 3.3977e-05, - 7.6449e-06, - -1.7602e-07, - -7.6558e-08, - 0.0000e+00, - -4.5415e-02, - -1.8027e-02, - 3.6561e-04, - -1.1274e-04, - 1.3047e-05, - 2.0001e-06, - -1.5152e-07, - -2.7807e-08, - 7.7491e-09 - }; - - /** Amplitude Anm coefficients for the temperature. */ - private static final double[] A_TEMPERATURE_AMPLITUDE = { - -1.8654e+00, - -9.0041e+00, - -1.2974e-01, - -3.6053e+00, - 2.0284e-02, - 2.1872e-01, - -1.3015e+00, - 4.0355e-01, - 2.2216e-01, - -4.0605e-03, - 1.9623e+00, - 4.2887e-01, - 2.1437e-01, - -1.0061e-02, - -1.1368e-03, - -6.9235e-02, - 5.6758e-01, - 1.1917e-01, - -7.0765e-03, - 3.0017e-04, - 3.0601e-04, - 1.6559e+00, - 2.0722e-01, - 6.0013e-02, - 1.7023e-04, - -9.2424e-04, - 1.1269e-05, - -6.9911e-06, - -2.0886e+00, - -6.7879e-02, - -8.5922e-04, - -1.6087e-03, - -4.5549e-05, - 3.3178e-05, - -6.1715e-06, - -1.4446e-06, - -3.7210e-01, - 1.5775e-01, - -1.7827e-03, - -4.4396e-04, - 2.2844e-04, - -1.1215e-05, - -2.1120e-06, - -9.6421e-07, - -1.4170e-08, - 7.8720e-01, - -4.4238e-02, - -1.5120e-03, - -9.4119e-04, - 4.0645e-06, - -4.9253e-06, - -1.8656e-06, - -4.0736e-07, - -4.9594e-08, - 1.6134e-09 - }; - - /** Amplitude Bnm coefficients for the temperature. */ - private static final double[] B_TEMPERATURE_AMPLITUDE = { - 0.0000e+00, - 0.0000e+00, - -8.9895e-01, - 0.0000e+00, - -1.0790e+00, - -1.2699e-01, - 0.0000e+00, - -5.9033e-01, - 3.4865e-02, - -3.2614e-02, - 0.0000e+00, - -2.4310e-02, - 1.5607e-02, - -2.9833e-02, - -5.9048e-03, - 0.0000e+00, - 2.8383e-01, - 4.0509e-02, - -1.8834e-02, - -1.2654e-03, - -1.3794e-04, - 0.0000e+00, - 1.3306e-01, - 3.4960e-02, - -3.6799e-03, - -3.5626e-04, - 1.4814e-04, - 3.7932e-06, - 0.0000e+00, - 2.0801e-01, - 6.5640e-03, - -3.4893e-03, - -2.7395e-04, - 7.4296e-05, - -7.9927e-06, - -1.0277e-06, - 0.0000e+00, - 3.6515e-02, - -7.4319e-03, - -6.2873e-04, - 8.2461e-05, - 3.1095e-05, - -5.3860e-07, - -1.2055e-07, - -1.1517e-07, - 0.0000e+00, - 3.1404e-02, - 1.5580e-02, - -1.1428e-03, - 3.3529e-05, - 1.0387e-05, - -1.9378e-06, - -2.7327e-07, - 7.5833e-09, - -9.2323e-09 - }; - - /** Build a new instance. */ - ABCoefficients() { - - } - - /** Get the value of the mean Anm pressure coefficient for the given index. - * @param index index - * @return the mean Anm pressure coefficient for the given index - */ - public double getAnmPressureMean(final int index) { - return A_PRESSURE_MEAN[index]; - } - - /** Get the value of the mean Bnm pressure coefficient for the given index. - * @param index index - * @return the mean Bnm pressure coefficient for the given index - */ - public double getBnmPressureMean(final int index) { - return B_PRESSURE_MEAN[index]; - } - - /** Get the value of the amplitude Anm pressure coefficient for the given index. - * @param index index - * @return the amplitude Anm pressure coefficient for the given index. - */ - public double getAnmPressureAmpl(final int index) { - return A_PRESSURE_AMPLITUDE[index]; - } - - /** Get the value of the amplitude Bnm pressure coefficient for the given index. - * @param index index - * @return the amplitude Bnm pressure coefficient for the given index - */ - public double getBnmPressureAmpl(final int index) { - return B_PRESSURE_AMPLITUDE[index]; - } - - /** Get the value of the mean Anm temperature coefficient for the given index. - * @param index index - * @return the mean Anm temperature coefficient for the given index - */ - public double getAnmTemperatureMean(final int index) { - return A_TEMPERATURE_MEAN[index]; - } - - /** Get the value of the mean Bnm temperature coefficient for the given index. - * @param index index - * @return the mean Bnm temperature coefficient for the given index - */ - public double getBnmTemperatureMean(final int index) { - return B_TEMPERATURE_MEAN[index]; - } + final GeodeticPoint location = new GeodeticPoint(latitude, longitude, height); - /** Get the value of the amplitude Anm temperature coefficient for the given index. - * @param index index - * @return the amplitude Anm temperature coefficient for the given index. - */ - public double getAnmTemperatureAmpl(final int index) { - return A_TEMPERATURE_AMPLITUDE[index]; - } + // Pressure and temperature + final PressureTemperature pt = getWeatherParameters(location, date); + this.temperature = pt.getTemperature(); + this.pressure = TroposphericModelUtils.HECTO_PASCAL.fromSI(pt.getPressure()); - /** Get the value of the amplitude Bnm temperature coefficient for the given index. - * @param index index - * @return the amplitude Bnm temperature coefficient for the given index - */ - public double getBnmTemperatureAmpl(final int index) { - return B_TEMPERATURE_AMPLITUDE[index]; - } } } diff --git a/src/main/java/org/orekit/models/earth/weather/GptNParser.java b/src/main/java/org/orekit/models/earth/weather/GptNParser.java new file mode 100644 index 0000000000..4fafbbcdd6 --- /dev/null +++ b/src/main/java/org/orekit/models/earth/weather/GptNParser.java @@ -0,0 +1,309 @@ +/* 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.models.earth.weather; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.regex.Pattern; + +import org.hipparchus.util.FastMath; +import org.orekit.data.DataLoader; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; + +/** Base parser for Global Pressure and Temperature 2, 2w and 3 models. + *

        + * The format for all models is always the same, with and example shown below + * for the pressure and the temperature. The "GPT2w" model (w stands for wet) + * also provides humidity parameters and the "GPT3" model also provides horizontal + * gradient, so the number of columns vary depending on the model. + *

        + * Example: + *

        + *
        + * %  lat    lon   p:a0    A1   B1   A2   B2  T:a0    A1   B1   A2   B2
        + *   87.5    2.5 101421    21  409 -217 -122 259.2 -13.2 -6.1  2.6  0.3
        + *   87.5    7.5 101416    21  411 -213 -120 259.3 -13.1 -6.1  2.6  0.3
        + *   87.5   12.5 101411    22  413 -209 -118 259.3 -13.1 -6.1  2.6  0.3
        + *   87.5   17.5 101407    23  415 -205 -116 259.4 -13.0 -6.1  2.6  0.3
        + *   ...
        + * 
        + * + * @see "K. Lagler, M. Schindelegger, J. Böhm, H. Krasna, T. Nilsson (2013), + * GPT2: empirical slant delay model for radio space geodetic techniques. Geophys + * Res Lett 40(6):1069–1073. doi:10.1002/grl.50288" + * + * @author Bryan Cazabonne + * @author Luc Maisonobe + * @since 12.1 + */ +class GptNParser implements DataLoader { + + /** Comment prefix. */ + private static final String COMMENT = "%"; + + /** Pattern for delimiting regular expressions. */ + private static final Pattern SEPARATOR = Pattern.compile("\\s+"); + + /** Label for latitude field. */ + private static final String LATITUDE_LABEL = "lat"; + + /** Label for longitude field. */ + private static final String LONGITUDE_LABEL = "lon"; + + /** Label for undulation field. */ + private static final String UNDULATION_LABEL = "undu"; + + /** Label for height correction field. */ + private static final String HEIGHT_CORRECTION_LABEL = "Hs"; + + /** Label for annual cosine amplitude field. */ + private static final String A1 = "A1"; + + /** Label for annual sine amplitude field. */ + private static final String B1 = "B1"; + + /** Label for semi-annual cosine amplitude field. */ + private static final String A2 = "A2"; + + /** Label for semi-annual sine amplitude field. */ + private static final String B2 = "B2"; + + /** Expected seasonal models types. */ + private final SeasonalModelType expected[]; + + /** Index for latitude field. */ + private int latitudeIndex; + + /** Index for longitude field. */ + private int longitudeIndex; + + /** Index for undulation field. */ + private int undulationIndex; + + /** Index for height correction field. */ + private int heightCorrectionIndex; + + /** Maximum index. */ + private int maxIndex; + + /** Indices for expected seasonal models types field. */ + private int expectedIndices[]; + + /** Grid entries. */ + private Grid grid; + + /** Simple constructor. + * @param expected expected seasonal models types + */ + GptNParser(final SeasonalModelType... expected) { + this.expected = expected.clone(); + this.expectedIndices = new int[expected.length]; + } + + @Override + public boolean stillAcceptsData() { + return grid == null; + } + + @Override + public void loadData(final InputStream input, final String name) throws IOException { + + final SortedSet latSample = new TreeSet<>(); + final SortedSet lonSample = new TreeSet<>(); + final List entries = new ArrayList<>(); + + // Open stream and parse data + int lineNumber = 0; + String line = null; + try (InputStreamReader isr = new InputStreamReader(input, StandardCharsets.UTF_8); + BufferedReader br = new BufferedReader(isr)) { + for (line = br.readLine(); line != null; line = br.readLine()) { + ++lineNumber; + line = line.trim(); + if (lineNumber == 1) { + // read header and store columns numbers + parseHeader(line, lineNumber, name); + } else { + // read grid data + final GridEntry entry = parseEntry(line, lineNumber, name); + latSample.add(entry.getLatKey()); + lonSample.add(entry.getLonKey()); + entries.add(entry); + } + + } + } + + // organize entries in a grid that wraps around Earth in longitude + grid = new Grid(latSample, lonSample, entries, name); + + } + + /** Parse header line in the grid file. + * @param line grid line + * @param lineNumber line number + * @param name file name + */ + private void parseHeader(final String line, final int lineNumber, final String name) { + + // reset indices + latitudeIndex = -1; + longitudeIndex = -1; + undulationIndex = -1; + heightCorrectionIndex = -1; + maxIndex = -1; + Arrays.fill(expectedIndices, -1); + + final String[] fields = SEPARATOR.split(line.substring(COMMENT.length()).trim()); + String lookingFor = LATITUDE_LABEL; + for (int i = 0; i < fields.length; ++i) { + maxIndex = FastMath.max(maxIndex, i); + checkLabel(fields[i], lookingFor, line, lineNumber, name); + switch (fields[i]) { + case LATITUDE_LABEL : + latitudeIndex = i; + lookingFor = LONGITUDE_LABEL; + break; + case LONGITUDE_LABEL : + lookingFor = null; + longitudeIndex = i; + break; + case UNDULATION_LABEL : + lookingFor = HEIGHT_CORRECTION_LABEL; + undulationIndex = i; + break; + case HEIGHT_CORRECTION_LABEL : + lookingFor = null; + heightCorrectionIndex = i; + break; + case A1 : + lookingFor = B1; + break; + case B1 : + lookingFor = A2; + break; + case A2 : + lookingFor = B2; + break; + case B2 : + lookingFor = null; + break; + default : { + final SeasonalModelType type = SeasonalModelType.parseType(fields[i], lineNumber, name); + for (int j = 0; j < expected.length; ++j) { + if (type == expected[j]) { + expectedIndices[j] = i; + lookingFor = A1; + break; + } + } + } + } + } + + // check all indices have been set + int minIndex = FastMath.min(latitudeIndex, + FastMath.min(longitudeIndex, + FastMath.min(undulationIndex, + heightCorrectionIndex))); + for (int index : expectedIndices) { + minIndex = FastMath.min(minIndex, index); + } + if (minIndex < 0) { + // some indices in the header are missing + throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, + lineNumber, name, line); + } + + } + + /** Check if header label is what we are looking for. + * @param label label to check + * @param lookingFor label we are looking for, or null if we don't known what to expect + * @param line grid line + * @param lineNumber line number + * @param name file name + */ + private void checkLabel(final String label, final String lookingFor, + final String line, final int lineNumber, final String name) { + if (lookingFor != null && !lookingFor.equals(label)) { + throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, + lineNumber, name, line); + } + } + + /** Parse one entry in the grid file. + * @param line grid line + * @param lineNumber line number + * @param name file name + * @return parsed entry + */ + private GridEntry parseEntry(final String line, final int lineNumber, final String name) { + try { + + final String[] fields = SEPARATOR.split(line); + if (fields.length != maxIndex + 1) { + throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, + lineNumber, name, line); + } + + final double latDegree = Double.parseDouble(fields[latitudeIndex]); + final double lonDegree = Double.parseDouble(fields[longitudeIndex]); + + final Map models = new HashMap<>(expected.length); + for (int i = 0; i < expected.length; ++i) { + final int first = expectedIndices[i]; + models.put(expected[i], new SeasonalModel(Double.parseDouble(fields[first ]), + Double.parseDouble(fields[first + 1]), + Double.parseDouble(fields[first + 2]), + Double.parseDouble(fields[first + 3]), + Double.parseDouble(fields[first + 4]))); + } + + return new GridEntry(FastMath.toRadians(latDegree), + (int) FastMath.rint(latDegree * GridEntry.DEG_TO_MAS), + FastMath.toRadians(lonDegree), + (int) FastMath.rint(lonDegree * GridEntry.DEG_TO_MAS), + Double.parseDouble(fields[undulationIndex]), + Double.parseDouble(fields[heightCorrectionIndex]), + models); + + } catch (NumberFormatException nfe) { + throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, + lineNumber, name, line); + } + } + + /** Get the parsed grid. + * @return parsed grid + */ + public Grid getGrid() { + return grid; + } + +} diff --git a/src/main/java/org/orekit/models/earth/weather/Grid.java b/src/main/java/org/orekit/models/earth/weather/Grid.java new file mode 100644 index 0000000000..f87aa83c86 --- /dev/null +++ b/src/main/java/org/orekit/models/earth/weather/Grid.java @@ -0,0 +1,174 @@ +/* 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.models.earth.weather; + +import java.util.List; +import java.util.SortedSet; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.MathUtils; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; + +/** Container for a complete grid. + * @author Bryan Cazabonne + * @author Luc Maisonobe + * @since 12.1 + */ +class Grid { + + /** Latitude sample. */ + private final SortedSet latitudeSample; + + /** Longitude sample. */ + private final SortedSet longitudeSample; + + /** Grid entries. */ + private final GridEntry[][] entries; + + /** Simple constructor. + * @param latitudeSample latitude sample + * @param longitudeSample longitude sample + * @param loadedEntries loaded entries, organized as a simple list + * @param name file name + */ + Grid(final SortedSet latitudeSample, final SortedSet longitudeSample, + final List loadedEntries, final String name) { + + final int nA = latitudeSample.size(); + final int nO = longitudeSample.size() + 1; // we add one here for wrapping the grid + this.entries = new GridEntry[nA][nO]; + this.latitudeSample = latitudeSample; + this.longitudeSample = longitudeSample; + + // organize entries in the regular grid + for (final GridEntry entry : loadedEntries) { + final int latitudeIndex = latitudeSample.headSet(entry.getLatKey() + 1).size() - 1; + final int longitudeIndex = longitudeSample.headSet(entry.getLonKey() + 1).size() - 1; + entries[latitudeIndex][longitudeIndex] = entry; + } + + // finalize the grid + for (final GridEntry[] row : entries) { + + // check for missing entries + for (int longitudeIndex = 0; longitudeIndex < nO - 1; ++longitudeIndex) { + if (row[longitudeIndex] == null) { + throw new OrekitException(OrekitMessages.IRREGULAR_OR_INCOMPLETE_GRID, name); + } + } + + // wrap the grid around the Earth in longitude + row[nO - 1] = row[0].buildWrappedEntry(); + + } + + } + + /** Get index of South entries in the grid. + * @param latitude latitude to locate (radians) + * @return index of South entries in the grid + */ + private int getSouthIndex(final double latitude) { + + final int latKey = (int) FastMath.rint(FastMath.toDegrees(latitude) * GridEntry.DEG_TO_MAS); + final int index = latitudeSample.headSet(latKey + 1).size() - 1; + + // make sure we have at least one point remaining on North by clipping to size - 2 + return FastMath.min(index, latitudeSample.size() - 2); + + } + + /** Get index of West entries in the grid. + * @param longitude longitude to locate (radians) + * @return index of West entries in the grid + */ + private int getWestIndex(final double longitude) { + + final int lonKey = (int) FastMath.rint(FastMath.toDegrees(longitude) * GridEntry.DEG_TO_MAS); + final int index = longitudeSample.headSet(lonKey + 1).size() - 1; + + // we don't do clipping in longitude because we have added a row to wrap around the Earth + return index; + + } + + /** Get interpolator within a cell. + * @param latitude latitude of point of interest + * @param longitude longitude of point of interest + * @return interpolator for the cell + */ + CellInterpolator getInterpolator(final double latitude, final double longitude) { + + // keep longitude within grid range + final double normalizedLongitude = + MathUtils.normalizeAngle(longitude, + entries[0][0].getLongitude() + FastMath.PI); + + // find neighboring grid entries + final int southIndex = getSouthIndex(latitude); + final int westIndex = getWestIndex(normalizedLongitude); + + // build interpolator + return new CellInterpolator(latitude, normalizedLongitude, + entries[southIndex ][westIndex ], + entries[southIndex ][westIndex + 1], + entries[southIndex + 1][westIndex ], + entries[southIndex + 1][westIndex + 1]); + + } + + /** Get interpolator within a cell. + * @param type of the field elements + * @param latitude latitude of point of interest + * @param longitude longitude of point of interest + * @return interpolator for the cell + */ + > FieldCellInterpolator getInterpolator(final T latitude, final T longitude) { + + // keep longitude within grid range + final T normalizedLongitude = + MathUtils.normalizeAngle(longitude, + longitude.newInstance(entries[0][0].getLongitude() + FastMath.PI)); + + // find neighboring grid entries + final int southIndex = getSouthIndex(latitude.getReal()); + final int westIndex = getWestIndex(normalizedLongitude.getReal()); + + // build interpolator + return new FieldCellInterpolator<>(latitude, normalizedLongitude, + entries[southIndex ][westIndex ], + entries[southIndex ][westIndex + 1], + entries[southIndex + 1][westIndex ], + entries[southIndex + 1][westIndex + 1]); + + } + + /** Check if grid contains all specified models. + * @param types models types + * @return true if grid contain the model + */ + boolean hasModels(final SeasonalModelType... types) { + boolean hasAll = true; + for (final SeasonalModelType type : types) { + hasAll &= entries[0][0].getModel(type) != null; + } + return hasAll; + } + +} diff --git a/src/main/java/org/orekit/models/earth/weather/GridEntry.java b/src/main/java/org/orekit/models/earth/weather/GridEntry.java new file mode 100644 index 0000000000..68c2a7d4e6 --- /dev/null +++ b/src/main/java/org/orekit/models/earth/weather/GridEntry.java @@ -0,0 +1,135 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.weather; + +import java.util.Map; + +import org.hipparchus.util.MathUtils; + +/** Grid entry in Global Pressure Temperature models. + * @author Luc Maisonobe + * @since 12.1 + */ +class GridEntry { + + /** Conversion factor from degrees to mill arcseconds. */ + public static final int DEG_TO_MAS = 3600000; + + /** Latitude (radian). */ + private final double latitude; + + /** Latitude key (mas). */ + private final int latKey; + + /** Longitude (radian). */ + private final double longitude; + + /** Longitude key (mas). */ + private final int lonKey; + + /** Undulation. */ + private final double undulation; + + /** Height correction. */ + private final double hS; + + /** Seasonal models. */ + private Map models; + + /** Build an entry from its components. + * @param latitude latitude (radian) + * @param latKey latitude key (mas) + * @param longitude longitude (radian) + * @param lonKey longitude key (mas) + * @param undulation undulation (m) + * @param hS height correction + * @param models seasonal models + */ + GridEntry(final double latitude, final int latKey, final double longitude, final int lonKey, + final double undulation, final double hS, final Map models) { + + this.latitude = latitude; + this.latKey = latKey; + this.longitude = longitude; + this.lonKey = lonKey; + this.undulation = undulation; + this.hS = hS; + this.models = models; + } + + /** Build a new entry 360° to the East of instance. + * @return new wrapping entry (always same type as instance) + */ + public GridEntry buildWrappedEntry() { + return new GridEntry(latitude, latKey, + longitude + MathUtils.TWO_PI, + lonKey + DEG_TO_MAS * 360, + undulation, hS, + models); + } + + /** Get latitude (radian). + * @return latitude (radian) + */ + double getLatitude() { + return latitude; + } + + /** Get latitude key (mas). + * @return latitude key (mas) + */ + int getLatKey() { + return latKey; + } + + /** Get longitude (radian). + * @return longitude (radian) + */ + double getLongitude() { + return longitude; + } + + /** Get longitude key (mas). + * @return longitude key (mas) + */ + int getLonKey() { + return lonKey; + } + + /** Get undulation. + * @return undulation + */ + double getUndulation() { + return undulation; + } + + /** Get height correction. + * @return height correction + */ + double getHs() { + return hS; + } + + /** Get a model. + * @param type model type + * @return model + */ + SeasonalModel getModel(final SeasonalModelType type) { + return models.get(type); + } + +} diff --git a/src/main/java/org/orekit/models/earth/weather/HeightDependentPressureTemperatureHumidityConverter.java b/src/main/java/org/orekit/models/earth/weather/HeightDependentPressureTemperatureHumidityConverter.java new file mode 100644 index 0000000000..8706f33218 --- /dev/null +++ b/src/main/java/org/orekit/models/earth/weather/HeightDependentPressureTemperatureHumidityConverter.java @@ -0,0 +1,88 @@ +/* Copyright 2002-2024 Thales Alenia Space + * Licensed to CS Communication & Systèmes (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.models.earth.weather; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.util.FastMath; +import org.orekit.models.earth.weather.water.WaterVaporPressureProvider; + +/** Converter for weather parameters that change with height. + *

        + * Height variations correspond to equations 5.98, 5.99 and 5.100 from + * Guochang Xu, GPS - Theory, Algorithms and Applications, Springer, 2007 + *

        + * @author Luc Maisonobe + * @since 12.1 + */ +public class HeightDependentPressureTemperatureHumidityConverter { + + /** Water pressure provider for water vapor pressure. */ + private final WaterVaporPressureProvider provider; + + /** Simple constructor. + *

        + * Points outside of altitude range will be silently clipped back to range. + *

        + * @param provider provider for water vapor pressure + */ + public HeightDependentPressureTemperatureHumidityConverter(final WaterVaporPressureProvider provider) { + this.provider = provider; + } + + /** Convert weather parameters. + * @param pth0 weather at reference altitude + * @param h altitude at which weather is requested + * @return converted weather + */ + public PressureTemperatureHumidity convert(final PressureTemperatureHumidity pth0, + final double h) { + + // retrieve parameters at reference altitude + final double rh0 = provider.relativeHumidity(pth0.getPressure(), pth0.getTemperature(), pth0.getWaterVaporPressure()); + + // compute changes due to altitude change + final double dh = h - pth0.getAltitude(); + final double p = pth0.getPressure() * FastMath.pow(1.0 - 2.26e-5 * dh, 5.225); + final double t = pth0.getTemperature() - 6.5e-3 * dh; + final double rh = rh0 * FastMath.exp(-6.396e-4 * dh); + + return new PressureTemperatureHumidity(h, p, t, provider.waterVaporPressure(p, t, rh), + pth0.getTm(), pth0.getLambda()); + + } + + /** Convert weather parameters. + * @param type of the elements + * @param pth0 weather at reference altitude + * @param h altitude at which weather is requested + * @return converted weather + */ + public > FieldPressureTemperatureHumidity convert(final FieldPressureTemperatureHumidity pth0, + final T h) { + // retrieve parameters at reference altitude + final T rh0 = provider.relativeHumidity(pth0.getPressure(), pth0.getTemperature(), pth0.getWaterVaporPressure()); + + // compute changes due to altitude change + final T dh = h.subtract(pth0.getAltitude()); + final T t = pth0.getTemperature().subtract(dh.multiply(6.5e-3)); + final T p = pth0.getPressure().multiply(dh.multiply(2.26e-5).negate().add(1.0).pow(5.225)); + final T rh = rh0.multiply(FastMath.exp(dh.multiply(-6.396e-4))); + return new FieldPressureTemperatureHumidity<>(h, p, t, provider.waterVaporPressure(p, t, rh), + pth0.getTm(), pth0.getLambda()); + } + +} diff --git a/src/main/java/org/orekit/models/earth/weather/PressureTemperature.java b/src/main/java/org/orekit/models/earth/weather/PressureTemperature.java new file mode 100644 index 0000000000..0adc7a3078 --- /dev/null +++ b/src/main/java/org/orekit/models/earth/weather/PressureTemperature.java @@ -0,0 +1,67 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.weather; + +/** Container for pressure and temperature. + * @author Luc Maisonobe + * @since 12.1 + */ +public class PressureTemperature { + + /** Altitude at which weather parameters have been computed. */ + private final double altitude; + + /** Pressure (Pa). */ + private final double pressure; + + /** Temperature (Kelvin). */ + private final double temperature; + + /** Simple constructor. + * @param altitude altitude at which weather parameters have been computed (m) + * @param pressure pressure (Pa) + * @param temperature temperature (Kelvin) + */ + public PressureTemperature(final double altitude, + final double pressure, final double temperature) { + this.altitude = altitude; + this.pressure = pressure; + this.temperature = temperature; + } + + /** Get altitude at which weather parameters have been computed. + * @return altitude at which weather parameters have been computed (m) + */ + public double getAltitude() { + return altitude; + } + + /** Get pressure. + * @return pressure (Pa) + */ + public double getPressure() { + return pressure; + } + + /** Get temperature. + * @return temperature (Kelvin) + */ + public double getTemperature() { + return temperature; + } + +} diff --git a/src/main/java/org/orekit/models/earth/weather/PressureTemperatureHumidity.java b/src/main/java/org/orekit/models/earth/weather/PressureTemperatureHumidity.java new file mode 100644 index 0000000000..8a580407da --- /dev/null +++ b/src/main/java/org/orekit/models/earth/weather/PressureTemperatureHumidity.java @@ -0,0 +1,75 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.weather; + +/** Container for pressure, temperature, and humidity. + * @author Luc Maisonobe + * @since 12.1 + */ +public class PressureTemperatureHumidity extends PressureTemperature { + + /** Humidity as water vapor pressure (Pa). */ + private final double waterVaporPressure; + + /** Mean temperature weighted with water vapor pressure. */ + private final double tm; + + /** Water vapor decrease factor. */ + private final double lambda; + + /** Simple constructor. + * @param altitude altitude at which weather parameters have been computed (m) + * @param pressure pressure (Pa) + * @param temperature temperature (Kelvin) + * @param waterVaporPressure humidity as water vapor pressure (Pa) + * @param tm mean temperature weighted with water vapor pressure + * @param lambda water vapor decrease factor + */ + public PressureTemperatureHumidity(final double altitude, + final double pressure, + final double temperature, + final double waterVaporPressure, + final double tm, + final double lambda) { + super(altitude, pressure, temperature); + this.waterVaporPressure = waterVaporPressure; + this.tm = tm; + this.lambda = lambda; + } + + /** Get humidity as water vapor pressure. + * @return humidity as water vapor pressure (Pa) + */ + public double getWaterVaporPressure() { + return waterVaporPressure; + } + + /** Get mean temperature weighted with water vapor pressure. + * @return mean temperature weighted with water vapor pressure + */ + public double getTm() { + return tm; + } + + /** Get water vapor decrease factor. + * @return water vapor decrease factor + */ + public double getLambda() { + return lambda; + } + +} diff --git a/src/main/java/org/orekit/models/earth/weather/PressureTemperatureHumidityProvider.java b/src/main/java/org/orekit/models/earth/weather/PressureTemperatureHumidityProvider.java new file mode 100644 index 0000000000..699af32088 --- /dev/null +++ b/src/main/java/org/orekit/models/earth/weather/PressureTemperatureHumidityProvider.java @@ -0,0 +1,47 @@ +/* Copyright 2002-2024 Thales Alenia Space + * Licensed to CS Communication & Systèmes (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.models.earth.weather; + +import org.hipparchus.CalculusFieldElement; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; + +/** Interface for providing weather parameters. + * @author Luc Maisonobe + * @since 12.1 + */ +public interface PressureTemperatureHumidityProvider { + + /** Provide weather parameters. + * @param location location at which parameters are requested + * @param date date at which parameters are requested + * @return weather parameters + */ + PressureTemperatureHumidity getWeatherParamerers(GeodeticPoint location, AbsoluteDate date); + + /** Provide weather parameters. + * @param type of the field elements + * @param location location at which parameters are requested + * @param date date at which parameters are requested + * @return weather parameters + */ + > FieldPressureTemperatureHumidity getWeatherParamerers(FieldGeodeticPoint location, + FieldAbsoluteDate date); + +} diff --git a/src/main/java/org/orekit/models/earth/weather/SeasonalModel.java b/src/main/java/org/orekit/models/earth/weather/SeasonalModel.java new file mode 100644 index 0000000000..e0a81d005d --- /dev/null +++ b/src/main/java/org/orekit/models/earth/weather/SeasonalModel.java @@ -0,0 +1,75 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.weather; + +import org.hipparchus.util.FastMath; +import org.hipparchus.util.SinCos; + +/** Seasonal model used in Global Pressure Temperature models. + * @see "Landskron, D. & Böhm, J. J Geod (2018) + * VMF3/GPT3: refined discrete and empirical troposphere mapping functions + * 92: 349. https://doi.org/10.1007/s00190-017-1066-2" + * @author Luc Maisonobe + * @since 12.1 + */ +class SeasonalModel { + + /** Constant. */ + private final double a0; + + /** Annual cosine amplitude. */ + private final double a1; + + /** Annual sine amplitude. */ + private final double b1; + + /** Semi-annual cosine amplitude. */ + private final double a2; + + /** Semi-annual sine amplitude. */ + private final double b2; + + /** Simple constructor. + * @param a0 constant + * @param a1 annual cosine amplitude + * @param b1 annual sine amplitude + * @param a2 semi-annual cosine amplitude + * @param b2 semi-annual sine amplitude + */ + SeasonalModel(final double a0, final double a1, final double b1, final double a2, final double b2) { + this.a0 = a0; + this.a1 = a1; + this.b1 = b1; + this.a2 = a2; + this.b2 = b2; + } + + /** Evaluate a model for some day. + * @param dayOfYear day to evaluate + * @return model value at specified day + */ + public double evaluate(final int dayOfYear) { + + final double coef = (dayOfYear / 365.25) * 2 * FastMath.PI; + final SinCos sc1 = FastMath.sinCos(coef); + final SinCos sc2 = FastMath.sinCos(2.0 * coef); + + return a0 + a1 * sc1.cos() + b1 * sc1.sin() + a2 * sc2.cos() + b2 * sc2.sin(); + + } + +} diff --git a/src/main/java/org/orekit/models/earth/weather/SeasonalModelType.java b/src/main/java/org/orekit/models/earth/weather/SeasonalModelType.java new file mode 100644 index 0000000000..ca2df5c2eb --- /dev/null +++ b/src/main/java/org/orekit/models/earth/weather/SeasonalModelType.java @@ -0,0 +1,101 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.weather; + +import java.util.HashMap; +import java.util.Map; + +/** Type of seasonal model used in Global Pressure Temperature models. + * @see "Landskron, D. & Böhm, J. J Geod (2018) + * VMF3/GPT3: refined discrete and empirical troposphere mapping functions + * 92: 349. https://doi.org/10.1007/s00190-017-1066-2" + * @author Luc Maisonobe + * @since 12.1 + */ +enum SeasonalModelType { + + /** Pressure model. */ + PRESSURE("p"), + + /** Temperature model. */ + TEMPERATURE("T"), + + /** Specific humidity model. */ + QV("Q"), + + /** Temperature gradient model. */ + DT("dT"), + + /** ah coefficient model. */ + AH("h", "a_h"), + + /** aw coefficient model. */ + AW("w", "a_w"), + + /** Water vapor decrease factor model. */ + LAMBDA("lambda"), + + /** Mean temperature weighted with water vapor pressure model. */ + TM("Tm"), + + /** Hydrostatic North gradient coefficient model. */ + GN_H("Gn_h"), + + /** Hydrostatic East gradient coefficient model. */ + GE_H("Ge_h"), + + /** Wet North gradient coefficient model. */ + GN_W("Gn_w"), + + /** Wet East gradient coefficient model. */ + GE_W("Ge_w"); + + /** Global suffix that applies to all labels. */ + private static final String SUFFIX = ":a0"; + + /** Parsing map. */ + private static final Map LABELS_MAP = new HashMap<>(); + static { + for (final SeasonalModelType type : values()) { + for (final String label : type.labels) { + LABELS_MAP.put(label + SUFFIX, type); + } + } + } + + /** Labels in grid files headers. */ + private final String[] labels; + + /** Simple constructor. + * @param labels labels in grid files headers + */ + SeasonalModelType(final String... labels) { + this.labels = labels.clone(); + } + + /** Parse a field to get the type. + * @param field field to parse + * @param lineNumber line number + * @param name file name + * @return the type corresponding to the field + * @exception IllegalArgumentException if the field does not correspond to a type + */ + public static SeasonalModelType parseType(final String field, final int lineNumber, final String name) { + return LABELS_MAP.get(field); + } + +} diff --git a/src/main/java/org/orekit/models/earth/weather/WeatherModel.java b/src/main/java/org/orekit/models/earth/weather/WeatherModel.java index 4e89316c50..1d259887da 100644 --- a/src/main/java/org/orekit/models/earth/weather/WeatherModel.java +++ b/src/main/java/org/orekit/models/earth/weather/WeatherModel.java @@ -22,7 +22,9 @@ * compute the different weather parameters (pressure, temperature, ...). * @author Bryan Cazabonne * @since 9.3 + * @deprecated as of 12.1, replaced by {@link PressureTemperatureHumidityProvider} */ +@Deprecated public interface WeatherModel { /** Calculates the weather parameters of the model. diff --git a/src/main/java/org/orekit/models/earth/weather/water/CIPM2007.java b/src/main/java/org/orekit/models/earth/weather/water/CIPM2007.java new file mode 100644 index 0000000000..a75bb1d9eb --- /dev/null +++ b/src/main/java/org/orekit/models/earth/weather/water/CIPM2007.java @@ -0,0 +1,89 @@ +/* Copyright 2002-2024 Thales Alenia Space + * Licensed to CS Communication & Systèmes (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.models.earth.weather.water; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.util.FastMath; +import org.orekit.models.earth.troposphere.TroposphericModelUtils; + +/** Official model CIPM-2007 (identical to CIPM-1981/91) from Comité International des Poids et Mesures. + *

        + * This water vapor model is the one from Giacomo and Davis as indicated in IERS TN 32, chap. 9. + *

        + * @see Revised + * formula for the density of moist air (CIPM-2007), Metrologia 45 (2008) 149–155 + * + * @author Luc Maisonobe + * @since 12.1 + */ +public class CIPM2007 implements WaterVaporPressureProvider { + + /** Laurent series coefficient for degree +2. */ + private static final double L_P2 = 1.2378847e-5; + + /** Laurent series coefficient for degree +1. */ + private static final double L_P1 = -1.9121316e-2; + + /** Laurent series coefficient for degree 0. */ + private static final double L_0 = 33.93711047; + + /** Laurent series coefficient for degree -1. */ + private static final double L_M1 = -6343.1645; + + /** Celsius temperature offset. */ + private static final double CELSIUS = 273.15; + + /** Constant enhancement factor. */ + private static final double F_0 = 1.00062; + + /** Pressure enhancement factor. */ + private static final double F_P = 3.14e-6; + + /** Temperature enhancement factor. */ + private static final double F_T2 = 5.6e-7; + + /** {@inheritDoc} */ + @Override + public double waterVaporPressure(final double p, final double t, final double rh) { + + // saturation water vapor, equation A1.1 (now in Pa, not hPa) + final double psv = FastMath.exp(t * (t * L_P2 + L_P1) + L_0 + L_M1 / t); + + // enhancement factor, equation A1.2 + final double tC = t - CELSIUS; + final double fw = TroposphericModelUtils.HECTO_PASCAL.fromSI(p) * F_P + tC * tC * F_T2 + F_0; + + return rh * fw * psv; + + } + + /** {@inheritDoc} */ + @Override + public > T waterVaporPressure(final T p, final T t, final T rh) { + + // saturation water vapor, equation A1.1 (now in Pa, not hPa) + final T psv = FastMath.exp(t.multiply(t.multiply(L_P2).add(L_P1)).add(L_0).add(t.reciprocal().multiply(L_M1))); + + // enhancement factor, equation A1.2 + final T tC = t.subtract(CELSIUS); + final T fw = TroposphericModelUtils.HECTO_PASCAL.fromSI(p).multiply(F_P).add(tC.multiply(tC).multiply(F_T2)).add(F_0); + + return rh.multiply(fw).multiply(psv); + + } + +} diff --git a/src/main/java/org/orekit/models/earth/weather/water/NbsNrcSteamTable.java b/src/main/java/org/orekit/models/earth/weather/water/NbsNrcSteamTable.java new file mode 100644 index 0000000000..cc144f1f9a --- /dev/null +++ b/src/main/java/org/orekit/models/earth/weather/water/NbsNrcSteamTable.java @@ -0,0 +1,84 @@ +/* Copyright 2002-2024 Thales Alenia Space + * Licensed to CS Communication & Systèmes (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.models.earth.weather.water; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.analysis.interpolation.SplineInterpolator; +import org.hipparchus.analysis.polynomials.PolynomialSplineFunction; +import org.hipparchus.util.FastMath; + +/** Steam table from US National Bureau of Standards (NBS) and National Research Council (NRC) of Canada. + *

        + * The table is an extract from table 1 in Thermopedia, + * using only the pressure column and truncated to 99°C (the original table goes up to 373.976°C). According to + * the access page, this data is available for free. + *

        + * @see Thermopedia Steam Tables + * + * @author Luc Maisonobe + * @since 12.1 + */ +public class NbsNrcSteamTable implements WaterVaporPressureProvider { + + /** Celsius temperature offset. */ + private static final double CELSIUS = 273.15; + + /** Minimum temperature of the model, at the triple point of water, i.e. 273.16K (which is 0.01°C). */ + private static final double MIN_T = CELSIUS + 0.01; + + /** Saturation pressure model. */ + private static final PolynomialSplineFunction MODEL; + + static { + + // saturation pressure in SI units (Pa) + final double[] pressure = { + 00611.73, 657.16, 706.05, 758.13, 813.59, 872.60, 935.37, 1002.09, 1072.97, 1148.25, + 01228.10, 1312.90, 1402.70, 1497.90, 1598.80, 1705.60, 1818.50, 1938.00, 2064.40, 2197.90, + 02338.80, 2487.70, 2644.70, 2810.40, 2985.00, 3169.10, 3362.90, 3567.00, 3781.80, 4007.80, + 04245.50, 4495.30, 4757.80, 5033.50, 5322.90, 5626.70, 5945.40, 6279.50, 6629.80, 6996.90, + 07381.40, 7784.00, 8205.40, 8646.40, 9107.60, 9589.80, 10093.80, 10620.50, 11170.60, 11744.90, + 12344.00, 12970.00, 13623.00, 14303.00, 15012.00, 15752.00, 16522.00, 17324.00, 18159.00, 19028.00, + 19932.00, 20873.00, 21851.00, 22868.00, 23925.00, 25022.00, 26163.00, 27347.00, 28576.00, 29852.00, + 31176.00, 32549.00, 33972.00, 35448.00, 36978.00, 38563.00, 40205.00, 41905.00, 43665.00, 45487.00, + 47373.00, 49324.00, 51342.00, 53428.00, 55585.00, 57815.00, 60119.00, 62499.00, 64958.00, 67496.00, + 70117.00, 72823.00, 75614.00, 78495.00, 81465.00, 84529.00, 87688.00, 90945.00, 94301.00, 97759.00 + }; + + // the table first entry is at 0.01°C, not 0.00°C, but remaining entries are 1°C, 2°C, … 99°C + final double[] temperature = new double[pressure.length]; + for (int i = 0; i < temperature.length; ++i) { + temperature[i] = (i == 0) ? MIN_T : (CELSIUS + i); + } + + MODEL = new SplineInterpolator().interpolate(temperature, pressure); + + } + + /** {@inheritDoc} */ + @Override + public double waterVaporPressure(final double p, final double t, final double rh) { + return MODEL.value(FastMath.max(t, MIN_T)) * rh; + } + + /** {@inheritDoc} */ + @Override + public > T waterVaporPressure(final T p, final T t, final T rh) { + return MODEL.value(FastMath.max(t, MIN_T)).multiply(rh); + } + +} diff --git a/src/main/java/org/orekit/models/earth/weather/water/Wang1988.java b/src/main/java/org/orekit/models/earth/weather/water/Wang1988.java new file mode 100644 index 0000000000..b3c9391f89 --- /dev/null +++ b/src/main/java/org/orekit/models/earth/weather/water/Wang1988.java @@ -0,0 +1,53 @@ +/* Copyright 2002-2024 Thales Alenia Space + * Licensed to CS Communication & Systèmes (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.models.earth.weather.water; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.analysis.polynomials.PolynomialFunction; +import org.hipparchus.util.FastMath; +import org.orekit.models.earth.troposphere.TroposphericModelUtils; + +/** Conversion polynomial from "The Principle of the GPS Precise Positioning System", Wang et al, 1988. + *

        + * This corresponds to equation 5.96 in Guochang Xu, GPS - Theory, Algorithms and Applications, Springer, 2007. + *

        + * @author Luc Maisonobe + * @since 12.1 + */ +public class Wang1988 implements WaterVaporPressureProvider { + + /** Coefficients for the partial pressure of water vapor polynomial. */ + private static final double[] E_COEFFICIENTS = { + -37.2465, 0.213166, -0.000256908 + }; + + /** Conversion polynomial. */ + private static final PolynomialFunction E_POLYNOMIAL = new PolynomialFunction(E_COEFFICIENTS); + + /** {@inheritDoc} */ + @Override + public double waterVaporPressure(final double p, final double t, final double rh) { + return TroposphericModelUtils.HECTO_PASCAL.toSI(rh * FastMath.exp(E_POLYNOMIAL.value(t))); + } + + /** {@inheritDoc} */ + @Override + public > T waterVaporPressure(final T p, final T t, final T rh) { + return TroposphericModelUtils.HECTO_PASCAL.toSI(rh.multiply(FastMath.exp(E_POLYNOMIAL.value(t)))); + } + +} diff --git a/src/main/java/org/orekit/models/earth/weather/water/WaterVaporPressureProvider.java b/src/main/java/org/orekit/models/earth/weather/water/WaterVaporPressureProvider.java new file mode 100644 index 0000000000..18b68f88d1 --- /dev/null +++ b/src/main/java/org/orekit/models/earth/weather/water/WaterVaporPressureProvider.java @@ -0,0 +1,67 @@ +/* Copyright 2002-2024 Thales Alenia Space + * Licensed to CS Communication & Systèmes (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.models.earth.weather.water; + +import org.hipparchus.CalculusFieldElement; + +/** Interface for converting between relative humidity and water vapor pressure. + * @author Luc Maisonobe + * @since 12.1 + */ +public interface WaterVaporPressureProvider { + + /** Compute water vapor pressure. + * @param p pressure (Pa) + * @param t temperature (Kelvin) + * @param rh relative humidity, as a ratio (50% → 0.5) + * @return water vapor pressure (Pa) + */ + double waterVaporPressure(double p, double t, double rh); + + /** Compute relative humidity. + * @param p pressure (Pa) + * @param t temperature (Kelvin) + * @param e water vapor pressure (Pa) + * @return relative humidity, as a ratio (50% → 0.5) + */ + default double relativeHumidity(final double p, final double t, final double e) { + final double saturationPressure = waterVaporPressure(p, t, 1.0); + return e / saturationPressure; + } + + /** Compute water vapor pressure. + * @param type of the field elements + * @param p pressure (Pa) + * @param t temperature (Kelvin) + * @param rh relative humidity, as a ratio (50% → 0.5) + * @return water vapor pressure (Pa) + */ + > T waterVaporPressure(T p, T t, T rh); + + /** Compute relative humidity. + * @param type of the field elements + * @param p pressure (Pa) + * @param t temperature (Kelvin) + * @param e water vapor pressure (Pa) + * @return relative humidity, as a ratio (50% → 0.5) + */ + default > T relativeHumidity(final T p, T t, T e) { + final T saturationPressure = waterVaporPressure(p, t, p.getField().getOne()); + return e.divide(saturationPressure); + } + +} diff --git a/src/main/java/org/orekit/models/earth/weather/water/package-info.java b/src/main/java/org/orekit/models/earth/weather/water/package-info.java new file mode 100644 index 0000000000..535cedcdb2 --- /dev/null +++ b/src/main/java/org/orekit/models/earth/weather/water/package-info.java @@ -0,0 +1,20 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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 models that convert between relative humidity and partial water pressure. + * @author Luc Maisonobe + */ +package org.orekit.models.earth.weather.water; diff --git a/src/main/java/org/orekit/orbits/CartesianOrbit.java b/src/main/java/org/orekit/orbits/CartesianOrbit.java index 9967013e2a..0fc67b6319 100644 --- a/src/main/java/org/orekit/orbits/CartesianOrbit.java +++ b/src/main/java/org/orekit/orbits/CartesianOrbit.java @@ -20,12 +20,9 @@ import org.hipparchus.analysis.differentiation.UnivariateDerivative2; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; -import org.hipparchus.geometry.euclidean.threed.Rotation; -import org.hipparchus.geometry.euclidean.threed.RotationConvention; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.linear.MatrixUtils; import org.hipparchus.util.FastMath; -import org.hipparchus.util.SinCos; import org.orekit.annotation.DefaultDataContext; import org.orekit.data.DataContext; import org.orekit.frames.Frame; @@ -379,6 +376,7 @@ public double getLMDot() { } /** {@inheritDoc} */ + @Override public boolean hasDerivatives() { return hasNonKeplerianAcceleration; } @@ -397,140 +395,31 @@ protected TimeStampedPVCoordinates initPVCoordinates() { /** {@inheritDoc} */ public CartesianOrbit shiftedBy(final double dt) { - final PVCoordinates shiftedPV = isElliptical() ? shiftPVElliptic(dt) : shiftPVHyperbolic(dt); + final PVCoordinates shiftedPV = shiftPV(dt); return new CartesianOrbit(shiftedPV, getFrame(), getDate().shiftedBy(dt), getMu()); } - /** Compute shifted position and velocity in elliptic case. + /** Compute shifted position and velocity. * @param dt time shift * @return shifted position and velocity */ - private PVCoordinates shiftPVElliptic(final double dt) { - - // preliminary computation - final PVCoordinates pv = getPVCoordinates(); - final Vector3D pvP = pv.getPosition(); - final Vector3D pvV = pv.getVelocity(); - final Vector3D pvM = pv.getMomentum(); - final double r2 = pvP.getNormSq(); - final double r = FastMath.sqrt(r2); - final double rV2OnMu = r * pvV.getNormSq() / getMu(); - final double a = r / (2 - rV2OnMu); - final double muA = getMu() * a; - - // compute mean anomaly - final double eSE = Vector3D.dotProduct(pvP, pvV) / FastMath.sqrt(muA); - final double eCE = rV2OnMu - 1; - final double E0 = FastMath.atan2(eSE, eCE); - final double M0 = E0 - eSE; - - final double e = FastMath.sqrt(eCE * eCE + eSE * eSE); - final double sqrt = FastMath.sqrt((1 + e) / (1 - e)); - - // find canonical 2D frame with p pointing to perigee - final double v0 = 2 * FastMath.atan(sqrt * FastMath.tan(E0 / 2)); - final Vector3D p = new Rotation(pvM, v0, RotationConvention.FRAME_TRANSFORM).applyTo(pvP).normalize(); - final Vector3D q = Vector3D.crossProduct(pvM, p).normalize(); - - // compute shifted eccentric anomaly - final double M1 = M0 + getKeplerianMeanMotion() * dt; - final double E1 = KeplerianAnomalyUtility.ellipticMeanToEccentric(e, M1); - - // compute shifted in-plane Cartesian coordinates - final SinCos scE = FastMath.sinCos(E1); - final double cE = scE.cos(); - final double sE = scE.sin(); - final double sE2m1 = FastMath.sqrt((1 - e) * (1 + e)); - - // coordinates of position and velocity in the orbital plane - final double x = a * (cE - e); - final double y = a * sE2m1 * sE; - final double factor = FastMath.sqrt(getMu() / a) / (1 - e * cE); - final double xDot = -factor * sE; - final double yDot = factor * sE2m1 * cE; - - final Vector3D shiftedP = new Vector3D(x, p, y, q); - final Vector3D shiftedV = new Vector3D(xDot, p, yDot, q); - if (hasNonKeplerianAcceleration) { + private PVCoordinates shiftPV(final double dt) { - // extract non-Keplerian part of the initial acceleration - final Vector3D nonKeplerianAcceleration = new Vector3D(1, getPVCoordinates().getAcceleration(), - getMu() / (r2 * r), pvP); + final Vector3D pvP = getPosition(); + final PVCoordinates shiftedPV = KeplerianMotionCartesianUtility.predictPositionVelocity(dt, pvP, + getPVCoordinates().getVelocity(), getMu()); - // add the quadratic motion due to the non-Keplerian acceleration to the Keplerian motion - final Vector3D fixedP = new Vector3D(1, shiftedP, - 0.5 * dt * dt, nonKeplerianAcceleration); - final double fixedR2 = fixedP.getNormSq(); - final double fixedR = FastMath.sqrt(fixedR2); - final Vector3D fixedV = new Vector3D(1, shiftedV, - dt, nonKeplerianAcceleration); - final Vector3D fixedA = new Vector3D(-getMu() / (fixedR2 * fixedR), shiftedP, - 1, nonKeplerianAcceleration); - - return new PVCoordinates(fixedP, fixedV, fixedA); - - } else { - // don't include acceleration, - // so the shifted orbit is not considered to have derivatives - return new PVCoordinates(shiftedP, shiftedV); - } - - } - - /** Compute shifted position and velocity in hyperbolic case. - * @param dt time shift - * @return shifted position and velocity - */ - private PVCoordinates shiftPVHyperbolic(final double dt) { - - final PVCoordinates pv = getPVCoordinates(); - final Vector3D pvP = pv.getPosition(); - final Vector3D pvV = pv.getVelocity(); - final Vector3D pvM = pv.getMomentum(); - final double r2 = pvP.getNormSq(); - final double r = FastMath.sqrt(r2); - final double rV2OnMu = r * pvV.getNormSq() / getMu(); - final double a = getA(); - final double muA = getMu() * a; - final double e = FastMath.sqrt(1 - Vector3D.dotProduct(pvM, pvM) / muA); - final double sqrt = FastMath.sqrt((e + 1) / (e - 1)); - - // compute mean anomaly - final double eSH = Vector3D.dotProduct(pvP, pvV) / FastMath.sqrt(-muA); - final double eCH = rV2OnMu - 1; - final double H0 = FastMath.log((eCH + eSH) / (eCH - eSH)) / 2; - final double M0 = e * FastMath.sinh(H0) - H0; - - // find canonical 2D frame with p pointing to perigee - final double v0 = 2 * FastMath.atan(sqrt * FastMath.tanh(H0 / 2)); - final Vector3D p = new Rotation(pvM, v0, RotationConvention.FRAME_TRANSFORM).applyTo(pvP).normalize(); - final Vector3D q = Vector3D.crossProduct(pvM, p).normalize(); - - // compute shifted eccentric anomaly - final double M1 = M0 + getKeplerianMeanMotion() * dt; - final double H1 = KeplerianAnomalyUtility.hyperbolicMeanToEccentric(e, M1); - - // compute shifted in-plane Cartesian coordinates - final double cH = FastMath.cosh(H1); - final double sH = FastMath.sinh(H1); - final double sE2m1 = FastMath.sqrt((e - 1) * (e + 1)); - - // coordinates of position and velocity in the orbital plane - final double x = a * (cH - e); - final double y = -a * sE2m1 * sH; - final double factor = FastMath.sqrt(getMu() / -a) / (e * cH - 1); - final double xDot = -factor * sH; - final double yDot = factor * sE2m1 * cH; - - final Vector3D shiftedP = new Vector3D(x, p, y, q); - final Vector3D shiftedV = new Vector3D(xDot, p, yDot, q); if (hasNonKeplerianAcceleration) { // extract non-Keplerian part of the initial acceleration + final double r2 = pvP.getNormSq(); + final double r = FastMath.sqrt(r2); final Vector3D nonKeplerianAcceleration = new Vector3D(1, getPVCoordinates().getAcceleration(), getMu() / (r2 * r), pvP); // add the quadratic motion due to the non-Keplerian acceleration to the Keplerian motion + final Vector3D shiftedP = shiftedPV.getPosition(); + final Vector3D shiftedV = shiftedPV.getVelocity(); final Vector3D fixedP = new Vector3D(1, shiftedP, 0.5 * dt * dt, nonKeplerianAcceleration); final double fixedR2 = fixedP.getNormSq(); @@ -545,7 +434,7 @@ private PVCoordinates shiftPVHyperbolic(final double dt) { } else { // don't include acceleration, // so the shifted orbit is not considered to have derivatives - return new PVCoordinates(shiftedP, shiftedV); + return shiftedPV; } } diff --git a/src/main/java/org/orekit/orbits/CircularLatitudeArgumentUtility.java b/src/main/java/org/orekit/orbits/CircularLatitudeArgumentUtility.java new file mode 100644 index 0000000000..5733e2ba5a --- /dev/null +++ b/src/main/java/org/orekit/orbits/CircularLatitudeArgumentUtility.java @@ -0,0 +1,175 @@ +/* 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.orbits; + +import org.hipparchus.util.FastMath; +import org.hipparchus.util.SinCos; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; + +/** + * Utility methods for converting between different latitude arguments used by {@link org.orekit.orbits.CircularOrbit}. + * @author Romain Serra + * @see org.orekit.orbits.CircularOrbit + * @since 12.1 + */ +public class CircularLatitudeArgumentUtility { + + /** Tolerance for stopping criterion in iterative conversion from mean to eccentric angle. */ + private static final double TOLERANCE_CONVERGENCE = 1.0e-12; + + /** Maximum number of iterations in iterative conversion from mean to eccentric angle. */ + private static final int MAXIMUM_ITERATION = 50; + + /** Private constructor for utility class. */ + private CircularLatitudeArgumentUtility() { + // nothing here (utils class) + } + + /** + * Computes the true latitude argument from the eccentric latitude argument. + * + * @param ex e cos(ω), first component of circular eccentricity vector + * @param ey e sin(ω), second component of circular eccentricity vector + * @param alphaE = E + ω eccentric latitude argument (rad) + * @return the true latitude argument. + */ + public static double eccentricToTrue(final double ex, final double ey, final double alphaE) { + final double epsilon = eccentricAndTrueEpsilon(ex, ey); + final SinCos scAlphaE = FastMath.sinCos(alphaE); + final double num = ex * scAlphaE.sin() - ey * scAlphaE.cos(); + final double den = epsilon + 1 - ex * scAlphaE.cos() - ey * scAlphaE.sin(); + return alphaE + eccentricAndTrueAtan(num, den); + } + + /** + * Computes the eccentric latitude argument from the true latitude argument. + * + * @param ex e cos(ω), first component of circular eccentricity vector + * @param ey e sin(ω), second component of circular eccentricity vector + * @param alphaV = V + ω true latitude argument (rad) + * @return the eccentric latitude argument. + */ + public static double trueToEccentric(final double ex, final double ey, final double alphaV) { + final double epsilon = eccentricAndTrueEpsilon(ex, ey); + final SinCos scAlphaV = FastMath.sinCos(alphaV); + final double num = ey * scAlphaV.cos() - ex * scAlphaV.sin(); + final double den = epsilon + 1 + ex * scAlphaV.cos() + ey * scAlphaV.sin(); + return alphaV + eccentricAndTrueAtan(num, den); + } + + /** + * Computes an intermediate quantity for conversions between true and eccentric. + * + * @param ex e cos(ω), first component of circular eccentricity vector + * @param ey e sin(ω), second component of circular eccentricity vector + * @return intermediate variable referred to as epsilon. + */ + private static double eccentricAndTrueEpsilon(final double ex, final double ey) { + return FastMath.sqrt(1 - ex * ex - ey * ey); + } + + /** + * Computes another intermediate quantity for conversions between true and eccentric. + * + * @param num numerator for angular conversion + * @param den denominator for angular conversion + * @return arc-tangent of ratio of inputs times two. + */ + private static double eccentricAndTrueAtan(final double num, final double den) { + return 2. * FastMath.atan(num / den); + } + + /** + * Computes the eccentric latitude argument from the mean latitude argument. + * + * @param ex e cos(ω), first component of circular eccentricity vector + * @param ey e sin(ω), second component of circular eccentricity vector + * @param alphaM = M + ω mean latitude argument (rad) + * @return the eccentric latitude argument. + */ + public static double meanToEccentric(final double ex, final double ey, final double alphaM) { + // Generalization of Kepler equation to circular parameters + // with alphaE = PA + E and + // alphaM = PA + M = alphaE - ex.sin(alphaE) + ey.cos(alphaE) + double alphaE = alphaM; + double shift; + double alphaEMalphaM = 0.0; + boolean hasConverged; + int iter = 0; + do { + final SinCos scAlphaE = FastMath.sinCos(alphaE); + final double f2 = ex * scAlphaE.sin() - ey * scAlphaE.cos(); + final double f1 = 1.0 - ex * scAlphaE.cos() - ey * scAlphaE.sin(); + final double f0 = alphaEMalphaM - f2; + + final double f12 = 2.0 * f1; + shift = f0 * f12 / (f1 * f12 - f0 * f2); + + alphaEMalphaM -= shift; + alphaE = alphaM + alphaEMalphaM; + + hasConverged = FastMath.abs(shift) <= TOLERANCE_CONVERGENCE; + } while (++iter < MAXIMUM_ITERATION && !hasConverged); + + if (!hasConverged) { + throw new OrekitException(OrekitMessages.UNABLE_TO_COMPUTE_ECCENTRIC_LATITUDE_ARGUMENT, iter); + } + return alphaE; + + } + + /** + * Computes the mean latitude argument from the eccentric latitude argument. + * + * @param ex e cos(ω), first component of circular eccentricity vector + * @param ey e sin(ω), second component of circular eccentricity vector + * @param alphaE = E + ω mean latitude argument (rad) + * @return the mean latitude argument. + */ + public static double eccentricToMean(final double ex, final double ey, final double alphaE) { + final SinCos scAlphaE = FastMath.sinCos(alphaE); + return alphaE + (ey * scAlphaE.cos() - ex * scAlphaE.sin()); + } + + /** + * Computes the mean latitude argument from the eccentric latitude argument. + * + * @param ex e cos(ω), first component of circular eccentricity vector + * @param ey e sin(ω), second component of circular eccentricity vector + * @param alphaV = V + ω true latitude argument (rad) + * @return the mean latitude argument. + */ + public static double trueToMean(final double ex, final double ey, final double alphaV) { + final double alphaE = trueToEccentric(ex, ey, alphaV); + return eccentricToMean(ex, ey, alphaE); + } + + /** + * Computes the true latitude argument from the eccentric latitude argument. + * + * @param ex e cos(ω), first component of circular eccentricity vector + * @param ey e sin(ω), second component of circular eccentricity vector + * @param alphaM = M + ω mean latitude argument (rad) + * @return the true latitude argument. + */ + public static double meanToTrue(final double ex, final double ey, final double alphaM) { + final double alphaE = meanToEccentric(ex, ey, alphaM); + return eccentricToTrue(ex, ey, alphaE); + } + +} diff --git a/src/main/java/org/orekit/orbits/CircularOrbit.java b/src/main/java/org/orekit/orbits/CircularOrbit.java index 0c3a5ded47..2405d81bf2 100644 --- a/src/main/java/org/orekit/orbits/CircularOrbit.java +++ b/src/main/java/org/orekit/orbits/CircularOrbit.java @@ -75,7 +75,7 @@ public class CircularOrbit extends Orbit implements PositionAngleBased { /** Serializable UID. */ - private static final long serialVersionUID = 20170414L; + private static final long serialVersionUID = 20231217L; /** Semi-major axis (m). */ private final double a; @@ -92,8 +92,11 @@ public class CircularOrbit extends Orbit implements PositionAngleBased { /** Right Ascension of Ascending Node (rad). */ private final double raan; - /** True latitude argument (rad). */ - private final double alphaV; + /** Cached latitude argument (rad). */ + private final double cachedAlpha; + + /** Type of cached position angle (latitude argument). */ + private final PositionAngleType cachedPositionAngleType; /** Semi-major axis derivative (m/s). */ private final double aDot; @@ -111,7 +114,7 @@ public class CircularOrbit extends Orbit implements PositionAngleBased { private final double raanDot; /** True latitude argument derivative (rad/s). */ - private final double alphaVDot; + private final double cachedAlphaDot; /** Indicator for {@link PVCoordinates} serialization. */ private final boolean serializePV; @@ -127,21 +130,46 @@ public class CircularOrbit extends Orbit implements PositionAngleBased { * @param raan right ascension of ascending node (Ω, rad) * @param alpha an + ω, mean, eccentric or true latitude argument (rad) * @param type type of latitude argument + * @param cachedPositionAngleType type of cached latitude argument * @param frame the frame in which are defined the parameters * (must be a {@link Frame#isPseudoInertial pseudo-inertial frame}) * @param date date of the orbital parameters * @param mu central attraction coefficient (m³/s²) * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame} + * @since 12.1 */ public CircularOrbit(final double a, final double ex, final double ey, final double i, final double raan, final double alpha, - final PositionAngleType type, + final PositionAngleType type, final PositionAngleType cachedPositionAngleType, final Frame frame, final AbsoluteDate date, final double mu) throws IllegalArgumentException { this(a, ex, ey, i, raan, alpha, Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN, - type, frame, date, mu); + type, cachedPositionAngleType, frame, date, mu); + } + + /** Creates a new instance without derivatives and with cached position angle same as value inputted. + * @param a semi-major axis (m) + * @param ex e cos(ω), first component of circular eccentricity vector + * @param ey e sin(ω), second component of circular eccentricity vector + * @param i inclination (rad) + * @param raan right ascension of ascending node (Ω, rad) + * @param alpha an + ω, mean, eccentric or true latitude argument (rad) + * @param type type of latitude argument + * @param frame the frame in which are defined the parameters + * (must be a {@link Frame#isPseudoInertial pseudo-inertial frame}) + * @param date date of the orbital parameters + * @param mu central attraction coefficient (m³/s²) + * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or + * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame} + */ + public CircularOrbit(final double a, final double ex, final double ey, + final double i, final double raan, final double alpha, + final PositionAngleType type, + final Frame frame, final AbsoluteDate date, final double mu) + throws IllegalArgumentException { + this(a, ex, ey, i, raan, alpha, type, type, frame, date, mu); } /** Creates a new instance. @@ -158,18 +186,20 @@ public CircularOrbit(final double a, final double ex, final double ey, * @param raanDot right ascension of ascending node derivative (rad/s) * @param alphaDot d(an + ω), mean, eccentric or true latitude argument derivative (rad/s) * @param type type of latitude argument + * @param cachedPositionAngleType type of cached latitude argument * @param frame the frame in which are defined the parameters * (must be a {@link Frame#isPseudoInertial pseudo-inertial frame}) * @param date date of the orbital parameters * @param mu central attraction coefficient (m³/s²) * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame} + * @since 12.1 */ public CircularOrbit(final double a, final double ex, final double ey, final double i, final double raan, final double alpha, final double aDot, final double exDot, final double eyDot, final double iDot, final double raanDot, final double alphaDot, - final PositionAngleType type, + final PositionAngleType type, final PositionAngleType cachedPositionAngleType, final Frame frame, final AbsoluteDate date, final double mu) throws IllegalArgumentException { super(frame, date, mu); @@ -187,42 +217,15 @@ public CircularOrbit(final double a, final double ex, final double ey, this.iDot = iDot; this.raan = raan; this.raanDot = raanDot; + this.cachedPositionAngleType = cachedPositionAngleType; if (hasDerivatives()) { - final UnivariateDerivative1 exUD = new UnivariateDerivative1(ex, exDot); - final UnivariateDerivative1 eyUD = new UnivariateDerivative1(ey, eyDot); - final UnivariateDerivative1 alphaUD = new UnivariateDerivative1(alpha, alphaDot); - final UnivariateDerivative1 alphavUD; - switch (type) { - case MEAN : - alphavUD = FieldCircularOrbit.eccentricToTrue(FieldCircularOrbit.meanToEccentric(alphaUD, exUD, eyUD), exUD, eyUD); - break; - case ECCENTRIC : - alphavUD = FieldCircularOrbit.eccentricToTrue(alphaUD, exUD, eyUD); - break; - case TRUE : - alphavUD = alphaUD; - break; - default : - throw new OrekitInternalError(null); - } - this.alphaV = alphavUD.getValue(); - this.alphaVDot = alphavUD.getDerivative(1); + final UnivariateDerivative1 alphaUD = initializeCachedAlpha(alpha, alphaDot, type); + this.cachedAlpha = alphaUD.getValue(); + this.cachedAlphaDot = alphaUD.getFirstDerivative(); } else { - switch (type) { - case MEAN : - this.alphaV = eccentricToTrue(meanToEccentric(alpha, ex, ey), ex, ey); - break; - case ECCENTRIC : - this.alphaV = eccentricToTrue(alpha, ex, ey); - break; - case TRUE : - this.alphaV = alpha; - break; - default : - throw new OrekitInternalError(null); - } - this.alphaVDot = Double.NaN; + this.cachedAlpha = initializeCachedAlpha(alpha, type); + this.cachedAlphaDot = Double.NaN; } serializePV = false; @@ -230,20 +233,53 @@ public CircularOrbit(final double a, final double ex, final double ey, } + /** Creates a new instance with derivatives and with cached position angle same as value inputted. + * @param a semi-major axis (m) + * @param ex e cos(ω), first component of circular eccentricity vector + * @param ey e sin(ω), second component of circular eccentricity vector + * @param i inclination (rad) + * @param raan right ascension of ascending node (Ω, rad) + * @param alpha an + ω, mean, eccentric or true latitude argument (rad) + * @param aDot semi-major axis derivative (m/s) + * @param exDot d(e cos(ω))/dt, first component of circular eccentricity vector derivative + * @param eyDot d(e sin(ω))/dt, second component of circular eccentricity vector derivative + * @param iDot inclination derivative(rad/s) + * @param raanDot right ascension of ascending node derivative (rad/s) + * @param alphaDot d(an + ω), mean, eccentric or true latitude argument derivative (rad/s) + * @param type type of latitude argument + * @param frame the frame in which are defined the parameters + * (must be a {@link Frame#isPseudoInertial pseudo-inertial frame}) + * @param date date of the orbital parameters + * @param mu central attraction coefficient (m³/s²) + * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or + * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame} + */ + public CircularOrbit(final double a, final double ex, final double ey, + final double i, final double raan, final double alpha, + final double aDot, final double exDot, final double eyDot, + final double iDot, final double raanDot, final double alphaDot, + final PositionAngleType type, + final Frame frame, final AbsoluteDate date, final double mu) + throws IllegalArgumentException { + this(a, ex, ey, i, raan, alpha, aDot, exDot, eyDot, iDot, raanDot, alphaDot, type, type, + frame, date, mu); + } + /** Creates a new instance. * @param a semi-major axis (m) * @param ex e cos(ω), first component of circular eccentricity vector * @param ey e sin(ω), second component of circular eccentricity vector * @param i inclination (rad) * @param raan right ascension of ascending node (Ω, rad) - * @param alphaV v + ω, true latitude argument (rad) + * @param alpha input latitude argument (rad) * @param aDot semi-major axis derivative (m/s) * @param exDot d(e cos(ω))/dt, first component of circular eccentricity vector derivative * @param eyDot d(e sin(ω))/dt, second component of circular eccentricity vector derivative * @param iDot inclination derivative(rad/s) * @param raanDot right ascension of ascending node derivative (rad/s) - * @param alphaVDot d(v + ω), true latitude argument derivative (rad/s) + * @param alphaDot input latitude argument derivative (rad/s) * @param pvCoordinates the {@link PVCoordinates} in inertial frame + * @param positionAngleType type of position angle * @param frame the frame in which are defined the parameters * (must be a {@link Frame#isPseudoInertial pseudo-inertial frame}) * @param mu central attraction coefficient (m³/s²) @@ -251,11 +287,11 @@ public CircularOrbit(final double a, final double ex, final double ey, * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame} */ private CircularOrbit(final double a, final double ex, final double ey, - final double i, final double raan, final double alphaV, + final double i, final double raan, final double alpha, final double aDot, final double exDot, final double eyDot, - final double iDot, final double raanDot, final double alphaVDot, - final TimeStampedPVCoordinates pvCoordinates, final Frame frame, - final double mu) + final double iDot, final double raanDot, final double alphaDot, + final TimeStampedPVCoordinates pvCoordinates, + final PositionAngleType positionAngleType, final Frame frame, final double mu) throws IllegalArgumentException { super(pvCoordinates, frame, mu); this.a = a; @@ -268,8 +304,9 @@ private CircularOrbit(final double a, final double ex, final double ey, this.iDot = iDot; this.raan = raan; this.raanDot = raanDot; - this.alphaV = alphaV; - this.alphaVDot = alphaVDot; + this.cachedAlpha = alpha; + this.cachedAlphaDot = alphaDot; + this.cachedPositionAngleType = positionAngleType; this.serializePV = true; this.partialPV = null; } @@ -291,6 +328,7 @@ private CircularOrbit(final double a, final double ex, final double ey, public CircularOrbit(final TimeStampedPVCoordinates pvCoordinates, final Frame frame, final double mu) throws IllegalArgumentException { super(pvCoordinates, frame, mu); + this.cachedPositionAngleType = PositionAngleType.TRUE; // compute semi-major axis final Vector3D pvP = pvCoordinates.getPosition(); @@ -337,7 +375,7 @@ public CircularOrbit(final TimeStampedPVCoordinates pvCoordinates, final Frame f // compute latitude argument final double beta = 1 / (1 + FastMath.sqrt(1 - ex * ex - ey * ey)); - alphaV = eccentricToTrue(FastMath.atan2(y2 + ey + eSE * beta * ex, x2 + ex - eSE * beta * ey), ex, ey); + cachedAlpha = CircularLatitudeArgumentUtility.eccentricToTrue(ex, ey, FastMath.atan2(y2 + ey + eSE * beta * ex, x2 + ex - eSE * beta * ey)); partialPV = pvCoordinates; @@ -358,15 +396,15 @@ public CircularOrbit(final TimeStampedPVCoordinates pvCoordinates, final Frame f iDot = jacobian[3][3] * aX + jacobian[3][4] * aY + jacobian[3][5] * aZ; raanDot = jacobian[4][3] * aX + jacobian[4][4] * aY + jacobian[4][5] * aZ; - // in order to compute true anomaly derivative, we must compute - // mean anomaly derivative including Keplerian motion and convert to true anomaly + // in order to compute latitude argument derivative, we must compute + // mean latitude argument derivative including Keplerian motion and convert to true latitude argument final double alphaMDot = getKeplerianMeanMotion() + jacobian[5][3] * aX + jacobian[5][4] * aY + jacobian[5][5] * aZ; final UnivariateDerivative1 exUD = new UnivariateDerivative1(ex, exDot); final UnivariateDerivative1 eyUD = new UnivariateDerivative1(ey, eyDot); final UnivariateDerivative1 alphaMUD = new UnivariateDerivative1(getAlphaM(), alphaMDot); - final UnivariateDerivative1 alphavUD = FieldCircularOrbit.eccentricToTrue(FieldCircularOrbit.meanToEccentric(alphaMUD, exUD, eyUD), exUD, eyUD); - alphaVDot = alphavUD.getDerivative(1); + final UnivariateDerivative1 alphavUD = FieldCircularLatitudeArgumentUtility.meanToTrue(exUD, eyUD, alphaMUD); + cachedAlphaDot = alphavUD.getFirstDerivative(); } else { // acceleration is either almost zero or NaN, @@ -377,7 +415,7 @@ public CircularOrbit(final TimeStampedPVCoordinates pvCoordinates, final Frame f eyDot = Double.NaN; iDot = Double.NaN; raanDot = Double.NaN; - alphaVDot = Double.NaN; + cachedAlphaDot = Double.NaN; } serializePV = true; @@ -426,7 +464,8 @@ public CircularOrbit(final Orbit op) { final double equiEy = op.getEquinoctialEy(); ex = equiEx * cosRaan + equiEy * sinRaan; ey = equiEy * cosRaan - equiEx * sinRaan; - alphaV = op.getLv() - raan; + cachedPositionAngleType = PositionAngleType.TRUE; + cachedAlpha = op.getLv() - raan; if (op.hasDerivatives()) { aDot = op.getADot(); @@ -440,14 +479,14 @@ public CircularOrbit(final Orbit op) { (equiEyDot - equiEx * raanDot) * sinRaan; eyDot = (equiEyDot - equiEx * raanDot) * cosRaan - (equiExDot + equiEy * raanDot) * sinRaan; - alphaVDot = op.getLvDot() - raanDot; + cachedAlphaDot = op.getLvDot() - raanDot; } else { aDot = Double.NaN; exDot = Double.NaN; eyDot = Double.NaN; iDot = Double.NaN; raanDot = Double.NaN; - alphaVDot = Double.NaN; + cachedAlphaDot = Double.NaN; } serializePV = false; @@ -456,39 +495,46 @@ public CircularOrbit(final Orbit op) { } /** {@inheritDoc} */ + @Override public OrbitType getType() { return OrbitType.CIRCULAR; } /** {@inheritDoc} */ + @Override public double getA() { return a; } /** {@inheritDoc} */ + @Override public double getADot() { return aDot; } /** {@inheritDoc} */ + @Override public double getEquinoctialEx() { final SinCos sc = FastMath.sinCos(raan); return ex * sc.cos() - ey * sc.sin(); } /** {@inheritDoc} */ + @Override public double getEquinoctialExDot() { final SinCos sc = FastMath.sinCos(raan); return (exDot - ey * raanDot) * sc.cos() - (eyDot + ex * raanDot) * sc.sin(); } /** {@inheritDoc} */ + @Override public double getEquinoctialEy() { final SinCos sc = FastMath.sinCos(raan); return ey * sc.cos() + ex * sc.sin(); } /** {@inheritDoc} */ + @Override public double getEquinoctialEyDot() { final SinCos sc = FastMath.sinCos(raan); return (eyDot + ex * raanDot) * sc.cos() + (exDot - ey * raanDot) * sc.sin(); @@ -524,6 +570,7 @@ public double getCircularEyDot() { } /** {@inheritDoc} */ + @Override public double getHx() { // Check for equatorial retrograde orbit if (FastMath.abs(i - FastMath.PI) < 1.0e-10) { @@ -533,6 +580,7 @@ public double getHx() { } /** {@inheritDoc} */ + @Override public double getHxDot() { // Check for equatorial retrograde orbit if (FastMath.abs(i - FastMath.PI) < 1.0e-10) { @@ -544,6 +592,7 @@ public double getHxDot() { } /** {@inheritDoc} */ + @Override public double getHy() { // Check for equatorial retrograde orbit if (FastMath.abs(i - FastMath.PI) < 1.0e-10) { @@ -553,6 +602,7 @@ public double getHy() { } /** {@inheritDoc} */ + @Override public double getHyDot() { // Check for equatorial retrograde orbit if (FastMath.abs(i - FastMath.PI) < 1.0e-10) { @@ -567,7 +617,19 @@ public double getHyDot() { * @return v + ω true latitude argument (rad) */ public double getAlphaV() { - return alphaV; + switch (cachedPositionAngleType) { + case TRUE: + return cachedAlpha; + + case ECCENTRIC: + return CircularLatitudeArgumentUtility.eccentricToTrue(ex, ey, cachedAlpha); + + case MEAN: + return CircularLatitudeArgumentUtility.meanToTrue(ex, ey, cachedAlpha); + + default: + throw new OrekitInternalError(null); + } } /** Get the true latitude argument derivative. @@ -578,14 +640,48 @@ public double getAlphaV() { * @since 9.0 */ public double getAlphaVDot() { - return alphaVDot; + switch (cachedPositionAngleType) { + case ECCENTRIC: + final UnivariateDerivative1 alphaEUD = new UnivariateDerivative1(cachedAlpha, cachedAlphaDot); + final UnivariateDerivative1 exUD = new UnivariateDerivative1(ex, exDot); + final UnivariateDerivative1 eyUD = new UnivariateDerivative1(ey, eyDot); + final UnivariateDerivative1 alphaVUD = FieldCircularLatitudeArgumentUtility.eccentricToTrue(exUD, eyUD, + alphaEUD); + return alphaVUD.getFirstDerivative(); + + case TRUE: + return cachedAlphaDot; + + case MEAN: + final UnivariateDerivative1 alphaMUD = new UnivariateDerivative1(cachedAlpha, cachedAlphaDot); + final UnivariateDerivative1 exUD2 = new UnivariateDerivative1(ex, exDot); + final UnivariateDerivative1 eyUD2 = new UnivariateDerivative1(ey, eyDot); + final UnivariateDerivative1 alphaVUD2 = FieldCircularLatitudeArgumentUtility.meanToTrue(exUD2, + eyUD2, alphaMUD); + return alphaVUD2.getFirstDerivative(); + + default: + throw new OrekitInternalError(null); + } } /** Get the eccentric latitude argument. * @return E + ω eccentric latitude argument (rad) */ public double getAlphaE() { - return trueToEccentric(alphaV, ex, ey); + switch (cachedPositionAngleType) { + case TRUE: + return CircularLatitudeArgumentUtility.trueToEccentric(ex, ey, cachedAlpha); + + case ECCENTRIC: + return cachedAlpha; + + case MEAN: + return CircularLatitudeArgumentUtility.meanToEccentric(ex, ey, cachedAlpha); + + default: + throw new OrekitInternalError(null); + } } /** Get the eccentric latitude argument derivative. @@ -596,18 +692,48 @@ public double getAlphaE() { * @since 9.0 */ public double getAlphaEDot() { - final UnivariateDerivative1 alphaVUD = new UnivariateDerivative1(alphaV, alphaVDot); - final UnivariateDerivative1 exUD = new UnivariateDerivative1(ex, exDot); - final UnivariateDerivative1 eyUD = new UnivariateDerivative1(ey, eyDot); - final UnivariateDerivative1 alphaEUD = FieldCircularOrbit.trueToEccentric(alphaVUD, exUD, eyUD); - return alphaEUD.getDerivative(1); + switch (cachedPositionAngleType) { + case TRUE: + final UnivariateDerivative1 alphaVUD = new UnivariateDerivative1(cachedAlpha, cachedAlphaDot); + final UnivariateDerivative1 exUD = new UnivariateDerivative1(ex, exDot); + final UnivariateDerivative1 eyUD = new UnivariateDerivative1(ey, eyDot); + final UnivariateDerivative1 alphaEUD = FieldCircularLatitudeArgumentUtility.trueToEccentric(exUD, eyUD, + alphaVUD); + return alphaEUD.getFirstDerivative(); + + case ECCENTRIC: + return cachedAlphaDot; + + case MEAN: + final UnivariateDerivative1 alphaMUD = new UnivariateDerivative1(cachedAlpha, cachedAlphaDot); + final UnivariateDerivative1 exUD2 = new UnivariateDerivative1(ex, exDot); + final UnivariateDerivative1 eyUD2 = new UnivariateDerivative1(ey, eyDot); + final UnivariateDerivative1 alphaVUD2 = FieldCircularLatitudeArgumentUtility.meanToEccentric(exUD2, + eyUD2, alphaMUD); + return alphaVUD2.getFirstDerivative(); + + default: + throw new OrekitInternalError(null); + } } /** Get the mean latitude argument. * @return M + ω mean latitude argument (rad) */ public double getAlphaM() { - return eccentricToMean(trueToEccentric(alphaV, ex, ey), ex, ey); + switch (cachedPositionAngleType) { + case TRUE: + return CircularLatitudeArgumentUtility.trueToMean(ex, ey, cachedAlpha); + + case MEAN: + return cachedAlpha; + + case ECCENTRIC: + return CircularLatitudeArgumentUtility.eccentricToMean(ex, ey, cachedAlpha); + + default: + throw new OrekitInternalError(null); + } } /** Get the mean latitude argument derivative. @@ -618,11 +744,29 @@ public double getAlphaM() { * @since 9.0 */ public double getAlphaMDot() { - final UnivariateDerivative1 alphaVUD = new UnivariateDerivative1(alphaV, alphaVDot); - final UnivariateDerivative1 exUD = new UnivariateDerivative1(ex, exDot); - final UnivariateDerivative1 eyUD = new UnivariateDerivative1(ey, eyDot); - final UnivariateDerivative1 alphaMUD = FieldCircularOrbit.eccentricToMean(FieldCircularOrbit.trueToEccentric(alphaVUD, exUD, eyUD), exUD, eyUD); - return alphaMUD.getDerivative(1); + switch (cachedPositionAngleType) { + case TRUE: + final UnivariateDerivative1 alphaVUD = new UnivariateDerivative1(cachedAlpha, cachedAlphaDot); + final UnivariateDerivative1 exUD = new UnivariateDerivative1(ex, exDot); + final UnivariateDerivative1 eyUD = new UnivariateDerivative1(ey, eyDot); + final UnivariateDerivative1 alphaMUD = FieldCircularLatitudeArgumentUtility.trueToMean(exUD, eyUD, + alphaVUD); + return alphaMUD.getFirstDerivative(); + + case MEAN: + return cachedAlphaDot; + + case ECCENTRIC: + final UnivariateDerivative1 alphaEUD = new UnivariateDerivative1(cachedAlpha, cachedAlphaDot); + final UnivariateDerivative1 exUD2 = new UnivariateDerivative1(ex, exDot); + final UnivariateDerivative1 eyUD2 = new UnivariateDerivative1(ey, eyDot); + final UnivariateDerivative1 alphaMUD2 = FieldCircularLatitudeArgumentUtility.eccentricToMean(exUD2, + eyUD2, alphaEUD); + return alphaMUD2.getFirstDerivative(); + + default: + throw new OrekitInternalError(null); + } } /** Get the latitude argument. @@ -655,11 +799,9 @@ public double getAlphaDot(final PositionAngleType type) { * @param ey e sin(ω), second component of circular eccentricity vector * @return the true latitude argument. */ + @Deprecated public static double eccentricToTrue(final double alphaE, final double ex, final double ey) { - final double epsilon = FastMath.sqrt(1 - ex * ex - ey * ey); - final SinCos scAlphaE = FastMath.sinCos(alphaE); - return alphaE + 2 * FastMath.atan((ex * scAlphaE.sin() - ey * scAlphaE.cos()) / - (epsilon + 1 - ex * scAlphaE.cos() - ey * scAlphaE.sin())); + return CircularLatitudeArgumentUtility.eccentricToTrue(ex, ey, alphaE); } /** Computes the eccentric latitude argument from the true latitude argument. @@ -668,11 +810,9 @@ public static double eccentricToTrue(final double alphaE, final double ex, final * @param ey e sin(ω), second component of circular eccentricity vector * @return the eccentric latitude argument. */ + @Deprecated public static double trueToEccentric(final double alphaV, final double ex, final double ey) { - final double epsilon = FastMath.sqrt(1 - ex * ex - ey * ey); - final SinCos scAlphaV = FastMath.sinCos(alphaV); - return alphaV + 2 * FastMath.atan((ey * scAlphaV.cos() - ex * scAlphaV.sin()) / - (epsilon + 1 + ex * scAlphaV.cos() + ey * scAlphaV.sin())); + return CircularLatitudeArgumentUtility.trueToEccentric(ex, ey, alphaV); } /** Computes the eccentric latitude argument from the mean latitude argument. @@ -681,31 +821,9 @@ public static double trueToEccentric(final double alphaV, final double ex, final * @param ey e sin(ω), second component of circular eccentricity vector * @return the eccentric latitude argument. */ + @Deprecated public static double meanToEccentric(final double alphaM, final double ex, final double ey) { - // Generalization of Kepler equation to circular parameters - // with alphaE = PA + E and - // alphaM = PA + M = alphaE - ex.sin(alphaE) + ey.cos(alphaE) - double alphaE = alphaM; - double shift = 0.0; - double alphaEMalphaM = 0.0; - SinCos scAlphaE = FastMath.sinCos(alphaE); - int iter = 0; - do { - final double f2 = ex * scAlphaE.sin() - ey * scAlphaE.cos(); - final double f1 = 1.0 - ex * scAlphaE.cos() - ey * scAlphaE.sin(); - final double f0 = alphaEMalphaM - f2; - - final double f12 = 2.0 * f1; - shift = f0 * f12 / (f1 * f12 - f0 * f2); - - alphaEMalphaM -= shift; - alphaE = alphaM + alphaEMalphaM; - scAlphaE = FastMath.sinCos(alphaE); - - } while (++iter < 50 && FastMath.abs(shift) > 1.0e-12); - - return alphaE; - + return CircularLatitudeArgumentUtility.meanToEccentric(ex, ey, alphaM); } /** Computes the mean latitude argument from the eccentric latitude argument. @@ -714,27 +832,31 @@ public static double meanToEccentric(final double alphaM, final double ex, final * @param ey e sin(ω), second component of circular eccentricity vector * @return the mean latitude argument. */ + @Deprecated public static double eccentricToMean(final double alphaE, final double ex, final double ey) { - final SinCos scAlphaE = FastMath.sinCos(alphaE); - return alphaE + (ey * scAlphaE.cos() - ex * scAlphaE.sin()); + return CircularLatitudeArgumentUtility.eccentricToMean(ex, ey, alphaE); } /** {@inheritDoc} */ + @Override public double getE() { return FastMath.sqrt(ex * ex + ey * ey); } /** {@inheritDoc} */ + @Override public double getEDot() { return (ex * exDot + ey * eyDot) / getE(); } /** {@inheritDoc} */ + @Override public double getI() { return i; } /** {@inheritDoc} */ + @Override public double getIDot() { return iDot; } @@ -758,31 +880,37 @@ public double getRightAscensionOfAscendingNodeDot() { } /** {@inheritDoc} */ + @Override public double getLv() { - return alphaV + raan; + return getAlphaV() + raan; } /** {@inheritDoc} */ + @Override public double getLvDot() { - return alphaVDot + raanDot; + return getAlphaVDot() + raanDot; } /** {@inheritDoc} */ + @Override public double getLE() { return getAlphaE() + raan; } /** {@inheritDoc} */ + @Override public double getLEDot() { return getAlphaEDot() + raanDot; } /** {@inheritDoc} */ + @Override public double getLM() { return getAlphaM() + raan; } /** {@inheritDoc} */ + @Override public double getLMDot() { return getAlphaMDot() + raanDot; } @@ -848,6 +976,95 @@ private void computePVWithoutA() { } + /** Initialize cached alpha with rate. + * @param alpha input alpha + * @param alphaDot rate of input alpha + * @param inputType position angle type passed as input + * @return alpha to cache with rate + * @since 12.1 + */ + private UnivariateDerivative1 initializeCachedAlpha(final double alpha, final double alphaDot, + final PositionAngleType inputType) { + if (cachedPositionAngleType == inputType) { + return new UnivariateDerivative1(alpha, alphaDot); + + } else { + final UnivariateDerivative1 exUD = new UnivariateDerivative1(ex, exDot); + final UnivariateDerivative1 eyUD = new UnivariateDerivative1(ey, eyDot); + final UnivariateDerivative1 alphaUD = new UnivariateDerivative1(alpha, alphaDot); + + switch (cachedPositionAngleType) { + + case ECCENTRIC: + if (inputType == PositionAngleType.MEAN) { + return FieldCircularLatitudeArgumentUtility.meanToEccentric(exUD, eyUD, alphaUD); + } else { + return FieldCircularLatitudeArgumentUtility.trueToEccentric(exUD, eyUD, alphaUD); + } + + case TRUE: + if (inputType == PositionAngleType.MEAN) { + return FieldCircularLatitudeArgumentUtility.meanToTrue(exUD, eyUD, alphaUD); + } else { + return FieldCircularLatitudeArgumentUtility.eccentricToTrue(exUD, eyUD, alphaUD); + } + + case MEAN: + if (inputType == PositionAngleType.TRUE) { + return FieldCircularLatitudeArgumentUtility.trueToMean(exUD, eyUD, alphaUD); + } else { + return FieldCircularLatitudeArgumentUtility.eccentricToMean(exUD, eyUD, alphaUD); + } + + default: + throw new OrekitInternalError(null); + + } + + } + + } + + /** Initialize cached alpha. + * @param alpha input alpha + * @param positionAngleType position angle type passed as input + * @return alpha to cache + * @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); + } + } + } + /** Compute non-Keplerian part of the acceleration from first time derivatives. *

        * This method should be called only when {@link #hasDerivatives()} returns true. @@ -872,6 +1089,7 @@ private Vector3D nonKeplerianAcceleration() { } /** {@inheritDoc} */ + @Override protected Vector3D initPosition() { // get equinoctial parameters @@ -917,6 +1135,7 @@ protected Vector3D initPosition() { } /** {@inheritDoc} */ + @Override protected TimeStampedPVCoordinates initPVCoordinates() { // position and velocity @@ -934,13 +1153,14 @@ protected TimeStampedPVCoordinates initPVCoordinates() { } /** {@inheritDoc} */ + @Override public CircularOrbit shiftedBy(final double dt) { // use Keplerian-only motion final CircularOrbit keplerianShifted = new CircularOrbit(a, ex, ey, i, raan, getAlphaM() + getKeplerianMeanMotion() * dt, - PositionAngleType.MEAN, getFrame(), - getDate().shiftedBy(dt), getMu()); + PositionAngleType.MEAN, cachedPositionAngleType, + getFrame(), getDate().shiftedBy(dt), getMu()); if (hasDerivatives()) { @@ -971,6 +1191,7 @@ PositionAngleType.MEAN, getFrame(), } /** {@inheritDoc} */ + @Override protected double[][] computeJacobianMeanWrtCartesian() { @@ -1100,6 +1321,7 @@ protected double[][] computeJacobianMeanWrtCartesian() { } /** {@inheritDoc} */ + @Override protected double[][] computeJacobianEccentricWrtCartesian() { // start by computing the Jacobian with mean angle @@ -1128,6 +1350,7 @@ protected double[][] computeJacobianEccentricWrtCartesian() { } /** {@inheritDoc} */ + @Override protected double[][] computeJacobianTrueWrtCartesian() { // start by computing the Jacobian with eccentric angle @@ -1176,22 +1399,24 @@ protected double[][] computeJacobianTrueWrtCartesian() { } /** {@inheritDoc} */ + @Override public void addKeplerContribution(final PositionAngleType type, final double gm, final double[] pDot) { final double oMe2; final double ksi; final double n = FastMath.sqrt(gm / a) / a; - final SinCos sc = FastMath.sinCos(alphaV); + final SinCos sc; switch (type) { case MEAN : pDot[5] += n; break; case ECCENTRIC : - oMe2 = 1 - ex * ex - ey * ey; - ksi = 1 + ex * sc.cos() + ey * sc.sin(); - pDot[5] += n * ksi / oMe2; + 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)); @@ -1210,14 +1435,14 @@ public String toString() { append(", ex: ").append(ex).append(", ey: ").append(ey). append(", i: ").append(FastMath.toDegrees(i)). append(", raan: ").append(FastMath.toDegrees(raan)). - append(", alphaV: ").append(FastMath.toDegrees(alphaV)). + append(", alphaV: ").append(FastMath.toDegrees(getAlphaV())). append(";}").toString(); } /** {@inheritDoc} */ @Override public PositionAngleType getCachedPositionAngleType() { - return PositionAngleType.TRUE; + return cachedPositionAngleType; } /** {@inheritDoc} */ @@ -1230,8 +1455,8 @@ public boolean hasRates() { @Override public CircularOrbit removeRates() { final PositionAngleType positionAngleType = getCachedPositionAngleType(); - return new CircularOrbit(getA(), getCircularEx(), getCircularEy(), getI(), getRightAscensionOfAscendingNode(), - getAlpha(positionAngleType), positionAngleType, getFrame(), getDate(), getMu()); + return new CircularOrbit(a, ex, ey, i, raan, cachedAlpha, positionAngleType, positionAngleType, + getFrame(), getDate(), getMu()); } /** Replace the instance with a data transfer object for serialization. @@ -1247,20 +1472,24 @@ private Object writeReplace() { private static class DTO implements Serializable { /** Serializable UID. */ - private static final long serialVersionUID = 20170414L; + private static final long serialVersionUID = 20231217L; /** Double values. */ - private double[] d; + private final double[] d; /** Frame in which are defined the orbital parameters. */ private final Frame frame; + /** Type of cached position angle. */ + private final PositionAngleType positionAngleType; + /** Simple constructor. * @param orbit instance to serialize */ private DTO(final CircularOrbit orbit) { final AbsoluteDate date = orbit.getDate(); + positionAngleType = orbit.getCachedPositionAngleType(); // decompose date final AbsoluteDate j2000Epoch = @@ -1275,9 +1504,9 @@ private DTO(final CircularOrbit orbit) { // date + mu + orbit + derivatives + Cartesian : 24 parameters epoch, offset, orbit.getMu(), orbit.a, orbit.ex, orbit.ey, - orbit.i, orbit.raan, orbit.alphaV, + orbit.i, orbit.raan, orbit.cachedAlpha, orbit.aDot, orbit.exDot, orbit.eyDot, - orbit.iDot, orbit.raanDot, orbit.alphaVDot, + orbit.iDot, orbit.raanDot, orbit.cachedAlphaDot, pv.getPosition().getX(), pv.getPosition().getY(), pv.getPosition().getZ(), pv.getVelocity().getX(), pv.getVelocity().getY(), pv.getVelocity().getZ(), pv.getAcceleration().getX(), pv.getAcceleration().getY(), pv.getAcceleration().getZ(), @@ -1287,7 +1516,7 @@ private DTO(final CircularOrbit orbit) { // date + mu + orbit + Cartesian : 18 parameters epoch, offset, orbit.getMu(), orbit.a, orbit.ex, orbit.ey, - orbit.i, orbit.raan, orbit.alphaV, + orbit.i, orbit.raan, orbit.cachedAlpha, pv.getPosition().getX(), pv.getPosition().getY(), pv.getPosition().getZ(), pv.getVelocity().getX(), pv.getVelocity().getY(), pv.getVelocity().getZ(), pv.getAcceleration().getX(), pv.getAcceleration().getY(), pv.getAcceleration().getZ(), @@ -1299,16 +1528,16 @@ private DTO(final CircularOrbit orbit) { this.d = new double[] { epoch, offset, orbit.getMu(), orbit.a, orbit.ex, orbit.ey, - orbit.i, orbit.raan, orbit.alphaV, + orbit.i, orbit.raan, orbit.cachedAlpha, orbit.aDot, orbit.exDot, orbit.eyDot, - orbit.iDot, orbit.raanDot, orbit.alphaVDot + orbit.iDot, orbit.raanDot, orbit.cachedAlphaDot }; } else { // date + mu + orbit: 9 parameters this.d = new double[] { epoch, offset, orbit.getMu(), orbit.a, orbit.ex, orbit.ey, - orbit.i, orbit.raan, orbit.alphaV + orbit.i, orbit.raan, orbit.cachedAlpha }; } } @@ -1331,7 +1560,7 @@ private Object readResolve() { new Vector3D(d[15], d[16], d[17]), new Vector3D(d[18], d[19], d[20]), new Vector3D(d[21], d[22], d[23])), - frame, + positionAngleType, frame, d[2]); case 18 : // date + mu + orbit + Cartesian return new CircularOrbit(d[3], d[4], d[5], d[6], d[7], d[8], @@ -1340,16 +1569,16 @@ private Object readResolve() { new Vector3D(d[ 9], d[10], d[11]), new Vector3D(d[12], d[13], d[14]), new Vector3D(d[15], d[16], d[17])), - frame, + positionAngleType, frame, d[2]); case 15 : // date + mu + orbit + derivatives return new CircularOrbit(d[ 3], d[ 4], d[ 5], d[ 6], d[ 7], d[ 8], d[ 9], d[10], d[11], d[12], d[13], d[14], - PositionAngleType.TRUE, + positionAngleType, positionAngleType, frame, j2000Epoch.shiftedBy(d[0]).shiftedBy(d[1]), d[2]); default : // date + mu + orbit - return new CircularOrbit(d[3], d[4], d[5], d[6], d[7], d[8], PositionAngleType.TRUE, + return new CircularOrbit(d[3], d[4], d[5], d[6], d[7], d[8], positionAngleType, positionAngleType, frame, j2000Epoch.shiftedBy(d[0]).shiftedBy(d[1]), d[2]); diff --git a/src/main/java/org/orekit/orbits/EquinoctialLongitudeArgumentUtility.java b/src/main/java/org/orekit/orbits/EquinoctialLongitudeArgumentUtility.java new file mode 100644 index 0000000000..68002b02b7 --- /dev/null +++ b/src/main/java/org/orekit/orbits/EquinoctialLongitudeArgumentUtility.java @@ -0,0 +1,175 @@ +/* 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.orbits; + +import org.hipparchus.util.FastMath; +import org.hipparchus.util.SinCos; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; + +/** + * Utility methods for converting between different longitude arguments used by {@link EquinoctialOrbit}. + * @author Romain Serra + * @see EquinoctialOrbit + * @since 12.1 + */ +public class EquinoctialLongitudeArgumentUtility { + + /** Tolerance for stopping criterion in iterative conversion from mean to eccentric angle. */ + private static final double TOLERANCE_CONVERGENCE = 1.0e-12; + + /** Maximum number of iterations in iterative conversion from mean to eccentric angle. */ + private static final int MAXIMUM_ITERATION = 50; + + /** Private constructor for utility class. */ + private EquinoctialLongitudeArgumentUtility() { + // nothing here (utils class) + } + + /** + * Computes the true longitude argument from the eccentric longitude argument. + * + * @param ex e cos(ω), first component of eccentricity vector + * @param ey e sin(ω), second component of eccentricity vector + * @param lE = E + ω + Ω eccentric longitude argument (rad) + * @return the true longitude argument. + */ + public static double eccentricToTrue(final double ex, final double ey, final double lE) { + final double epsilon = eccentricAndTrueEpsilon(ex, ey); + final SinCos scLE = FastMath.sinCos(lE); + final double num = ex * scLE.sin() - ey * scLE.cos(); + final double den = epsilon + 1 - ex * scLE.cos() - ey * scLE.sin(); + return lE + eccentricAndTrueAtan(num, den); + } + + /** + * Computes the eccentric longitude argument from the true longitude argument. + * + * @param ex e cos(ω), first component of eccentricity vector + * @param ey e sin(ω), second component of eccentricity vector + * @param lV = V + ω + Ω true longitude argument (rad) + * @return the eccentric longitude argument. + */ + public static double trueToEccentric(final double ex, final double ey, final double lV) { + final double epsilon = eccentricAndTrueEpsilon(ex, ey); + final SinCos scLv = FastMath.sinCos(lV); + final double num = ey * scLv.cos() - ex * scLv.sin(); + final double den = epsilon + 1 + ex * scLv.cos() + ey * scLv.sin(); + return lV + eccentricAndTrueAtan(num, den); + } + + /** + * Computes an intermediate quantity for conversions between true and eccentric. + * + * @param ex e cos(ω), first component of eccentricity vector + * @param ey e sin(ω), second component of eccentricity vector + * @return intermediate variable referred to as epsilon. + */ + private static double eccentricAndTrueEpsilon(final double ex, final double ey) { + return FastMath.sqrt(1 - ex * ex - ey * ey); + } + + /** + * Computes another intermediate quantity for conversions between true and eccentric. + * + * @param num numerator for angular conversion + * @param den denominator for angular conversion + * @return arc-tangent of ratio of inputs times two. + */ + private static double eccentricAndTrueAtan(final double num, final double den) { + return 2. * FastMath.atan(num / den); + } + + /** + * Computes the eccentric longitude argument from the mean longitude argument. + * + * @param ex e cos(ω), first component of eccentricity vector + * @param ey e sin(ω), second component of eccentricity vector + * @param lM = M + ω + Ω mean longitude argument (rad) + * @return the eccentric longitude argument. + */ + public static double meanToEccentric(final double ex, final double ey, final double lM) { + // Generalization of Kepler equation to equinoctial parameters + // with lE = PA + RAAN + E and + // lM = PA + RAAN + M = lE - ex.sin(lE) + ey.cos(lE) + double lE = lM; + double shift; + double lEmlM = 0.0; + boolean hasConverged; + int iter = 0; + do { + final SinCos scLE = FastMath.sinCos(lE); + final double f2 = ex * scLE.sin() - ey * scLE.cos(); + final double f1 = 1.0 - ex * scLE.cos() - ey * scLE.sin(); + final double f0 = lEmlM - f2; + + final double f12 = 2.0 * f1; + shift = f0 * f12 / (f1 * f12 - f0 * f2); + + lEmlM -= shift; + lE = lM + lEmlM; + + hasConverged = FastMath.abs(shift) <= TOLERANCE_CONVERGENCE; + } while (++iter < MAXIMUM_ITERATION && !hasConverged); + + if (!hasConverged) { + throw new OrekitException(OrekitMessages.UNABLE_TO_COMPUTE_ECCENTRIC_LONGITUDE_ARGUMENT, iter); + } + return lE; + + } + + /** + * Computes the mean longitude argument from the eccentric longitude argument. + * + * @param ex e cos(ω), first component of eccentricity vector + * @param ey e sin(ω), second component of eccentricity vector + * @param lE = E + ω + Ω mean longitude argument (rad) + * @return the mean longitude argument. + */ + public static double eccentricToMean(final double ex, final double ey, final double lE) { + final SinCos scLE = FastMath.sinCos(lE); + return lE - ex * scLE.sin() + ey * scLE.cos(); + } + + /** + * Computes the mean longitude argument from the eccentric longitude argument. + * + * @param ex e cos(ω), first component of eccentricity vector + * @param ey e sin(ω), second component of eccentricity vector + * @param lV = V + ω + Ω true longitude argument (rad) + * @return the mean longitude argument. + */ + public static double trueToMean(final double ex, final double ey, final double lV) { + final double alphaE = trueToEccentric(ex, ey, lV); + return eccentricToMean(ex, ey, alphaE); + } + + /** + * Computes the true longitude argument from the eccentric longitude argument. + * + * @param ex e cos(ω), first component of eccentricity vector + * @param ey e sin(ω), second component of eccentricity vector + * @param lM = M + ω + Ω mean longitude argument (rad) + * @return the true longitude argument. + */ + public static double meanToTrue(final double ex, final double ey, final double lM) { + final double alphaE = meanToEccentric(ex, ey, lM); + return eccentricToTrue(ex, ey, alphaE); + } + +} diff --git a/src/main/java/org/orekit/orbits/EquinoctialOrbit.java b/src/main/java/org/orekit/orbits/EquinoctialOrbit.java index c73dc03e9c..7b7fb0feac 100644 --- a/src/main/java/org/orekit/orbits/EquinoctialOrbit.java +++ b/src/main/java/org/orekit/orbits/EquinoctialOrbit.java @@ -57,7 +57,7 @@ * parameters are still unambiguously defined whereas some Keplerian elements * (more precisely ω and Ω) become ambiguous. For this reason, equinoctial * parameters are the recommended way to represent orbits. Note however than - * * the present implementation does not handle non-elliptical cases. + * the present implementation does not handle non-elliptical cases. *

        *

        * The instance EquinoctialOrbit is guaranteed to be immutable. @@ -92,8 +92,11 @@ public class EquinoctialOrbit extends Orbit implements PositionAngleBased { /** Second component of the inclination vector. */ private final double hy; - /** True longitude argument (rad). */ - private final double lv; + /** Cached longitude argument (rad). */ + private final double cachedL; + + /** Cache type of position angle (longitude argument). */ + private final PositionAngleType cachedPositionAngleType; /** Semi-major axis derivative (m/s). */ private final double aDot; @@ -110,8 +113,8 @@ public class EquinoctialOrbit extends Orbit implements PositionAngleBased { /** Second component of the inclination vector derivative. */ private final double hyDot; - /** True longitude argument derivative (rad/s). */ - private final double lvDot; + /** Derivative of cached longitude argument (rad/s). */ + private final double cachedLDot; /** Partial Cartesian coordinates (position and velocity are valid, acceleration may be missing). */ private transient PVCoordinates partialPV; @@ -124,21 +127,46 @@ public class EquinoctialOrbit extends Orbit implements PositionAngleBased { * @param hy tan(i/2) sin(Ω), second component of inclination vector * @param l (M or E or v) + ω + Ω, mean, eccentric or true longitude argument (rad) * @param type type of longitude argument + * @param cachedPositionAngleType type of cached longitude argument * @param frame the frame in which the parameters are defined * (must be a {@link Frame#isPseudoInertial pseudo-inertial frame}) * @param date date of the orbital parameters * @param mu central attraction coefficient (m³/s²) * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame} + * @since 12.1 */ public EquinoctialOrbit(final double a, final double ex, final double ey, final double hx, final double hy, final double l, - final PositionAngleType type, + final PositionAngleType type, final PositionAngleType cachedPositionAngleType, final Frame frame, final AbsoluteDate date, final double mu) throws IllegalArgumentException { this(a, ex, ey, hx, hy, l, Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN, - type, frame, date, mu); + type, cachedPositionAngleType, frame, date, mu); + } + + /** Creates a new instance without derivatives and with cached position angle same as value inputted. + * @param a semi-major axis (m) + * @param ex e cos(ω + Ω), first component of eccentricity vector + * @param ey e sin(ω + Ω), second component of eccentricity vector + * @param hx tan(i/2) cos(Ω), first component of inclination vector + * @param hy tan(i/2) sin(Ω), second component of inclination vector + * @param l (M or E or v) + ω + Ω, mean, eccentric or true longitude argument (rad) + * @param type type of longitude argument + * @param frame the frame in which the parameters are defined + * (must be a {@link Frame#isPseudoInertial pseudo-inertial frame}) + * @param date date of the orbital parameters + * @param mu central attraction coefficient (m³/s²) + * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or + * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame} + */ + public EquinoctialOrbit(final double a, final double ex, final double ey, + final double hx, final double hy, final double l, + final PositionAngleType type, + final Frame frame, final AbsoluteDate date, final double mu) + throws IllegalArgumentException { + this(a, ex, ey, hx, hy, l, type, type, frame, date, mu); } /** Creates a new instance. @@ -155,18 +183,20 @@ public EquinoctialOrbit(final double a, final double ex, final double ey, * @param hyDot d(tan(i/2) sin(Ω))/dt, second component of inclination vector derivative * @param lDot d(M or E or v) + ω + Ω)/dr, mean, eccentric or true longitude argument derivative (rad/s) * @param type type of longitude argument + * @param cachedPositionAngleType of cached longitude argument * @param frame the frame in which the parameters are defined * (must be a {@link Frame#isPseudoInertial pseudo-inertial frame}) * @param date date of the orbital parameters * @param mu central attraction coefficient (m³/s²) * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame} + * @since 12.1 */ public EquinoctialOrbit(final double a, final double ex, final double ey, final double hx, final double hy, final double l, final double aDot, final double exDot, final double eyDot, final double hxDot, final double hyDot, final double lDot, - final PositionAngleType type, + final PositionAngleType type, final PositionAngleType cachedPositionAngleType, final Frame frame, final AbsoluteDate date, final double mu) throws IllegalArgumentException { super(frame, date, mu); @@ -174,6 +204,7 @@ public EquinoctialOrbit(final double a, final double ex, final double ey, throw new OrekitIllegalArgumentException(OrekitMessages.HYPERBOLIC_ORBIT_NOT_HANDLED_AS, getClass().getName()); } + this.cachedPositionAngleType = cachedPositionAngleType; this.a = a; this.aDot = aDot; this.ex = ex; @@ -186,46 +217,49 @@ public EquinoctialOrbit(final double a, final double ex, final double ey, this.hyDot = hyDot; if (hasDerivatives()) { - final UnivariateDerivative1 exUD = new UnivariateDerivative1(ex, exDot); - final UnivariateDerivative1 eyUD = new UnivariateDerivative1(ey, eyDot); - final UnivariateDerivative1 lUD = new UnivariateDerivative1(l, lDot); - final UnivariateDerivative1 lvUD; - switch (type) { - case MEAN : - lvUD = FieldEquinoctialOrbit.eccentricToTrue(FieldEquinoctialOrbit.meanToEccentric(lUD, exUD, eyUD), exUD, eyUD); - break; - case ECCENTRIC : - lvUD = FieldEquinoctialOrbit.eccentricToTrue(lUD, exUD, eyUD); - break; - case TRUE : - lvUD = lUD; - break; - default : // this should never happen - throw new OrekitInternalError(null); - } - this.lv = lvUD.getValue(); - this.lvDot = lvUD.getDerivative(1); + final UnivariateDerivative1 alphaUD = initializeCachedL(l, lDot, type); + this.cachedL = alphaUD.getValue(); + this.cachedLDot = alphaUD.getFirstDerivative(); } else { - switch (type) { - case MEAN : - this.lv = eccentricToTrue(meanToEccentric(l, ex, ey), ex, ey); - break; - case ECCENTRIC : - this.lv = eccentricToTrue(l, ex, ey); - break; - case TRUE : - this.lv = l; - break; - default : // this should never happen - throw new OrekitInternalError(null); - } - this.lvDot = Double.NaN; + this.cachedL = initializeCachedL(l, type); + this.cachedLDot = Double.NaN; } this.partialPV = null; } + /** Creates a new instance with derivatives and with cached position angle same as value inputted. + * @param a semi-major axis (m) + * @param ex e cos(ω + Ω), first component of eccentricity vector + * @param ey e sin(ω + Ω), second component of eccentricity vector + * @param hx tan(i/2) cos(Ω), first component of inclination vector + * @param hy tan(i/2) sin(Ω), second component of inclination vector + * @param l (M or E or v) + ω + Ω, mean, eccentric or true longitude argument (rad) + * @param aDot semi-major axis derivative (m/s) + * @param exDot d(e cos(ω + Ω))/dt, first component of eccentricity vector derivative + * @param eyDot d(e sin(ω + Ω))/dt, second component of eccentricity vector derivative + * @param hxDot d(tan(i/2) cos(Ω))/dt, first component of inclination vector derivative + * @param hyDot d(tan(i/2) sin(Ω))/dt, second component of inclination vector derivative + * @param lDot d(M or E or v) + ω + Ω)/dr, mean, eccentric or true longitude argument derivative (rad/s) + * @param type type of longitude argument + * @param frame the frame in which the parameters are defined + * (must be a {@link Frame#isPseudoInertial pseudo-inertial frame}) + * @param date date of the orbital parameters + * @param mu central attraction coefficient (m³/s²) + * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or + * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame} + */ + public EquinoctialOrbit(final double a, final double ex, final double ey, + final double hx, final double hy, final double l, + final double aDot, final double exDot, final double eyDot, + final double hxDot, final double hyDot, final double lDot, + final PositionAngleType type, + final Frame frame, final AbsoluteDate date, final double mu) + throws IllegalArgumentException { + this(a, ex, ey, hx, hy, l, aDot, exDot, eyDot, hxDot, hyDot, lDot, type, type, frame, date, mu); + } + /** Constructor from Cartesian parameters. * *

        The acceleration provided in {@code pvCoordinates} is accessible using @@ -269,9 +303,10 @@ public EquinoctialOrbit(final TimeStampedPVCoordinates pvCoordinates, hy = d * w.getX(); // compute true longitude argument + cachedPositionAngleType = PositionAngleType.TRUE; final double cLv = (pvP.getX() - d * pvP.getZ() * w.getX()) / r; final double sLv = (pvP.getY() - d * pvP.getZ() * w.getY()) / r; - lv = FastMath.atan2(sLv, cLv); + cachedL = FastMath.atan2(sLv, cLv); // compute eccentricity vector final double eSE = Vector3D.dotProduct(pvP, pvV) / FastMath.sqrt(mu * a); @@ -301,15 +336,15 @@ public EquinoctialOrbit(final TimeStampedPVCoordinates pvCoordinates, hxDot = jacobian[3][3] * aX + jacobian[3][4] * aY + jacobian[3][5] * aZ; hyDot = jacobian[4][3] * aX + jacobian[4][4] * aY + jacobian[4][5] * aZ; - // in order to compute true anomaly derivative, we must compute - // mean anomaly derivative including Keplerian motion and convert to true anomaly + // in order to compute true longitude argument derivative, we must compute + // mean longitude argument derivative including Keplerian motion and convert to true longitude argument final double lMDot = getKeplerianMeanMotion() + jacobian[5][3] * aX + jacobian[5][4] * aY + jacobian[5][5] * aZ; final UnivariateDerivative1 exUD = new UnivariateDerivative1(ex, exDot); final UnivariateDerivative1 eyUD = new UnivariateDerivative1(ey, eyDot); final UnivariateDerivative1 lMUD = new UnivariateDerivative1(getLM(), lMDot); - final UnivariateDerivative1 lvUD = FieldEquinoctialOrbit.eccentricToTrue(FieldEquinoctialOrbit.meanToEccentric(lMUD, exUD, eyUD), exUD, eyUD); - lvDot = lvUD.getDerivative(1); + final UnivariateDerivative1 lvUD = FieldEquinoctialLongitudeArgumentUtility.meanToTrue(exUD, eyUD, lMUD); + cachedLDot = lvUD.getFirstDerivative(); } else { // acceleration is either almost zero or NaN, @@ -320,7 +355,7 @@ public EquinoctialOrbit(final TimeStampedPVCoordinates pvCoordinates, eyDot = Double.NaN; hxDot = Double.NaN; hyDot = Double.NaN; - lvDot = Double.NaN; + cachedLDot = Double.NaN; } } @@ -361,102 +396,213 @@ public EquinoctialOrbit(final Orbit op) { hxDot = op.getHxDot(); hy = op.getHy(); hyDot = op.getHyDot(); - lv = op.getLv(); - lvDot = op.getLvDot(); + cachedPositionAngleType = PositionAngleType.TRUE; + cachedL = op.getLv(); + cachedLDot = op.getLvDot(); partialPV = null; } /** {@inheritDoc} */ + @Override public OrbitType getType() { return OrbitType.EQUINOCTIAL; } /** {@inheritDoc} */ + @Override public double getA() { return a; } /** {@inheritDoc} */ + @Override public double getADot() { return aDot; } /** {@inheritDoc} */ + @Override public double getEquinoctialEx() { return ex; } /** {@inheritDoc} */ + @Override public double getEquinoctialExDot() { return exDot; } /** {@inheritDoc} */ + @Override public double getEquinoctialEy() { return ey; } /** {@inheritDoc} */ + @Override public double getEquinoctialEyDot() { return eyDot; } /** {@inheritDoc} */ + @Override public double getHx() { return hx; } /** {@inheritDoc} */ + @Override public double getHxDot() { return hxDot; } /** {@inheritDoc} */ + @Override public double getHy() { return hy; } /** {@inheritDoc} */ + @Override public double getHyDot() { return hyDot; } /** {@inheritDoc} */ + @Override public double getLv() { - return lv; + switch (cachedPositionAngleType) { + case TRUE: + return cachedL; + + case ECCENTRIC: + return EquinoctialLongitudeArgumentUtility.eccentricToTrue(ex, ey, cachedL); + + case MEAN: + return EquinoctialLongitudeArgumentUtility.meanToTrue(ex, ey, cachedL); + + default: + throw new OrekitInternalError(null); + } } /** {@inheritDoc} */ + @Override public double getLvDot() { - return lvDot; + switch (cachedPositionAngleType) { + case ECCENTRIC: + final UnivariateDerivative1 lEUD = new UnivariateDerivative1(cachedL, cachedLDot); + final UnivariateDerivative1 exUD = new UnivariateDerivative1(ex, exDot); + final UnivariateDerivative1 eyUD = new UnivariateDerivative1(ey, eyDot); + final UnivariateDerivative1 lvUD = FieldEquinoctialLongitudeArgumentUtility.eccentricToTrue(exUD, eyUD, + lEUD); + return lvUD.getFirstDerivative(); + + case TRUE: + return cachedLDot; + + case MEAN: + final UnivariateDerivative1 lMUD = new UnivariateDerivative1(cachedL, cachedLDot); + final UnivariateDerivative1 exUD2 = new UnivariateDerivative1(ex, exDot); + final UnivariateDerivative1 eyUD2 = new UnivariateDerivative1(ey, eyDot); + final UnivariateDerivative1 lvUD2 = FieldEquinoctialLongitudeArgumentUtility.meanToTrue(exUD2, + eyUD2, lMUD); + return lvUD2.getFirstDerivative(); + + default: + throw new OrekitInternalError(null); + } } /** {@inheritDoc} */ + @Override public double getLE() { - return trueToEccentric(lv, ex, ey); + switch (cachedPositionAngleType) { + case TRUE: + return EquinoctialLongitudeArgumentUtility.trueToEccentric(ex, ey, cachedL); + + case ECCENTRIC: + return cachedL; + + case MEAN: + return EquinoctialLongitudeArgumentUtility.meanToEccentric(ex, ey, cachedL); + + default: + throw new OrekitInternalError(null); + } } /** {@inheritDoc} */ + @Override public double getLEDot() { - final UnivariateDerivative1 lVUD = new UnivariateDerivative1(lv, lvDot); - final UnivariateDerivative1 exUD = new UnivariateDerivative1(ex, exDot); - final UnivariateDerivative1 eyUD = new UnivariateDerivative1(ey, eyDot); - final UnivariateDerivative1 lEUD = FieldEquinoctialOrbit.trueToEccentric(lVUD, exUD, eyUD); - return lEUD.getDerivative(1); + switch (cachedPositionAngleType) { + case TRUE: + final UnivariateDerivative1 lvUD = new UnivariateDerivative1(cachedL, cachedLDot); + final UnivariateDerivative1 exUD = new UnivariateDerivative1(ex, exDot); + final UnivariateDerivative1 eyUD = new UnivariateDerivative1(ey, eyDot); + final UnivariateDerivative1 lEUD = FieldEquinoctialLongitudeArgumentUtility.trueToEccentric(exUD, eyUD, + lvUD); + return lEUD.getFirstDerivative(); + + case ECCENTRIC: + return cachedLDot; + + case MEAN: + final UnivariateDerivative1 lMUD = new UnivariateDerivative1(cachedL, cachedLDot); + final UnivariateDerivative1 exUD2 = new UnivariateDerivative1(ex, exDot); + final UnivariateDerivative1 eyUD2 = new UnivariateDerivative1(ey, eyDot); + final UnivariateDerivative1 lEUD2 = FieldEquinoctialLongitudeArgumentUtility.meanToEccentric(exUD2, + eyUD2, lMUD); + return lEUD2.getFirstDerivative(); + + default: + throw new OrekitInternalError(null); + } } /** {@inheritDoc} */ + @Override public double getLM() { - return eccentricToMean(trueToEccentric(lv, ex, ey), ex, ey); + switch (cachedPositionAngleType) { + case TRUE: + return EquinoctialLongitudeArgumentUtility.trueToMean(ex, ey, cachedL); + + case MEAN: + return cachedL; + + case ECCENTRIC: + return EquinoctialLongitudeArgumentUtility.eccentricToMean(ex, ey, cachedL); + + default: + throw new OrekitInternalError(null); + } } /** {@inheritDoc} */ + @Override public double getLMDot() { - final UnivariateDerivative1 lVUD = new UnivariateDerivative1(lv, lvDot); - final UnivariateDerivative1 exUD = new UnivariateDerivative1(ex, exDot); - final UnivariateDerivative1 eyUD = new UnivariateDerivative1(ey, eyDot); - final UnivariateDerivative1 lMUD = FieldEquinoctialOrbit.eccentricToMean(FieldEquinoctialOrbit.trueToEccentric(lVUD, exUD, eyUD), exUD, eyUD); - return lMUD.getDerivative(1); + switch (cachedPositionAngleType) { + case TRUE: + final UnivariateDerivative1 lvUD = new UnivariateDerivative1(cachedL, cachedLDot); + final UnivariateDerivative1 exUD = new UnivariateDerivative1(ex, exDot); + final UnivariateDerivative1 eyUD = new UnivariateDerivative1(ey, eyDot); + final UnivariateDerivative1 lMUD = FieldEquinoctialLongitudeArgumentUtility.trueToMean(exUD, eyUD, lvUD); + return lMUD.getFirstDerivative(); + + case MEAN: + return cachedLDot; + + case ECCENTRIC: + final UnivariateDerivative1 lEUD = new UnivariateDerivative1(cachedL, cachedLDot); + final UnivariateDerivative1 exUD2 = new UnivariateDerivative1(ex, exDot); + final UnivariateDerivative1 eyUD2 = new UnivariateDerivative1(ey, eyDot); + final UnivariateDerivative1 lMUD2 = FieldEquinoctialLongitudeArgumentUtility.eccentricToMean(exUD2, + eyUD2, lEUD); + return lMUD2.getFirstDerivative(); + + default: + throw new OrekitInternalError(null); + } } /** Get the longitude argument. @@ -485,12 +631,9 @@ public double getLDot(final PositionAngleType type) { * @param ey second component of the eccentricity vector * @return the true longitude argument */ + @Deprecated public static double eccentricToTrue(final double lE, final double ex, final double ey) { - final double epsilon = FastMath.sqrt(1 - ex * ex - ey * ey); - final SinCos scLE = FastMath.sinCos(lE); - final double num = ex * scLE.sin() - ey * scLE.cos(); - final double den = epsilon + 1 - ex * scLE.cos() - ey * scLE.sin(); - return lE + 2 * FastMath.atan(num / den); + return EquinoctialLongitudeArgumentUtility.eccentricToTrue(ex, ey, lE); } /** Computes the eccentric longitude argument from the true longitude argument. @@ -499,12 +642,9 @@ public static double eccentricToTrue(final double lE, final double ex, final dou * @param ey second component of the eccentricity vector * @return the eccentric longitude argument */ + @Deprecated public static double trueToEccentric(final double lv, final double ex, final double ey) { - final double epsilon = FastMath.sqrt(1 - ex * ex - ey * ey); - final SinCos scLv = FastMath.sinCos(lv); - final double num = ey * scLv.cos() - ex * scLv.sin(); - final double den = epsilon + 1 + ex * scLv.cos() + ey * scLv.sin(); - return lv + 2 * FastMath.atan(num / den); + return EquinoctialLongitudeArgumentUtility.trueToEccentric(ex, ey, lv); } /** Computes the eccentric longitude argument from the mean longitude argument. @@ -513,31 +653,9 @@ public static double trueToEccentric(final double lv, final double ex, final dou * @param ey second component of the eccentricity vector * @return the eccentric longitude argument */ + @Deprecated public static double meanToEccentric(final double lM, final double ex, final double ey) { - // Generalization of Kepler equation to equinoctial parameters - // with lE = PA + RAAN + E and - // lM = PA + RAAN + M = lE - ex.sin(lE) + ey.cos(lE) - double lE = lM; - double shift = 0.0; - double lEmlM = 0.0; - SinCos scLE = FastMath.sinCos(lE); - int iter = 0; - do { - final double f2 = ex * scLE.sin() - ey * scLE.cos(); - final double f1 = 1.0 - ex * scLE.cos() - ey * scLE.sin(); - final double f0 = lEmlM - f2; - - final double f12 = 2.0 * f1; - shift = f0 * f12 / (f1 * f12 - f0 * f2); - - lEmlM -= shift; - lE = lM + lEmlM; - scLE = FastMath.sinCos(lE); - - } while (++iter < 50 && FastMath.abs(shift) > 1.0e-12); - - return lE; - + return EquinoctialLongitudeArgumentUtility.meanToEccentric(ex, ey, lM); } /** Computes the mean longitude argument from the eccentric longitude argument. @@ -546,27 +664,31 @@ public static double meanToEccentric(final double lM, final double ex, final dou * @param ey second component of the eccentricity vector * @return the mean longitude argument */ + @Deprecated public static double eccentricToMean(final double lE, final double ex, final double ey) { - final SinCos scLE = FastMath.sinCos(lE); - return lE - ex * scLE.sin() + ey * scLE.cos(); + return EquinoctialLongitudeArgumentUtility.eccentricToMean(ex, ey, lE); } /** {@inheritDoc} */ + @Override public double getE() { return FastMath.sqrt(ex * ex + ey * ey); } /** {@inheritDoc} */ + @Override public double getEDot() { return (ex * exDot + ey * eyDot) / FastMath.sqrt(ex * ex + ey * ey); } /** {@inheritDoc} */ + @Override public double getI() { return 2 * FastMath.atan(FastMath.sqrt(hx * hx + hy * hy)); } /** {@inheritDoc} */ + @Override public double getIDot() { final double h2 = hx * hx + hy * hy; final double h = FastMath.sqrt(h2); @@ -629,6 +751,95 @@ private void computePVWithoutA() { } + /** Initialize cached argument of longitude with rate. + * @param l input argument of longitude + * @param lDot rate of input argument of longitude + * @param inputType position angle type passed as input + * @return argument of longitude to cache with rate + * @since 12.1 + */ + private UnivariateDerivative1 initializeCachedL(final double l, final double lDot, + final PositionAngleType inputType) { + if (cachedPositionAngleType == inputType) { + return new UnivariateDerivative1(l, lDot); + + } else { + final UnivariateDerivative1 exUD = new UnivariateDerivative1(ex, exDot); + final UnivariateDerivative1 eyUD = new UnivariateDerivative1(ey, eyDot); + final UnivariateDerivative1 lUD = new UnivariateDerivative1(l, lDot); + + switch (cachedPositionAngleType) { + + case ECCENTRIC: + if (inputType == PositionAngleType.MEAN) { + return FieldEquinoctialLongitudeArgumentUtility.meanToEccentric(exUD, eyUD, lUD); + } else { + return FieldEquinoctialLongitudeArgumentUtility.trueToEccentric(exUD, eyUD, lUD); + } + + case TRUE: + if (inputType == PositionAngleType.MEAN) { + return FieldEquinoctialLongitudeArgumentUtility.meanToTrue(exUD, eyUD, lUD); + } else { + return FieldEquinoctialLongitudeArgumentUtility.eccentricToTrue(exUD, eyUD, lUD); + } + + case MEAN: + if (inputType == PositionAngleType.TRUE) { + return FieldEquinoctialLongitudeArgumentUtility.trueToMean(exUD, eyUD, lUD); + } else { + return FieldEquinoctialLongitudeArgumentUtility.eccentricToMean(exUD, eyUD, lUD); + } + + default: + throw new OrekitInternalError(null); + + } + + } + + } + + /** Initialize cached argument of longitude. + * @param l input argument of longitude + * @param positionAngleType position angle type passed as input + * @return argument of longitude to cache + * @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); + } + } + } + /** Compute non-Keplerian part of the acceleration from first time derivatives. *

        * This method should be called only when {@link #hasDerivatives()} returns true. @@ -653,6 +864,7 @@ private Vector3D nonKeplerianAcceleration() { } /** {@inheritDoc} */ + @Override protected Vector3D initPosition() { // get equinoctial parameters @@ -694,6 +906,7 @@ protected Vector3D initPosition() { } /** {@inheritDoc} */ + @Override protected TimeStampedPVCoordinates initPVCoordinates() { // position and velocity @@ -711,12 +924,14 @@ protected TimeStampedPVCoordinates initPVCoordinates() { } /** {@inheritDoc} */ + @Override public EquinoctialOrbit shiftedBy(final double dt) { // use Keplerian-only motion final EquinoctialOrbit keplerianShifted = new EquinoctialOrbit(a, ex, ey, hx, hy, getLM() + getKeplerianMeanMotion() * dt, - PositionAngleType.MEAN, getFrame(), + PositionAngleType.MEAN, cachedPositionAngleType, + getFrame(), getDate().shiftedBy(dt), getMu()); if (hasDerivatives()) { @@ -748,6 +963,7 @@ PositionAngleType.MEAN, getFrame(), } /** {@inheritDoc} */ + @Override protected double[][] computeJacobianMeanWrtCartesian() { final double[][] jacobian = new double[6][6]; @@ -836,6 +1052,7 @@ protected double[][] computeJacobianMeanWrtCartesian() { } /** {@inheritDoc} */ + @Override protected double[][] computeJacobianEccentricWrtCartesian() { // start by computing the Jacobian with mean angle @@ -863,15 +1080,16 @@ protected double[][] computeJacobianEccentricWrtCartesian() { } /** {@inheritDoc} */ + @Override protected double[][] computeJacobianTrueWrtCartesian() { // start by computing the Jacobian with eccentric angle final double[][] jacobian = computeJacobianEccentricWrtCartesian(); // Differentiating the eccentric longitude equation - // tan((lV - lE)/2) = [ex sin lE - ey cos lE] / [sqrt(1-ex^2-ey^2) + 1 - ex cos lE - ey sin lE] + // tan((lv - lE)/2) = [ex sin lE - ey cos lE] / [sqrt(1-ex^2-ey^2) + 1 - ex cos lE - ey sin lE] // leads to - // cT (dlV - dlE) = cE dlE + cX dex + cY dey + // cT (dlv - dlE) = cE dlE + cX dex + cY dey // with // cT = [d^2 + (ex sin lE - ey cos lE)^2] / 2 // d = 1 + sqrt(1-ex^2-ey^2) - ex cos lE - ey sin lE @@ -879,7 +1097,7 @@ protected double[][] computeJacobianTrueWrtCartesian() { // cX = sin lE (sqrt(1-ex^2-ey^2) + 1) - ey + ex (ex sin lE - ey cos lE) / sqrt(1-ex^2-ey^2) // cY = -cos lE (sqrt(1-ex^2-ey^2) + 1) + ex + ey (ex sin lE - ey cos lE) / sqrt(1-ex^2-ey^2) // which can be solved to find the differential of the true longitude - // dlV = (cT + cE) / cT dlE + cX / cT deX + cY / cT deX + // dlv = (cT + cE) / cT dlE + cX / cT deX + cY / cT deX final SinCos scLe = FastMath.sinCos(getLE()); final double cosLe = scLe.cos(); final double sinLe = scLe.sin(); @@ -910,22 +1128,24 @@ protected double[][] computeJacobianTrueWrtCartesian() { } /** {@inheritDoc} */ + @Override public void addKeplerContribution(final PositionAngleType type, final double gm, final double[] pDot) { final double oMe2; final double ksi; final double n = FastMath.sqrt(gm / a) / a; - final SinCos sc = FastMath.sinCos(lv); + final SinCos sc; switch (type) { case MEAN : pDot[5] += n; break; case ECCENTRIC : - oMe2 = 1 - ex * ex - ey * ey; - ksi = 1 + ex * sc.cos() + ey * sc.sin(); - pDot[5] += n * ksi / oMe2; + 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)); @@ -943,14 +1163,14 @@ public String toString() { append("a: ").append(a). append("; ex: ").append(ex).append("; ey: ").append(ey). append("; hx: ").append(hx).append("; hy: ").append(hy). - append("; lv: ").append(FastMath.toDegrees(lv)). + append("; lv: ").append(FastMath.toDegrees(getLv())). append(";}").toString(); } /** {@inheritDoc} */ @Override public PositionAngleType getCachedPositionAngleType() { - return PositionAngleType.TRUE; + return cachedPositionAngleType; } /** {@inheritDoc} */ @@ -980,20 +1200,24 @@ private Object writeReplace() { private static class DTO implements Serializable { /** Serializable UID. */ - private static final long serialVersionUID = 20170414L; + private static final long serialVersionUID = 20231217L; /** Double values. */ - private double[] d; + private final double[] d; /** Frame in which are defined the orbital parameters. */ private final Frame frame; + /** Cached type of position angle. */ + private final PositionAngleType positionAngleType; + /** Simple constructor. * @param orbit instance to serialize */ private DTO(final EquinoctialOrbit orbit) { final TimeStampedPVCoordinates pv = orbit.getPVCoordinates(); + positionAngleType = orbit.cachedPositionAngleType; // decompose date final AbsoluteDate j2000Epoch = @@ -1006,16 +1230,16 @@ private DTO(final EquinoctialOrbit orbit) { this.d = new double[] { epoch, offset, orbit.getMu(), orbit.a, orbit.ex, orbit.ey, - orbit.hx, orbit.hy, orbit.lv, + orbit.hx, orbit.hy, orbit.cachedL, orbit.aDot, orbit.exDot, orbit.eyDot, - orbit.hxDot, orbit.hyDot, orbit.lvDot + orbit.hxDot, orbit.hyDot, orbit.cachedLDot }; } else { // we don't have derivatives this.d = new double[] { epoch, offset, orbit.getMu(), orbit.a, orbit.ex, orbit.ey, - orbit.hx, orbit.hy, orbit.lv + orbit.hx, orbit.hy, orbit.cachedL }; } @@ -1033,12 +1257,12 @@ private Object readResolve() { // we have derivatives return new EquinoctialOrbit(d[ 3], d[ 4], d[ 5], d[ 6], d[ 7], d[ 8], d[ 9], d[10], d[11], d[12], d[13], d[14], - PositionAngleType.TRUE, + positionAngleType, frame, j2000Epoch.shiftedBy(d[0]).shiftedBy(d[1]), d[2]); } else { // we don't have derivatives - return new EquinoctialOrbit(d[ 3], d[ 4], d[ 5], d[ 6], d[ 7], d[ 8], PositionAngleType.TRUE, + return new EquinoctialOrbit(d[ 3], d[ 4], d[ 5], d[ 6], d[ 7], d[ 8], positionAngleType, frame, j2000Epoch.shiftedBy(d[0]).shiftedBy(d[1]), d[2]); } diff --git a/src/main/java/org/orekit/orbits/FieldCartesianOrbit.java b/src/main/java/org/orekit/orbits/FieldCartesianOrbit.java index f1b4531f25..3647b36676 100644 --- a/src/main/java/org/orekit/orbits/FieldCartesianOrbit.java +++ b/src/main/java/org/orekit/orbits/FieldCartesianOrbit.java @@ -22,12 +22,8 @@ import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative2; -import org.hipparchus.geometry.euclidean.threed.FieldRotation; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; -import org.hipparchus.geometry.euclidean.threed.RotationConvention; import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.hipparchus.util.FastMath; -import org.hipparchus.util.FieldSinCos; import org.hipparchus.util.MathArrays; import org.orekit.frames.Frame; import org.orekit.time.AbsoluteDate; @@ -147,7 +143,7 @@ public FieldCartesianOrbit(final FieldOrbit op) { */ public FieldCartesianOrbit(final Field field, final CartesianOrbit op) { super(new TimeStampedFieldPVCoordinates<>(field, op.getPVCoordinates()), op.getFrame(), - field.getZero().add(op.getMu())); + field.getZero().newInstance(op.getMu())); hasNonKeplerianAcceleration = op.hasDerivatives(); if (op.isElliptical()) { equinoctial = new FieldEquinoctialOrbit<>(field, new EquinoctialOrbit(op)); @@ -240,7 +236,7 @@ public T getE() { final T rV2OnMu = pvP.getNorm().multiply(pvV.getNormSq()).divide(getMu()); final T eSE = FieldVector3D.dotProduct(pvP, pvV).divide(muA.sqrt()); final T eCE = rV2OnMu.subtract(1); - return (eCE.multiply(eCE).add(eSE.multiply(eSE))).sqrt(); + return (eCE.square().add(eSE.square())).sqrt(); } else { // hyperbolic orbit final FieldVector3D pvM = getPVCoordinates().getMomentum(); @@ -258,7 +254,7 @@ public T getEDot() { final FieldUnivariateDerivative2 a = r.divide(rV2OnMu.negate().add(2)); final FieldUnivariateDerivative2 eSE = FieldVector3D.dotProduct(pv.getPosition(), pv.getVelocity()).divide(a.multiply(getMu()).sqrt()); final FieldUnivariateDerivative2 eCE = rV2OnMu.subtract(1); - final FieldUnivariateDerivative2 e = eCE.multiply(eCE).add(eSE.multiply(eSE)).sqrt(); + final FieldUnivariateDerivative2 e = eCE.square().add(eSE.square()).sqrt(); return e.getDerivative(1); } else { return null; @@ -428,74 +424,38 @@ protected TimeStampedFieldPVCoordinates initPVCoordinates() { /** {@inheritDoc} */ public FieldCartesianOrbit shiftedBy(final double dt) { - return shiftedBy(getZero().add(dt)); + return shiftedBy(getZero().newInstance(dt)); } /** {@inheritDoc} */ public FieldCartesianOrbit shiftedBy(final T dt) { - final FieldPVCoordinates shiftedPV = isElliptical() ? shiftPVElliptic(dt) : shiftPVHyperbolic(dt); + final FieldPVCoordinates shiftedPV = shiftPV(dt); return new FieldCartesianOrbit<>(shiftedPV, getFrame(), getDate().shiftedBy(dt), getMu()); } - /** Compute shifted position and velocity in elliptic case. + /** Compute shifted position and velocity. * @param dt time shift * @return shifted position and velocity */ - private FieldPVCoordinates shiftPVElliptic(final T dt) { + private FieldPVCoordinates shiftPV(final T dt) { + final FieldPVCoordinates pvCoordinates = getPVCoordinates(); + final FieldPVCoordinates shiftedPV = KeplerianMotionCartesianUtility.predictPositionVelocity(dt, + pvCoordinates.getPosition(), pvCoordinates.getVelocity(), getMu()); - // preliminary computation0 - final FieldPVCoordinates pva = getPVCoordinates(); - final FieldVector3D pvP = pva.getPosition(); - final FieldVector3D pvV = pva.getVelocity(); - final FieldVector3D pvM = pva.getMomentum(); - final T r2 = pvP.getNormSq(); - final T r = r2.sqrt(); - final T rV2OnMu = r.multiply(pvV.getNormSq()).divide(getMu()); - final T a = r.divide(rV2OnMu.negate().add(2)); - final T muA = getMu().multiply(a); - - // compute mean anomaly - final T eSE = FieldVector3D.dotProduct(pvP, pvV).divide(muA.sqrt()); - final T eCE = rV2OnMu.subtract(1); - final T E0 = FastMath.atan2(eSE, eCE); - final T M0 = E0.subtract(eSE); - - final T e = eCE.multiply(eCE).add(eSE.multiply(eSE)).sqrt(); - final T sqrt = e.add(1).divide(e.negate().add(1)).sqrt(); - - // find canonical 2D frame with p pointing to perigee - final T v0 = sqrt.multiply(FastMath.tan(E0.divide(2))).atan().multiply(2); - final FieldVector3D p = new FieldRotation<>(pvM, v0, RotationConvention.FRAME_TRANSFORM).applyTo(pvP).normalize(); - final FieldVector3D q = FieldVector3D.crossProduct(pvM, p).normalize(); - - // compute shifted eccentric anomaly - final T M1 = M0.add(getKeplerianMeanMotion().multiply(dt)); - final T E1 = FieldKeplerianAnomalyUtility.ellipticMeanToEccentric(e, M1); - - // compute shifted in-plane Cartesian coordinates - final FieldSinCos scE = FastMath.sinCos(E1); - final T cE = scE.cos(); - final T sE = scE.sin(); - final T sE2m1 = e.negate().add(1).multiply(e.add(1)).sqrt(); - - // coordinates of position and velocity in the orbital plane - final T x = a.multiply(cE.subtract(e)); - final T y = a.multiply(sE2m1).multiply(sE); - final T factor = a.reciprocal().multiply(getMu()).sqrt().divide(e.multiply(cE).negate().add(1)); - final T xDot = factor.multiply(sE).negate(); - final T yDot = factor.multiply(sE2m1).multiply(cE); - - final FieldVector3D shiftedP = new FieldVector3D<>(x, p, y, q); - final FieldVector3D shiftedV = new FieldVector3D<>(xDot, p, yDot, q); if (hasNonKeplerianAcceleration) { + final FieldVector3D pvP = getPosition(); + final T r2 = pvP.getNormSq(); + final T r = r2.sqrt(); // extract non-Keplerian part of the initial acceleration final FieldVector3D nonKeplerianAcceleration = new FieldVector3D<>(getOne(), getPVCoordinates().getAcceleration(), r.multiply(r2).reciprocal().multiply(getMu()), pvP); // add the quadratic motion due to the non-Keplerian acceleration to the Keplerian motion + final FieldVector3D shiftedP = shiftedPV.getPosition(); + final FieldVector3D shiftedV = shiftedPV.getVelocity(); final FieldVector3D fixedP = new FieldVector3D<>(getOne(), shiftedP, - dt.multiply(dt).multiply(0.5), nonKeplerianAcceleration); + dt.square().multiply(0.5), nonKeplerianAcceleration); final T fixedR2 = fixedP.getNormSq(); final T fixedR = fixedR2.sqrt(); final FieldVector3D fixedV = new FieldVector3D<>(getOne(), shiftedV, @@ -508,82 +468,8 @@ private FieldPVCoordinates shiftPVElliptic(final T dt) { } else { // don't include acceleration, // so the shifted orbit is not considered to have derivatives - return new FieldPVCoordinates<>(shiftedP, shiftedV); + return shiftedPV; } - - } - - /** Compute shifted position and velocity in hyperbolic case. - * @param dt time shift - * @return shifted position and velocity - */ - private FieldPVCoordinates shiftPVHyperbolic(final T dt) { - - final FieldPVCoordinates pv = getPVCoordinates(); - final FieldVector3D pvP = pv.getPosition(); - final FieldVector3D pvV = pv.getVelocity(); - final FieldVector3D pvM = pv.getMomentum(); - final T r2 = pvP.getNormSq(); - final T r = r2.sqrt(); - final T rV2OnMu = r.multiply(pvV.getNormSq()).divide(getMu()); - final T a = getA(); - final T muA = a.multiply(getMu()); - final T e = getOne().subtract(FieldVector3D.dotProduct(pvM, pvM).divide(muA)).sqrt(); - final T sqrt = e.add(1).divide(e.subtract(1)).sqrt(); - - // compute mean anomaly - final T eSH = FieldVector3D.dotProduct(pvP, pvV).divide(muA.negate().sqrt()); - final T eCH = rV2OnMu.subtract(1); - final T H0 = eCH.add(eSH).divide(eCH.subtract(eSH)).log().divide(2); - final T M0 = e.multiply(H0.sinh()).subtract(H0); - - // find canonical 2D frame with p pointing to perigee - final T v0 = sqrt.multiply(H0.divide(2).tanh()).atan().multiply(2); - final FieldVector3D p = new FieldRotation<>(pvM, v0, RotationConvention.FRAME_TRANSFORM).applyTo(pvP).normalize(); - final FieldVector3D q = FieldVector3D.crossProduct(pvM, p).normalize(); - - // compute shifted eccentric anomaly - final T M1 = M0.add(getKeplerianMeanMotion().multiply(dt)); - final T H1 = FieldKeplerianAnomalyUtility.hyperbolicMeanToEccentric(e, M1); - - // compute shifted in-plane Cartesian coordinates - final T cH = H1.cosh(); - final T sH = H1.sinh(); - final T sE2m1 = e.subtract(1).multiply(e.add(1)).sqrt(); - - // coordinates of position and velocity in the orbital plane - final T x = a.multiply(cH.subtract(e)); - final T y = a.negate().multiply(sE2m1).multiply(sH); - final T factor = getMu().divide(a.negate()).sqrt().divide(e.multiply(cH).subtract(1)); - final T xDot = factor.negate().multiply(sH); - final T yDot = factor.multiply(sE2m1).multiply(cH); - - final FieldVector3D shiftedP = new FieldVector3D<>(x, p, y, q); - final FieldVector3D shiftedV = new FieldVector3D<>(xDot, p, yDot, q); - if (hasNonKeplerianAcceleration) { - - // extract non-Keplerian part of the initial acceleration - final FieldVector3D nonKeplerianAcceleration = new FieldVector3D<>(getOne(), getPVCoordinates().getAcceleration(), - r.multiply(r2).reciprocal().multiply(getMu()), pvP); - - // add the quadratic motion due to the non-Keplerian acceleration to the Keplerian motion - final FieldVector3D fixedP = new FieldVector3D<>(getOne(), shiftedP, - dt.multiply(dt).multiply(0.5), nonKeplerianAcceleration); - final T fixedR2 = fixedP.getNormSq(); - final T fixedR = fixedR2.sqrt(); - final FieldVector3D fixedV = new FieldVector3D<>(getOne(), shiftedV, - dt, nonKeplerianAcceleration); - final FieldVector3D fixedA = new FieldVector3D<>(fixedR.multiply(fixedR2).reciprocal().multiply(getMu().negate()), shiftedP, - getOne(), nonKeplerianAcceleration); - - return new FieldPVCoordinates<>(fixedP, fixedV, fixedA); - - } else { - // don't include acceleration, - // so the shifted orbit is not considered to have derivatives - return new FieldPVCoordinates<>(shiftedP, shiftedV); - } - } /** Create a 6x6 identity matrix. diff --git a/src/main/java/org/orekit/orbits/FieldCircularLatitudeArgumentUtility.java b/src/main/java/org/orekit/orbits/FieldCircularLatitudeArgumentUtility.java new file mode 100644 index 0000000000..b98dc91885 --- /dev/null +++ b/src/main/java/org/orekit/orbits/FieldCircularLatitudeArgumentUtility.java @@ -0,0 +1,189 @@ +/* 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.orbits; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.FieldSinCos; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; + +/** + * Utility methods for converting between different latitude arguments used by {@link FieldCircularOrbit}. + * @author Romain Serra + * @see FieldCircularOrbit + * @since 12.1 + */ +public class FieldCircularLatitudeArgumentUtility { + + /** Tolerance for stopping criterion in iterative conversion from mean to eccentric angle. */ + private static final double TOLERANCE_CONVERGENCE = 1.0e-12; + + /** Maximum number of iterations in iterative conversion from mean to eccentric angle. */ + private static final int MAXIMUM_ITERATION = 50; + + /** Private constructor for utility class. */ + private FieldCircularLatitudeArgumentUtility() { + // nothing here (utils class) + } + + /** + * Computes the true latitude argument from the eccentric latitude argument. + * + * @param Type of the field elements + * @param ex e cos(ω), first component of circular eccentricity vector + * @param ey e sin(ω), second component of circular eccentricity vector + * @param alphaE = E + ω eccentric latitude argument (rad) + * @return the true latitude argument. + */ + public static > T eccentricToTrue(final T ex, final T ey, final T alphaE) { + final T epsilon = eccentricAndTrueEpsilon(ex, ey); + final FieldSinCos scAlphaE = FastMath.sinCos(alphaE); + final T cosAlphaE = scAlphaE.cos(); + final T sinAlphaE = scAlphaE.sin(); + final T num = ex.multiply(sinAlphaE).subtract(ey.multiply(cosAlphaE)); + final T den = epsilon.add(1).subtract(ex.multiply(cosAlphaE)).subtract(ey.multiply(sinAlphaE)); + return alphaE.add(eccentricAndTrueAtan(num, den)); + } + + /** + * Computes the eccentric latitude argument from the true latitude argument. + * + * @param Type of the field elements + * @param ex e cos(ω), first component of circular eccentricity vector + * @param ey e sin(ω), second component of circular eccentricity vector + * @param alphaV = v + ω true latitude argument (rad) + * @return the eccentric latitude argument. + */ + public static > T trueToEccentric(final T ex, final T ey, final T alphaV) { + final T epsilon = eccentricAndTrueEpsilon(ex, ey); + final FieldSinCos scAlphaV = FastMath.sinCos(alphaV); + final T cosAlphaV = scAlphaV.cos(); + final T sinAlphaV = scAlphaV.sin(); + final T num = ey.multiply(cosAlphaV).subtract(ex.multiply(sinAlphaV)); + final T den = epsilon.add(1).add(ex.multiply(cosAlphaV).add(ey.multiply(sinAlphaV))); + return alphaV.add(eccentricAndTrueAtan(num, den)); + } + + /** + * Computes an intermediate quantity for conversions between true and eccentric. + * + * @param Type of the field elements + * @param ex e cos(ω), first component of circular eccentricity vector + * @param ey e sin(ω), second component of circular eccentricity vector + * @return intermediate variable referred to as epsilon. + */ + private static > T eccentricAndTrueEpsilon(final T ex, final T ey) { + return (ex.square().negate().subtract(ey.square()).add(1.)).sqrt(); + } + + /** + * Computes another intermediate quantity for conversions between true and eccentric. + * + * @param Type of the field elements + * @param num numerator for angular conversion + * @param den denominator for angular conversion + * @return arc-tangent of ratio of inputs times two. + */ + private static > T eccentricAndTrueAtan(final T num, final T den) { + return (num.divide(den)).atan().multiply(2); + } + + /** + * Computes the eccentric latitude argument from the mean latitude argument. + * + * @param Type of the field elements + * @param ex e cos(ω), first component of circular eccentricity vector + * @param ey e sin(ω), second component of circular eccentricity vector + * @param alphaM = M + ω mean latitude argument (rad) + * @return the eccentric latitude argument. + */ + public static > T meanToEccentric(final T ex, final T ey, final T alphaM) { + // Generalization of Kepler equation to circular parameters + // with alphaE = PA + E and + // alphaM = PA + M = alphaE - ex.sin(alphaE) + ey.cos(alphaE) + + T alphaE = alphaM; + T shift; + T alphaEMalphaM = alphaM.getField().getZero(); + boolean hasConverged; + int iter = 0; + do { + final FieldSinCos scAlphaE = FastMath.sinCos(alphaE); + final T f2 = ex.multiply(scAlphaE.sin()).subtract(ey.multiply(scAlphaE.cos())); + final T f1 = ex.negate().multiply(scAlphaE.cos()).subtract(ey.multiply(scAlphaE.sin())).add(1); + final T f0 = alphaEMalphaM.subtract(f2); + + final T f12 = f1.multiply(2); + shift = f0.multiply(f12).divide(f1.multiply(f12).subtract(f0.multiply(f2))); + + alphaEMalphaM = alphaEMalphaM.subtract(shift); + alphaE = alphaM.add(alphaEMalphaM); + + hasConverged = FastMath.abs(shift.getReal()) <= TOLERANCE_CONVERGENCE; + } while (++iter < MAXIMUM_ITERATION && !hasConverged); + + if (!hasConverged) { + throw new OrekitException(OrekitMessages.UNABLE_TO_COMPUTE_ECCENTRIC_LATITUDE_ARGUMENT, iter); + } + return alphaE; + + } + + /** + * Computes the mean latitude argument from the eccentric latitude argument. + * + * @param Type of the field elements + * @param ex e cos(ω), first component of circular eccentricity vector + * @param ey e sin(ω), second component of circular eccentricity vector + * @param alphaE = E + ω eccentric latitude argument (rad) + * @return the mean latitude argument. + */ + public static > T eccentricToMean(final T ex, final T ey, final T alphaE) { + final FieldSinCos scAlphaE = FastMath.sinCos(alphaE); + return alphaE.subtract(ex.multiply(scAlphaE.sin()).subtract(ey.multiply(scAlphaE.cos()))); + } + + /** + * Computes the mean latitude argument from the eccentric latitude argument. + * + * @param Type of the field elements + * @param ex e cos(ω), first component of circular eccentricity vector + * @param ey e sin(ω), second component of circular eccentricity vector + * @param alphaV = V + ω true latitude argument (rad) + * @return the mean latitude argument. + */ + public static > T trueToMean(final T ex, final T ey, final T alphaV) { + final T alphaE = trueToEccentric(ex, ey, alphaV); + return eccentricToMean(ex, ey, alphaE); + } + + /** + * Computes the true latitude argument from the eccentric latitude argument. + * + * @param Type of the field elements + * @param ex e cos(ω), first component of circular eccentricity vector + * @param ey e sin(ω), second component of circular eccentricity vector + * @param alphaM = M + ω mean latitude argument (rad) + * @return the true latitude argument. + */ + public static > T meanToTrue(final T ex, final T ey, final T alphaM) { + final T alphaE = meanToEccentric(ex, ey, alphaM); + return eccentricToTrue(ex, ey, alphaE); + } + +} diff --git a/src/main/java/org/orekit/orbits/FieldCircularOrbit.java b/src/main/java/org/orekit/orbits/FieldCircularOrbit.java index 45d094a135..2d123e407c 100644 --- a/src/main/java/org/orekit/orbits/FieldCircularOrbit.java +++ b/src/main/java/org/orekit/orbits/FieldCircularOrbit.java @@ -89,8 +89,11 @@ public class FieldCircularOrbit> extends Field /** Right Ascension of Ascending Node (rad). */ private final T raan; - /** True latitude argument (rad). */ - private final T alphaV; + /** Cached latitude argument (rad). */ + private final T cachedAlpha; + + /** Type of cached position angle (latitude argument). */ + private final PositionAngleType cachedPositionAngleType; /** Semi-major axis derivative (m/s). */ private final T aDot; @@ -108,7 +111,7 @@ public class FieldCircularOrbit> extends Field private final T raanDot; /** True latitude argument derivative (rad/s). */ - private final T alphaVDot; + private final T cachedAlphaDot; /** Partial Cartesian coordinates (position and velocity are valid, acceleration may be missing). */ private FieldPVCoordinates partialPV; @@ -121,21 +124,45 @@ public class FieldCircularOrbit> extends Field * @param raan right ascension of ascending node (Ω, rad) * @param alpha an + ω, mean, eccentric or true latitude argument (rad) * @param type type of latitude argument + * @param cachedPositionAngleType type of cached latitude argument * @param frame the frame in which are defined the parameters * (must be a {@link Frame#isPseudoInertial pseudo-inertial frame}) * @param date date of the orbital parameters * @param mu central attraction coefficient (m³/s²) * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame} + * @since 12.1 */ - public FieldCircularOrbit(final T a, final T ex, final T ey, - final T i, final T raan, + public FieldCircularOrbit(final T a, final T ex, final T ey, final T i, final T raan, final T alpha, final PositionAngleType type, + final PositionAngleType cachedPositionAngleType, final Frame frame, final FieldAbsoluteDate date, final T mu) throws IllegalArgumentException { this(a, ex, ey, i, raan, alpha, null, null, null, null, null, null, - type, frame, date, mu); + type, cachedPositionAngleType, frame, date, mu); + } + + /** Creates a new instance without derivatives and with cached position angle same as value inputted. + * @param a semi-major axis (m) + * @param ex e cos(ω), first component of circular eccentricity vector + * @param ey e sin(ω), second component of circular eccentricity vector + * @param i inclination (rad) + * @param raan right ascension of ascending node (Ω, rad) + * @param alpha an + ω, mean, eccentric or true latitude argument (rad) + * @param type type of latitude argument + * @param frame the frame in which are defined the parameters + * (must be a {@link Frame#isPseudoInertial pseudo-inertial frame}) + * @param date date of the orbital parameters + * @param mu central attraction coefficient (m³/s²) + * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or + * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame} + */ + public FieldCircularOrbit(final T a, final T ex, final T ey, final T i, final T raan, + final T alpha, final PositionAngleType type, + final Frame frame, final FieldAbsoluteDate date, final T mu) + throws IllegalArgumentException { + this(a, ex, ey, i, raan, alpha, type, type, frame, date, mu); } /** Creates a new instance. @@ -159,11 +186,43 @@ public FieldCircularOrbit(final T a, final T ex, final T ey, * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame} */ + public FieldCircularOrbit(final T a, final T ex, final T ey, + final T i, final T raan, final T alpha, + final T aDot, final T exDot, final T eyDot, + final T iDot, final T raanDot, final T alphaDot, final PositionAngleType type, + final Frame frame, final FieldAbsoluteDate date, final T mu) + throws IllegalArgumentException { + this(a, ex, ey, i, raan, alpha, aDot, exDot, eyDot, iDot, raanDot, alphaDot, type, type, frame, date, mu); + } + + /** Creates a new instance. + * @param a semi-major axis (m) + * @param ex e cos(ω), first component of circular eccentricity vector + * @param ey e sin(ω), second component of circular eccentricity vector + * @param i inclination (rad) + * @param raan right ascension of ascending node (Ω, rad) + * @param alpha an + ω, mean, eccentric or true latitude argument (rad) + * @param aDot semi-major axis derivative (m/s) + * @param exDot d(e cos(ω))/dt, first component of circular eccentricity vector derivative + * @param eyDot d(e sin(ω))/dt, second component of circular eccentricity vector derivative + * @param iDot inclination derivative(rad/s) + * @param raanDot right ascension of ascending node derivative (rad/s) + * @param alphaDot d(an + ω), mean, eccentric or true latitude argument derivative (rad/s) + * @param type type of latitude argument + * @param cachedPositionAngleType type of cached latitude argument + * @param frame the frame in which are defined the parameters + * (must be a {@link Frame#isPseudoInertial pseudo-inertial frame}) + * @param date date of the orbital parameters + * @param mu central attraction coefficient (m³/s²) + * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or + * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame} + * @since 12.1 + */ public FieldCircularOrbit(final T a, final T ex, final T ey, final T i, final T raan, final T alpha, final T aDot, final T exDot, final T eyDot, final T iDot, final T raanDot, final T alphaDot, - final PositionAngleType type, + final PositionAngleType type, final PositionAngleType cachedPositionAngleType, final Frame frame, final FieldAbsoluteDate date, final T mu) throws IllegalArgumentException { super(frame, date, mu); @@ -182,45 +241,18 @@ public FieldCircularOrbit(final T a, final T ex, final T ey, this.iDot = iDot; this.raan = raan; this.raanDot = raanDot; + this.cachedPositionAngleType = cachedPositionAngleType; if (hasDerivatives()) { - final FieldUnivariateDerivative1 exUD = new FieldUnivariateDerivative1<>(ex, exDot); - final FieldUnivariateDerivative1 eyUD = new FieldUnivariateDerivative1<>(ey, eyDot); - final FieldUnivariateDerivative1 alphaUD = new FieldUnivariateDerivative1<>(alpha, alphaDot); - final FieldUnivariateDerivative1 alphavUD; - switch (type) { - case MEAN : - alphavUD = eccentricToTrue(meanToEccentric(alphaUD, exUD, eyUD), exUD, eyUD); - break; - case ECCENTRIC : - alphavUD = eccentricToTrue(alphaUD, exUD, eyUD); - break; - case TRUE : - alphavUD = alphaUD; - break; - default : - throw new OrekitInternalError(null); - } - this.alphaV = alphavUD.getValue(); - this.alphaVDot = alphavUD.getDerivative(1); + final FieldUnivariateDerivative1 alphaUD = initializeCachedAlpha(alpha, alphaDot, type); + this.cachedAlpha = alphaUD.getValue(); + this.cachedAlphaDot = alphaUD.getFirstDerivative(); } else { - switch (type) { - case MEAN : - this.alphaV = eccentricToTrue(meanToEccentric(alpha, ex, ey), ex, ey); - break; - case ECCENTRIC : - this.alphaV = eccentricToTrue(alpha, ex, ey); - break; - case TRUE : - this.alphaV = alpha; - break; - default : - throw new OrekitInternalError(null); - } - this.alphaVDot = null; + this.cachedAlpha = initializeCachedAlpha(alpha, type); + this.cachedAlphaDot = null; } - this.partialPV = null; + partialPV = null; } @@ -242,6 +274,7 @@ public FieldCircularOrbit(final TimeStampedFieldPVCoordinates pvCoordinates, final Frame frame, final T mu) throws IllegalArgumentException { super(pvCoordinates, frame, mu); + this.cachedPositionAngleType = PositionAngleType.TRUE; // compute semi-major axis final FieldVector3D pvP = pvCoordinates.getPosition(); @@ -284,14 +317,14 @@ public FieldCircularOrbit(final TimeStampedFieldPVCoordinates pvCoordinates, final T f = eCE.subtract(e2); final T g = eSE.multiply(e2.negate().add(1).sqrt()); final T aOnR = a.divide(r); - final T a2OnR2 = aOnR.multiply(aOnR); + final T a2OnR2 = aOnR.square(); ex = a2OnR2.multiply(f.multiply(x2).add(g.multiply(y2))); ey = a2OnR2.multiply(f.multiply(y2).subtract(g.multiply(x2))); // compute latitude argument final T beta = (ex.multiply(ex).add(ey.multiply(ey)).negate().add(1)).sqrt().add(1).reciprocal(); - alphaV = eccentricToTrue(y2.add(ey).add(eSE.multiply(beta).multiply(ex)).atan2(x2.add(ex).subtract(eSE.multiply(beta).multiply(ey))), - ex, ey); + cachedAlpha = FieldCircularLatitudeArgumentUtility.eccentricToTrue(ex, ey, y2.add(ey).add(eSE.multiply(beta).multiply(ex)).atan2(x2.add(ex).subtract(eSE.multiply(beta).multiply(ey))) + ); partialPV = pvCoordinates; @@ -319,8 +352,8 @@ public FieldCircularOrbit(final TimeStampedFieldPVCoordinates pvCoordinates, final FieldUnivariateDerivative1 exUD = new FieldUnivariateDerivative1<>(ex, exDot); final FieldUnivariateDerivative1 eyUD = new FieldUnivariateDerivative1<>(ey, eyDot); final FieldUnivariateDerivative1 alphaMUD = new FieldUnivariateDerivative1<>(getAlphaM(), alphaMDot); - final FieldUnivariateDerivative1 alphavUD = eccentricToTrue(meanToEccentric(alphaMUD, exUD, eyUD), exUD, eyUD); - alphaVDot = alphavUD.getDerivative(1); + final FieldUnivariateDerivative1 alphavUD = FieldCircularLatitudeArgumentUtility.meanToTrue(exUD, eyUD, alphaMUD); + cachedAlphaDot = alphavUD.getFirstDerivative(); } else { // acceleration is either almost zero or NaN, @@ -331,7 +364,7 @@ public FieldCircularOrbit(final TimeStampedFieldPVCoordinates pvCoordinates, eyDot = null; iDot = null; raanDot = null; - alphaVDot = null; + cachedAlphaDot = null; } } @@ -366,7 +399,7 @@ public FieldCircularOrbit(final FieldOrbit op) { i = op.getI(); final T hx = op.getHx(); final T hy = op.getHy(); - final T h2 = hx.multiply(hx).add(hy.multiply(hy)); + final T h2 = hx.square().add(hy.square()); final T h = h2.sqrt(); raan = hy.atan2(hx); final FieldSinCos scRaan = FastMath.sinCos(raan); @@ -376,7 +409,8 @@ public FieldCircularOrbit(final FieldOrbit op) { final T equiEy = op.getEquinoctialEy(); ex = equiEx.multiply(cosRaan).add(equiEy.multiply(sinRaan)); ey = equiEy.multiply(cosRaan).subtract(equiEx.multiply(sinRaan)); - alphaV = op.getLv().subtract(raan); + cachedPositionAngleType = PositionAngleType.TRUE; + cachedAlpha = op.getLv().subtract(raan); if (op.hasDerivatives()) { aDot = op.getADot(); @@ -390,14 +424,14 @@ public FieldCircularOrbit(final FieldOrbit op) { add(equiEyDot.subtract(equiEx.multiply(raanDot)).multiply(sinRaan)); eyDot = equiEyDot.subtract(equiEx.multiply(raanDot)).multiply(cosRaan). subtract(equiExDot.add(equiEy.multiply(raanDot)).multiply(sinRaan)); - alphaVDot = op.getLvDot().subtract(raanDot); + cachedAlphaDot = op.getLvDot().subtract(raanDot); } else { aDot = null; exDot = null; eyDot = null; iDot = null; raanDot = null; - alphaVDot = null; + cachedAlphaDot = null; } partialPV = null; @@ -411,29 +445,30 @@ public FieldCircularOrbit(final FieldOrbit op) { * @since 12.0 */ public FieldCircularOrbit(final Field field, final CircularOrbit op) { - super(op.getFrame(), new FieldAbsoluteDate<>(field, op.getDate()), field.getZero().add(op.getMu())); + super(op.getFrame(), new FieldAbsoluteDate<>(field, op.getDate()), field.getZero().newInstance(op.getMu())); - a = getZero().add(op.getA()); - i = getZero().add(op.getI()); - raan = getZero().add(op.getRightAscensionOfAscendingNode()); - ex = getZero().add(op.getCircularEx()); - ey = getZero().add(op.getCircularEy()); - alphaV = getZero().add(op.getAlphaV()); + a = getZero().newInstance(op.getA()); + i = getZero().newInstance(op.getI()); + raan = getZero().newInstance(op.getRightAscensionOfAscendingNode()); + ex = getZero().newInstance(op.getCircularEx()); + ey = getZero().newInstance(op.getCircularEy()); + cachedPositionAngleType = op.getCachedPositionAngleType(); + cachedAlpha = getZero().newInstance(op.getAlpha(cachedPositionAngleType)); if (op.hasDerivatives()) { - aDot = getZero().add(op.getADot()); - iDot = getZero().add(op.getIDot()); - raanDot = getZero().add(op.getRightAscensionOfAscendingNodeDot()); - exDot = getZero().add(op.getCircularExDot()); - eyDot = getZero().add(op.getCircularEyDot()); - alphaVDot = getZero().add(op.getAlphaVDot()); + aDot = getZero().newInstance(op.getADot()); + iDot = getZero().newInstance(op.getIDot()); + raanDot = getZero().newInstance(op.getRightAscensionOfAscendingNodeDot()); + exDot = getZero().newInstance(op.getCircularExDot()); + eyDot = getZero().newInstance(op.getCircularEyDot()); + cachedAlphaDot = getZero().newInstance(op.getAlphaDot(cachedPositionAngleType)); } else { aDot = null; exDot = null; eyDot = null; iDot = null; raanDot = null; - alphaVDot = null; + cachedAlphaDot = null; } partialPV = null; @@ -447,31 +482,36 @@ public FieldCircularOrbit(final Field field, final CircularOrbit op) { * @since 12.0 */ public FieldCircularOrbit(final Field field, final Orbit op) { - this(field, new CircularOrbit(op)); + this(field, (CircularOrbit) OrbitType.CIRCULAR.convertType(op)); } /** {@inheritDoc} */ + @Override public OrbitType getType() { return OrbitType.CIRCULAR; } /** {@inheritDoc} */ + @Override public T getA() { return a; } /** {@inheritDoc} */ + @Override public T getADot() { return aDot; } /** {@inheritDoc} */ + @Override public T getEquinoctialEx() { final FieldSinCos sc = FastMath.sinCos(raan); return ex.multiply(sc.cos()).subtract(ey.multiply(sc.sin())); } /** {@inheritDoc} */ + @Override public T getEquinoctialExDot() { if (!hasDerivatives()) { @@ -485,12 +525,14 @@ public T getEquinoctialExDot() { } /** {@inheritDoc} */ + @Override public T getEquinoctialEy() { final FieldSinCos sc = FastMath.sinCos(raan); return ey.multiply(sc.cos()).add(ex.multiply(sc.sin())); } /** {@inheritDoc} */ + @Override public T getEquinoctialEyDot() { if (!hasDerivatives()) { @@ -532,6 +574,7 @@ public T getCircularEyDot() { } /** {@inheritDoc} */ + @Override public T getHx() { // Check for equatorial retrograde orbit if (FastMath.abs(i.subtract(i.getPi()).getReal()) < 1.0e-10) { @@ -541,6 +584,7 @@ public T getHx() { } /** {@inheritDoc} */ + @Override public T getHxDot() { if (!hasDerivatives()) { @@ -560,6 +604,7 @@ public T getHxDot() { } /** {@inheritDoc} */ + @Override public T getHy() { // Check for equatorial retrograde orbit if (FastMath.abs(i.subtract(i.getPi()).getReal()) < 1.0e-10) { @@ -569,6 +614,7 @@ public T getHy() { } /** {@inheritDoc} */ + @Override public T getHyDot() { if (!hasDerivatives()) { @@ -591,21 +637,71 @@ public T getHyDot() { * @return v + ω true latitude argument (rad) */ public T getAlphaV() { - return alphaV; + switch (cachedPositionAngleType) { + case TRUE: + return cachedAlpha; + + case ECCENTRIC: + return FieldCircularLatitudeArgumentUtility.eccentricToTrue(ex, ey, cachedAlpha); + + case MEAN: + return FieldCircularLatitudeArgumentUtility.meanToTrue(ex, ey, cachedAlpha); + + default: + throw new OrekitInternalError(null); + } } /** Get the true latitude argument derivative. * @return d(v + ω)/dt true latitude argument derivative (rad/s) */ public T getAlphaVDot() { - return alphaVDot; + + if (!hasDerivatives()) { + return null; + } + switch (cachedPositionAngleType) { + case ECCENTRIC: + final FieldUnivariateDerivative1 alphaEUD = new FieldUnivariateDerivative1<>(cachedAlpha, cachedAlphaDot); + final FieldUnivariateDerivative1 exUD = new FieldUnivariateDerivative1<>(ex, exDot); + final FieldUnivariateDerivative1 eyUD = new FieldUnivariateDerivative1<>(ey, eyDot); + final FieldUnivariateDerivative1 alphaVUD = FieldCircularLatitudeArgumentUtility.eccentricToTrue(exUD, eyUD, + alphaEUD); + return alphaVUD.getFirstDerivative(); + + case TRUE: + return cachedAlphaDot; + + case MEAN: + final FieldUnivariateDerivative1 alphaMUD = new FieldUnivariateDerivative1<>(cachedAlpha, cachedAlphaDot); + final FieldUnivariateDerivative1 exUD2 = new FieldUnivariateDerivative1<>(ex, exDot); + final FieldUnivariateDerivative1 eyUD2 = new FieldUnivariateDerivative1<>(ey, eyDot); + final FieldUnivariateDerivative1 alphaVUD2 = FieldCircularLatitudeArgumentUtility.meanToTrue(exUD2, + eyUD2, alphaMUD); + return alphaVUD2.getFirstDerivative(); + + default: + throw new OrekitInternalError(null); + } } /** Get the eccentric latitude argument. * @return E + ω eccentric latitude argument (rad) */ public T getAlphaE() { - return trueToEccentric(alphaV, ex, ey); + switch (cachedPositionAngleType) { + case TRUE: + return FieldCircularLatitudeArgumentUtility.trueToEccentric(ex, ey, cachedAlpha); + + case ECCENTRIC: + return cachedAlpha; + + case MEAN: + return FieldCircularLatitudeArgumentUtility.meanToEccentric(ex, ey, cachedAlpha); + + default: + throw new OrekitInternalError(null); + } } /** Get the eccentric latitude argument derivative. @@ -616,12 +712,29 @@ public T getAlphaEDot() { if (!hasDerivatives()) { return null; } - - final FieldUnivariateDerivative1 alphaVUD = new FieldUnivariateDerivative1<>(alphaV, alphaVDot); - final FieldUnivariateDerivative1 exUD = new FieldUnivariateDerivative1<>(ex, exDot); - final FieldUnivariateDerivative1 eyUD = new FieldUnivariateDerivative1<>(ey, eyDot); - final FieldUnivariateDerivative1 alphaEUD = trueToEccentric(alphaVUD, exUD, eyUD); - return alphaEUD.getDerivative(1); + switch (cachedPositionAngleType) { + case TRUE: + final FieldUnivariateDerivative1 alphaVUD = new FieldUnivariateDerivative1<>(cachedAlpha, cachedAlphaDot); + final FieldUnivariateDerivative1 exUD = new FieldUnivariateDerivative1<>(ex, exDot); + final FieldUnivariateDerivative1 eyUD = new FieldUnivariateDerivative1<>(ey, eyDot); + final FieldUnivariateDerivative1 alphaEUD = FieldCircularLatitudeArgumentUtility.trueToEccentric(exUD, eyUD, + alphaVUD); + return alphaEUD.getFirstDerivative(); + + case ECCENTRIC: + return cachedAlphaDot; + + case MEAN: + final FieldUnivariateDerivative1 alphaMUD = new FieldUnivariateDerivative1<>(cachedAlpha, cachedAlphaDot); + final FieldUnivariateDerivative1 exUD2 = new FieldUnivariateDerivative1<>(ex, exDot); + final FieldUnivariateDerivative1 eyUD2 = new FieldUnivariateDerivative1<>(ey, eyDot); + final FieldUnivariateDerivative1 alphaVUD2 = FieldCircularLatitudeArgumentUtility.meanToEccentric(exUD2, + eyUD2, alphaMUD); + return alphaVUD2.getFirstDerivative(); + + default: + throw new OrekitInternalError(null); + } } @@ -629,7 +742,19 @@ public T getAlphaEDot() { * @return M + ω mean latitude argument (rad) */ public T getAlphaM() { - return eccentricToMean(trueToEccentric(alphaV, ex, ey), ex, ey); + switch (cachedPositionAngleType) { + case TRUE: + return FieldCircularLatitudeArgumentUtility.trueToMean(ex, ey, cachedAlpha); + + case MEAN: + return cachedAlpha; + + case ECCENTRIC: + return FieldCircularLatitudeArgumentUtility.eccentricToMean(ex, ey, cachedAlpha); + + default: + throw new OrekitInternalError(null); + } } /** Get the mean latitude argument derivative. @@ -640,13 +765,29 @@ public T getAlphaMDot() { if (!hasDerivatives()) { return null; } - - final FieldUnivariateDerivative1 alphaVUD = new FieldUnivariateDerivative1<>(alphaV, alphaVDot); - final FieldUnivariateDerivative1 exUD = new FieldUnivariateDerivative1<>(ex, exDot); - final FieldUnivariateDerivative1 eyUD = new FieldUnivariateDerivative1<>(ey, eyDot); - final FieldUnivariateDerivative1 alphaMUD = eccentricToMean(trueToEccentric(alphaVUD, exUD, eyUD), exUD, eyUD); - return alphaMUD.getDerivative(1); - + switch (cachedPositionAngleType) { + case TRUE: + final FieldUnivariateDerivative1 alphaVUD = new FieldUnivariateDerivative1<>(cachedAlpha, cachedAlphaDot); + final FieldUnivariateDerivative1 exUD = new FieldUnivariateDerivative1<>(ex, exDot); + final FieldUnivariateDerivative1 eyUD = new FieldUnivariateDerivative1<>(ey, eyDot); + final FieldUnivariateDerivative1 alphaMUD = FieldCircularLatitudeArgumentUtility.trueToMean(exUD, eyUD, + alphaVUD); + return alphaMUD.getFirstDerivative(); + + case MEAN: + return cachedAlphaDot; + + case ECCENTRIC: + final FieldUnivariateDerivative1 alphaEUD = new FieldUnivariateDerivative1<>(cachedAlpha, cachedAlphaDot); + final FieldUnivariateDerivative1 exUD2 = new FieldUnivariateDerivative1<>(ex, exDot); + final FieldUnivariateDerivative1 eyUD2 = new FieldUnivariateDerivative1<>(ey, eyDot); + final FieldUnivariateDerivative1 alphaMUD2 = FieldCircularLatitudeArgumentUtility.eccentricToMean(exUD2, + eyUD2, alphaEUD); + return alphaMUD2.getFirstDerivative(); + + default: + throw new OrekitInternalError(null); + } } /** Get the latitude argument. @@ -676,12 +817,9 @@ public T getAlphaDot(final PositionAngleType type) { * @param Type of the field elements * @return the true latitude argument. */ + @Deprecated public static > T eccentricToTrue(final T alphaE, final T ex, final T ey) { - final T epsilon = ex.multiply(ex).add(ey.multiply(ey)).negate().add(1).sqrt(); - final FieldSinCos scAlphaE = FastMath.sinCos(alphaE); - return alphaE.add(ex.multiply(scAlphaE.sin()).subtract(ey.multiply(scAlphaE.cos())).divide( - epsilon.add(1).subtract(ex.multiply(scAlphaE.cos())).subtract( - ey.multiply(scAlphaE.sin()))).atan().multiply(2)); + return FieldCircularLatitudeArgumentUtility.eccentricToTrue(ex, ey, alphaE); } /** Computes the eccentric latitude argument from the true latitude argument. @@ -691,11 +829,9 @@ public static > T eccentricToTrue(final T alph * @param Type of the field elements * @return the eccentric latitude argument. */ + @Deprecated public static > T trueToEccentric(final T alphaV, final T ex, final T ey) { - final T epsilon = ex.multiply(ex).add(ey.multiply(ey)).negate().add(1).sqrt(); - final FieldSinCos scAlphaV = FastMath.sinCos(alphaV); - return alphaV.add(ey.multiply(scAlphaV.cos()).subtract(ex.multiply(scAlphaV.sin())).divide - (epsilon.add(1).add(ex.multiply(scAlphaV.cos()).add(ey.multiply(scAlphaV.sin())))).atan().multiply(2)); + return FieldCircularLatitudeArgumentUtility.trueToEccentric(ex, ey, alphaV); } /** Computes the eccentric latitude argument from the mean latitude argument. @@ -705,30 +841,9 @@ public static > T trueToEccentric(final T alph * @param Type of the field elements * @return the eccentric latitude argument. */ + @Deprecated public static > T meanToEccentric(final T alphaM, final T ex, final T ey) { - // Generalization of Kepler equation to circular parameters - // with alphaE = PA + E and - // alphaM = PA + M = alphaE - ex.sin(alphaE) + ey.cos(alphaE) - - T alphaE = alphaM; - T shift = alphaM.getField().getZero(); - T alphaEMalphaM = alphaM.getField().getZero(); - FieldSinCos scAlphaE = FastMath.sinCos(alphaE); - int iter = 0; - do { - final T f2 = ex.multiply(scAlphaE.sin()).subtract(ey.multiply(scAlphaE.cos())); - final T f1 = ex.negate().multiply(scAlphaE.cos()).subtract(ey.multiply(scAlphaE.sin())).add(1); - final T f0 = alphaEMalphaM.subtract(f2); - - final T f12 = f1.multiply(2); - shift = f0.multiply(f12).divide(f1.multiply(f12).subtract(f0.multiply(f2))); - - alphaEMalphaM = alphaEMalphaM.subtract(shift); - alphaE = alphaM.add(alphaEMalphaM); - scAlphaE = FastMath.sinCos(alphaE); - } while (++iter < 50 && FastMath.abs(shift.getReal()) > 1.0e-12); - return alphaE; - + return FieldCircularLatitudeArgumentUtility.meanToEccentric(ex, ey, alphaM); } /** Computes the mean latitude argument from the eccentric latitude argument. @@ -738,17 +853,19 @@ public static > T meanToEccentric(final T alph * @param Type of the field elements * @return the mean latitude argument. */ + @Deprecated public static > T eccentricToMean(final T alphaE, final T ex, final T ey) { - final FieldSinCos scAlphaE = FastMath.sinCos(alphaE); - return alphaE.subtract(ex.multiply(scAlphaE.sin()).subtract(ey.multiply(scAlphaE.cos()))); + return FieldCircularLatitudeArgumentUtility.eccentricToMean(ex, ey, alphaE); } /** {@inheritDoc} */ + @Override public T getE() { return ex.multiply(ex).add(ey.multiply(ey)).sqrt(); } /** {@inheritDoc} */ + @Override public T getEDot() { if (!hasDerivatives()) { @@ -760,11 +877,13 @@ public T getEDot() { } /** {@inheritDoc} */ + @Override public T getI() { return i; } /** {@inheritDoc} */ + @Override public T getIDot() { return iDot; } @@ -784,31 +903,37 @@ public T getRightAscensionOfAscendingNodeDot() { } /** {@inheritDoc} */ + @Override public T getLv() { - return alphaV.add(raan); + return getAlphaV().add(raan); } /** {@inheritDoc} */ + @Override public T getLvDot() { - return hasDerivatives() ? alphaVDot.add(raanDot) : null; + return hasDerivatives() ? getAlphaVDot().add(raanDot) : null; } /** {@inheritDoc} */ + @Override public T getLE() { return getAlphaE().add(raan); } /** {@inheritDoc} */ + @Override public T getLEDot() { return hasDerivatives() ? getAlphaEDot().add(raanDot) : null; } /** {@inheritDoc} */ + @Override public T getLM() { return getAlphaM().add(raan); } /** {@inheritDoc} */ + @Override public T getLMDot() { return hasDerivatives() ? getAlphaMDot().add(raanDot) : null; } @@ -850,8 +975,8 @@ private void computePVWithoutA() { // eccentricity-related intermediate parameters final T exey = equEx.multiply(equEy); - final T ex2 = equEx.multiply(equEx); - final T ey2 = equEy.multiply(equEy); + final T ex2 = equEx.square(); + final T ey2 = equEy.square(); final T e2 = ex2.add(ey2); final T eta = e2.negate().add(1).sqrt().add(1); final T beta = eta.reciprocal(); @@ -880,6 +1005,96 @@ private void computePVWithoutA() { } + + /** Initialize cached alpha with rate. + * @param alpha input alpha + * @param alphaDot rate of input alpha + * @param inputType position angle type passed as input + * @return alpha to cache with rate + * @since 12.1 + */ + private FieldUnivariateDerivative1 initializeCachedAlpha(final T alpha, final T alphaDot, + final PositionAngleType inputType) { + if (cachedPositionAngleType == inputType) { + return new FieldUnivariateDerivative1<>(alpha, alphaDot); + + } else { + final FieldUnivariateDerivative1 exUD = new FieldUnivariateDerivative1<>(ex, exDot); + final FieldUnivariateDerivative1 eyUD = new FieldUnivariateDerivative1<>(ey, eyDot); + final FieldUnivariateDerivative1 alphaUD = new FieldUnivariateDerivative1<>(alpha, alphaDot); + + switch (cachedPositionAngleType) { + + case ECCENTRIC: + if (inputType == PositionAngleType.MEAN) { + return FieldCircularLatitudeArgumentUtility.meanToEccentric(exUD, eyUD, alphaUD); + } else { + return FieldCircularLatitudeArgumentUtility.trueToEccentric(exUD, eyUD, alphaUD); + } + + case TRUE: + if (inputType == PositionAngleType.MEAN) { + return FieldCircularLatitudeArgumentUtility.meanToTrue(exUD, eyUD, alphaUD); + } else { + return FieldCircularLatitudeArgumentUtility.eccentricToTrue(exUD, eyUD, alphaUD); + } + + case MEAN: + if (inputType == PositionAngleType.TRUE) { + return FieldCircularLatitudeArgumentUtility.trueToMean(exUD, eyUD, alphaUD); + } else { + return FieldCircularLatitudeArgumentUtility.eccentricToMean(exUD, eyUD, alphaUD); + } + + default: + throw new OrekitInternalError(null); + + } + + } + + } + + /** Initialize cached alpha. + * @param alpha input alpha + * @param positionAngleType position angle type passed as input + * @return alpha to cache + * @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); + } + } + } + /** Compute non-Keplerian part of the acceleration from first time derivatives. *

        * This method should be called only when {@link #hasDerivatives()} returns true. @@ -916,6 +1131,7 @@ private FieldVector3D nonKeplerianAcceleration() { } /** {@inheritDoc} */ + @Override protected FieldVector3D initPosition() { // get equinoctial parameters final T equEx = getEquinoctialEx(); @@ -939,8 +1155,8 @@ protected FieldVector3D initPosition() { // eccentricity-related intermediate parameters final T exey = equEx.multiply(equEy); - final T ex2 = equEx.multiply(equEx); - final T ey2 = equEy.multiply(equEy); + final T ex2 = equEx.square(); + final T ey2 = equEy.square(); final T e2 = ex2.add(ey2); final T eta = e2.negate().add(1).sqrt().add(1); final T beta = eta.reciprocal(); @@ -961,6 +1177,7 @@ protected FieldVector3D initPosition() { } /** {@inheritDoc} */ + @Override protected TimeStampedFieldPVCoordinates initPVCoordinates() { // position and velocity @@ -979,17 +1196,19 @@ protected TimeStampedFieldPVCoordinates initPVCoordinates() { } /** {@inheritDoc} */ + @Override public FieldCircularOrbit shiftedBy(final double dt) { - return shiftedBy(getZero().add(dt)); + return shiftedBy(getZero().newInstance(dt)); } /** {@inheritDoc} */ + @Override public FieldCircularOrbit shiftedBy(final T dt) { // use Keplerian-only motion final FieldCircularOrbit keplerianShifted = new FieldCircularOrbit<>(a, ex, ey, i, raan, getAlphaM().add(getKeplerianMeanMotion().multiply(dt)), - PositionAngleType.MEAN, getFrame(), + PositionAngleType.MEAN, cachedPositionAngleType, getFrame(), getDate().shiftedBy(dt), getMu()); if (hasDerivatives()) { @@ -1000,7 +1219,7 @@ PositionAngleType.MEAN, getFrame(), // add quadratic effect of non-Keplerian acceleration to Keplerian-only shift keplerianShifted.computePVWithoutA(); final FieldVector3D fixedP = new FieldVector3D<>(getOne(), keplerianShifted.partialPV.getPosition(), - dt.multiply(dt).multiply(0.5), nonKeplerianAcceleration); + dt.square().multiply(0.5), nonKeplerianAcceleration); final T fixedR2 = fixedP.getNormSq(); final T fixedR = fixedR2.sqrt(); final FieldVector3D fixedV = new FieldVector3D<>(getOne(), keplerianShifted.partialPV.getVelocity(), @@ -1022,6 +1241,7 @@ PositionAngleType.MEAN, getFrame(), } /** {@inheritDoc} */ + @Override protected T[][] computeJacobianMeanWrtCartesian() { final T[][] jacobian = MathArrays.buildArray(getOne().getField(), 6, 6); @@ -1047,10 +1267,10 @@ protected T[][] computeJacobianMeanWrtCartesian() { final T rOa = r.divide(a); final T aOr = a.divide(r); final T aOr2 = a.divide(r2); - final T a2 = a.multiply(a); + final T a2 = a.square(); - final T ex2 = ex.multiply(ex); - final T ey2 = ey.multiply(ey); + final T ex2 = ex.square(); + final T ey2 = ey.square(); final T e2 = ex2.add(ey2); final T epsilon = e2.negate().add(1.0).sqrt(); final T beta = epsilon.add(1).reciprocal(); @@ -1133,7 +1353,7 @@ protected T[][] computeJacobianMeanWrtCartesian() { final FieldVector3D dc1P = new FieldVector3D<>(aOr2.multiply(eSinE.multiply(eSinE).multiply(2).add(1).subtract(eCosE)).divide(r2), position, aOr2.multiply(-2).multiply(eSinE).multiply(oOsqrtMuA), velocity); final FieldVector3D dc1V = new FieldVector3D<>(aOr2.multiply(-2).multiply(eSinE).multiply(oOsqrtMuA), position, - getZero().add(2).divide(mu), velocity); + getZero().newInstance(2).divide(mu), velocity); final FieldVector3D dc2P = new FieldVector3D<>(aOr2.multiply(eSinE).multiply(eSinE.multiply(eSinE).subtract(e2.negate().add(1))).divide(r2.multiply(epsilon)), position, aOr2.multiply(e2.negate().add(1).subtract(eSinE.multiply(eSinE))).multiply(oOsqrtMuA).divide(epsilon), velocity); final FieldVector3D dc2V = new FieldVector3D<>(aOr2.multiply(e2.negate().add(1).subtract(eSinE.multiply(eSinE))).multiply(oOsqrtMuA).divide(epsilon), position, @@ -1181,6 +1401,7 @@ protected T[][] computeJacobianMeanWrtCartesian() { } /** {@inheritDoc} */ + @Override protected T[][] computeJacobianEccentricWrtCartesian() { // start by computing the Jacobian with mean angle @@ -1209,6 +1430,7 @@ protected T[][] computeJacobianEccentricWrtCartesian() { } /** {@inheritDoc} */ + @Override protected T[][] computeJacobianTrueWrtCartesian() { // start by computing the Jacobian with eccentric angle @@ -1255,22 +1477,24 @@ 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); - final FieldSinCos sc = FastMath.sinCos(alphaV); + final FieldSinCos sc; switch (type) { case MEAN : pDot[5] = pDot[5].add(n); break; case ECCENTRIC : - 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).divide(oMe2)); + 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()))); @@ -1289,14 +1513,14 @@ public String toString() { append(", ex: ").append(ex.getReal()).append(", ey: ").append(ey.getReal()). append(", i: ").append(FastMath.toDegrees(i.getReal())). append(", raan: ").append(FastMath.toDegrees(raan.getReal())). - append(", alphaV: ").append(FastMath.toDegrees(alphaV.getReal())). + append(", alphaV: ").append(FastMath.toDegrees(getAlphaV().getReal())). append(";}").toString(); } /** {@inheritDoc} */ @Override public PositionAngleType getCachedPositionAngleType() { - return PositionAngleType.TRUE; + return cachedPositionAngleType; } /** {@inheritDoc} */ @@ -1308,26 +1532,26 @@ public boolean hasRates() { /** {@inheritDoc} */ @Override public FieldCircularOrbit removeRates() { - final PositionAngleType positionAngleType = getCachedPositionAngleType(); return new FieldCircularOrbit<>(getA(), getCircularEx(), getCircularEy(), - getI(), getRightAscensionOfAscendingNode(), getAlpha(positionAngleType), - positionAngleType, getFrame(), getDate(), getMu()); + getI(), getRightAscensionOfAscendingNode(), cachedAlpha, + cachedPositionAngleType, getFrame(), getDate(), getMu()); } /** {@inheritDoc} */ @Override public CircularOrbit toOrbit() { + final double cachedPositionAngle = cachedAlpha.getReal(); if (hasDerivatives()) { return new CircularOrbit(a.getReal(), ex.getReal(), ey.getReal(), - i.getReal(), raan.getReal(), alphaV.getReal(), + i.getReal(), raan.getReal(), cachedPositionAngle, aDot.getReal(), exDot.getReal(), eyDot.getReal(), - iDot.getReal(), raanDot.getReal(), alphaVDot.getReal(), - PositionAngleType.TRUE, getFrame(), + iDot.getReal(), raanDot.getReal(), cachedAlphaDot.getReal(), + cachedPositionAngleType, getFrame(), getDate().toAbsoluteDate(), getMu().getReal()); } else { return new CircularOrbit(a.getReal(), ex.getReal(), ey.getReal(), - i.getReal(), raan.getReal(), alphaV.getReal(), - PositionAngleType.TRUE, getFrame(), + i.getReal(), raan.getReal(), cachedPositionAngle, + cachedPositionAngleType, getFrame(), getDate().toAbsoluteDate(), getMu().getReal()); } } diff --git a/src/main/java/org/orekit/orbits/FieldEquinoctialLongitudeArgumentUtility.java b/src/main/java/org/orekit/orbits/FieldEquinoctialLongitudeArgumentUtility.java new file mode 100644 index 0000000000..d0c8ae579a --- /dev/null +++ b/src/main/java/org/orekit/orbits/FieldEquinoctialLongitudeArgumentUtility.java @@ -0,0 +1,188 @@ +/* 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.orbits; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.FieldSinCos; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; + +/** + * Utility methods for converting between different longitude arguments used by {@link FieldEquinoctialOrbit}. + * @author Romain Serra + * @see FieldEquinoctialOrbit + * @since 12.1 + */ +public class FieldEquinoctialLongitudeArgumentUtility { + + /** Tolerance for stopping criterion in iterative conversion from mean to eccentric angle. */ + private static final double TOLERANCE_CONVERGENCE = 1.0e-12; + + /** Maximum number of iterations in iterative conversion from mean to eccentric angle. */ + private static final int MAXIMUM_ITERATION = 50; + + /** Private constructor for utility class. */ + private FieldEquinoctialLongitudeArgumentUtility() { + // nothing here (utils class) + } + + /** + * Computes the true longitude argument from the eccentric longitude argument. + * + * @param Type of the field elements + * @param ex e cos(ω), first component of eccentricity vector + * @param ey e sin(ω), second component of eccentricity vector + * @param lE = E + ω + Ω eccentric longitude argument (rad) + * @return the true longitude argument. + */ + public static > T eccentricToTrue(final T ex, final T ey, final T lE) { + final T epsilon = eccentricAndTrueEpsilon(ex, ey); + final FieldSinCos scLE = FastMath.sinCos(lE); + final T cosLE = scLE.cos(); + final T sinLE = scLE.sin(); + final T num = ex.multiply(sinLE).subtract(ey.multiply(cosLE)); + final T den = epsilon.add(1).subtract(ex.multiply(cosLE)).subtract(ey.multiply(sinLE)); + return lE.add(eccentricAndTrueAtan(num, den)); + } + + /** + * Computes the eccentric longitude argument from the true longitude argument. + * + * @param Type of the field elements + * @param ex e cos(ω), first component of eccentricity vector + * @param ey e sin(ω), second component of eccentricity vector + * @param lV = V + ω + Ω true longitude argument (rad) + * @return the eccentric longitude argument. + */ + public static > T trueToEccentric(final T ex, final T ey, final T lV) { + final T epsilon = eccentricAndTrueEpsilon(ex, ey); + final FieldSinCos scLv = FastMath.sinCos(lV); + final T cosLv = scLv.cos(); + final T sinLv = scLv.sin(); + final T num = ey.multiply(cosLv).subtract(ex.multiply(sinLv)); + final T den = epsilon.add(1).add(ex.multiply(cosLv)).add(ey.multiply(sinLv)); + return lV.add(eccentricAndTrueAtan(num, den)); + } + + /** + * Computes an intermediate quantity for conversions between true and eccentric. + * + * @param Type of the field elements + * @param ex e cos(ω), first component of eccentricity vector + * @param ey e sin(ω), second component of eccentricity vector + * @return intermediate variable referred to as epsilon. + */ + private static > T eccentricAndTrueEpsilon(final T ex, final T ey) { + return (ex.square().negate().subtract(ey.square()).add(1.)).sqrt(); + } + + /** + * Computes another intermediate quantity for conversions between true and eccentric. + * + * @param Type of the field elements + * @param num numerator for angular conversion + * @param den denominator for angular conversion + * @return arc-tangent of ratio of inputs times two. + */ + private static > T eccentricAndTrueAtan(final T num, final T den) { + return (num.divide(den)).atan().multiply(2); + } + + /** + * Computes the eccentric longitude argument from the mean longitude argument. + * + * @param Type of the field elements + * @param ex e cos(ω), first component of eccentricity vector + * @param ey e sin(ω), second component of eccentricity vector + * @param lM = M + ω + Ω mean longitude argument (rad) + * @return the eccentric longitude argument. + */ + public static > T meanToEccentric(final T ex, final T ey, final T lM) { + // Generalization of Kepler equation to equinoctial parameters + // with lE = PA + RAAN + E and + // lM = PA + RAAN + M = lE - ex.sin(lE) + ey.cos(lE) + T lE = lM; + T shift; + T lEmlM = lM.getField().getZero(); + boolean hasConverged; + int iter = 0; + do { + final FieldSinCos scLE = FastMath.sinCos(lE); + final T f2 = ex.multiply(scLE.sin()).subtract(ey.multiply(scLE.cos())); + final T f1 = ex.multiply(scLE.cos()).add(ey.multiply(scLE.sin())).negate().add(1); + final T f0 = lEmlM.subtract(f2); + + final T f12 = f1.multiply(2.0); + shift = f0.multiply(f12).divide(f1.multiply(f12).subtract(f0.multiply(f2))); + + lEmlM = lEmlM.subtract(shift); + lE = lM.add(lEmlM); + + hasConverged = FastMath.abs(shift.getReal()) <= TOLERANCE_CONVERGENCE; + } while (++iter < MAXIMUM_ITERATION && !hasConverged); + + if (!hasConverged) { + throw new OrekitException(OrekitMessages.UNABLE_TO_COMPUTE_ECCENTRIC_LONGITUDE_ARGUMENT, iter); + } + return lE; + + } + + /** + * Computes the mean longitude argument from the eccentric longitude argument. + * + * @param Type of the field elements + * @param ex e cos(ω), first component of eccentricity vector + * @param ey e sin(ω), second component of eccentricity vector + * @param lE = E + ω + Ω mean longitude argument (rad) + * @return the mean longitude argument. + */ + public static > T eccentricToMean(final T ex, final T ey, final T lE) { + final FieldSinCos scLE = FastMath.sinCos(lE); + return lE.subtract(ex.multiply(scLE.sin())).add(ey.multiply(scLE.cos())); + } + + /** + * Computes the mean longitude argument from the eccentric longitude argument. + * + * @param Type of the field elements + * @param ex e cos(ω), first component of eccentricity vector + * @param ey e sin(ω), second component of eccentricity vector + * @param lV = V + ω + Ω true longitude argument (rad) + * @return the mean longitude argument. + */ + public static > T trueToMean(final T ex, final T ey, final T lV) { + final T alphaE = trueToEccentric(ex, ey, lV); + return eccentricToMean(ex, ey, alphaE); + } + + /** + * Computes the true longitude argument from the eccentric longitude argument. + * + * @param Type of the field elements + * @param ex e cos(ω), first component of eccentricity vector + * @param ey e sin(ω), second component of eccentricity vector + * @param lM = M + ω + Ω mean longitude argument (rad) + * @return the true longitude argument. + */ + public static > T meanToTrue(final T ex, final T ey, final T lM) { + final T alphaE = meanToEccentric(ex, ey, lM); + return eccentricToTrue(ex, ey, alphaE); + } + +} diff --git a/src/main/java/org/orekit/orbits/FieldEquinoctialOrbit.java b/src/main/java/org/orekit/orbits/FieldEquinoctialOrbit.java index fc76ea205d..539d57d44a 100644 --- a/src/main/java/org/orekit/orbits/FieldEquinoctialOrbit.java +++ b/src/main/java/org/orekit/orbits/FieldEquinoctialOrbit.java @@ -90,8 +90,11 @@ public class FieldEquinoctialOrbit> extends Fi /** Second component of the inclination vector. */ private final T hy; - /** True longitude argument (rad). */ - private final T lv; + /** Cached longitude argument (rad). */ + private final T cachedL; + + /** Cache type of position angle (longitude argument). */ + private final PositionAngleType cachedPositionAngleType; /** Semi-major axis derivative (m/s). */ private final T aDot; @@ -108,8 +111,8 @@ public class FieldEquinoctialOrbit> extends Fi /** Second component of the inclination vector derivative. */ private final T hyDot; - /** True longitude argument derivative (rad/s). */ - private final T lvDot; + /** Derivative of cached longitude argument (rad/s). */ + private final T cachedLDot; /** Partial Cartesian coordinates (position and velocity are valid, acceleration may be missing). */ private FieldPVCoordinates partialPV; @@ -122,21 +125,48 @@ public class FieldEquinoctialOrbit> extends Fi * @param hy tan(i/2) sin(Ω), second component of inclination vector * @param l (M or E or v) + ω + Ω, mean, eccentric or true longitude argument (rad) * @param type type of longitude argument + * @param cachedPositionAngleType type of cached longitude argument * @param frame the frame in which the parameters are defined * (must be a {@link Frame#isPseudoInertial pseudo-inertial frame}) * @param date date of the orbital parameters * @param mu central attraction coefficient (m³/s²) * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame} + * @since 12.1 */ public FieldEquinoctialOrbit(final T a, final T ex, final T ey, final T hx, final T hy, final T l, - final PositionAngleType type, + final PositionAngleType type, final PositionAngleType cachedPositionAngleType, final Frame frame, final FieldAbsoluteDate date, final T mu) throws IllegalArgumentException { this(a, ex, ey, hx, hy, l, null, null, null, null, null, null, - type, frame, date, mu); + type, cachedPositionAngleType, frame, date, mu); + } + + /** Creates a new instance. + * @param a semi-major axis (m) + * @param ex e cos(ω + Ω), first component of eccentricity vector + * @param ey e sin(ω + Ω), second component of eccentricity vector + * @param hx tan(i/2) cos(Ω), first component of inclination vector + * @param hy tan(i/2) sin(Ω), second component of inclination vector + * @param l (M or E or v) + ω + Ω, mean, eccentric or true longitude argument (rad) + * @param type type of longitude argument + * @param frame the frame in which the parameters are defined + * (must be a {@link Frame#isPseudoInertial pseudo-inertial frame}) + * @param date date of the orbital parameters + * @param mu central attraction coefficient (m³/s²) + * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or + * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame} + */ + public FieldEquinoctialOrbit(final T a, final T ex, final T ey, + final T hx, final T hy, final T l, + final PositionAngleType type, + final Frame frame, final FieldAbsoluteDate date, final T mu) + throws IllegalArgumentException { + this(a, ex, ey, hx, hy, l, + null, null, null, null, null, null, + type, type, frame, date, mu); } /** Creates a new instance. @@ -153,18 +183,20 @@ public FieldEquinoctialOrbit(final T a, final T ex, final T ey, * @param hyDot d(tan(i/2) sin(Ω))/dt, second component of inclination vector derivative * @param lDot d(M or E or v) + ω + Ω)/dr, mean, eccentric or true longitude argument derivative (rad/s) * @param type type of longitude argument + * @param cachedPositionAngleType of cached longitude argument * @param frame the frame in which the parameters are defined * (must be a {@link Frame#isPseudoInertial pseudo-inertial frame}) * @param date date of the orbital parameters * @param mu central attraction coefficient (m³/s²) * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame} + * @since 12.1 */ public FieldEquinoctialOrbit(final T a, final T ex, final T ey, final T hx, final T hy, final T l, final T aDot, final T exDot, final T eyDot, final T hxDot, final T hyDot, final T lDot, - final PositionAngleType type, + final PositionAngleType type, final PositionAngleType cachedPositionAngleType, final Frame frame, final FieldAbsoluteDate date, final T mu) throws IllegalArgumentException { super(frame, date, mu); @@ -173,7 +205,7 @@ public FieldEquinoctialOrbit(final T a, final T ex, final T ey, throw new OrekitIllegalArgumentException(OrekitMessages.HYPERBOLIC_ORBIT_NOT_HANDLED_AS, getClass().getName()); } - + this.cachedPositionAngleType = cachedPositionAngleType; this.a = a; this.aDot = aDot; this.ex = ex; @@ -186,46 +218,50 @@ public FieldEquinoctialOrbit(final T a, final T ex, final T ey, this.hyDot = hyDot; if (hasDerivatives()) { - final FieldUnivariateDerivative1 exUD = new FieldUnivariateDerivative1<>(ex, exDot); - final FieldUnivariateDerivative1 eyUD = new FieldUnivariateDerivative1<>(ey, eyDot); - final FieldUnivariateDerivative1 lUD = new FieldUnivariateDerivative1<>(l, lDot); - final FieldUnivariateDerivative1 lvUD; - switch (type) { - case MEAN : - lvUD = eccentricToTrue(meanToEccentric(lUD, exUD, eyUD), exUD, eyUD); - break; - case ECCENTRIC : - lvUD = eccentricToTrue(lUD, exUD, eyUD); - break; - case TRUE : - lvUD = lUD; - break; - default : // this should never happen - throw new OrekitInternalError(null); - } - this.lv = lvUD.getValue(); - this.lvDot = lvUD.getDerivative(1); + final FieldUnivariateDerivative1 alphaUD = initializeCachedL(l, lDot, type); + this.cachedL = alphaUD.getValue(); + this.cachedLDot = alphaUD.getFirstDerivative(); } else { - switch (type) { - case MEAN : - this.lv = eccentricToTrue(meanToEccentric(l, ex, ey), ex, ey); - break; - case ECCENTRIC : - this.lv = eccentricToTrue(l, ex, ey); - break; - case TRUE : - this.lv = l; - break; - default : - throw new OrekitInternalError(null); - } - this.lvDot = null; + this.cachedL = initializeCachedL(l, type); + this.cachedLDot = null; } this.partialPV = null; } + /** Creates a new instance. + * @param a semi-major axis (m) + * @param ex e cos(ω + Ω), first component of eccentricity vector + * @param ey e sin(ω + Ω), second component of eccentricity vector + * @param hx tan(i/2) cos(Ω), first component of inclination vector + * @param hy tan(i/2) sin(Ω), second component of inclination vector + * @param l (M or E or v) + ω + Ω, mean, eccentric or true longitude argument (rad) + * @param aDot semi-major axis derivative (m/s) + * @param exDot d(e cos(ω + Ω))/dt, first component of eccentricity vector derivative + * @param eyDot d(e sin(ω + Ω))/dt, second component of eccentricity vector derivative + * @param hxDot d(tan(i/2) cos(Ω))/dt, first component of inclination vector derivative + * @param hyDot d(tan(i/2) sin(Ω))/dt, second component of inclination vector derivative + * @param lDot d(M or E or v) + ω + Ω)/dr, mean, eccentric or true longitude argument derivative (rad/s) + * @param type type of longitude argument + * @param frame the frame in which the parameters are defined + * (must be a {@link Frame#isPseudoInertial pseudo-inertial frame}) + * @param date date of the orbital parameters + * @param mu central attraction coefficient (m³/s²) + * @exception IllegalArgumentException if eccentricity is equal to 1 or larger or + * if frame is not a {@link Frame#isPseudoInertial pseudo-inertial frame} + * @since 12.1 + */ + public FieldEquinoctialOrbit(final T a, final T ex, final T ey, + final T hx, final T hy, final T l, + final T aDot, final T exDot, final T eyDot, + final T hxDot, final T hyDot, final T lDot, + final PositionAngleType type, + final Frame frame, final FieldAbsoluteDate date, final T mu) + throws IllegalArgumentException { + this(a, ex, ey, hx, hy, l, aDot, exDot, eyDot, hxDot, hyDot, lDot, type, type, frame, date, mu); + } + /** Constructor from Cartesian parameters. * *

        The acceleration provided in {@code pvCoordinates} is accessible using @@ -269,14 +305,15 @@ public FieldEquinoctialOrbit(final TimeStampedFieldPVCoordinates pvCoordinate hy = d.multiply(w.getX()); // compute true longitude argument + cachedPositionAngleType = PositionAngleType.TRUE; final T cLv = (pvP.getX().subtract(d.multiply(pvP.getZ()).multiply(w.getX()))).divide(r); final T sLv = (pvP.getY().subtract(d.multiply(pvP.getZ()).multiply(w.getY()))).divide(r); - lv = sLv.atan2(cLv); + cachedL = sLv.atan2(cLv); // compute eccentricity vector final T eSE = FieldVector3D.dotProduct(pvP, pvV).divide(a.multiply(mu).sqrt()); final T eCE = rV2OnMu.subtract(1); - final T e2 = eCE.multiply(eCE).add(eSE.multiply(eSE)); + final T e2 = eCE.square().add(eSE.square()); final T f = eCE.subtract(e2); final T g = e2.negate().add(1).sqrt().multiply(eSE); ex = a.multiply(f.multiply(cLv).add( g.multiply(sLv))).divide(r); @@ -301,15 +338,15 @@ public FieldEquinoctialOrbit(final TimeStampedFieldPVCoordinates pvCoordinate hxDot = jacobian[3][3].multiply(aX).add(jacobian[3][4].multiply(aY)).add(jacobian[3][5].multiply(aZ)); hyDot = jacobian[4][3].multiply(aX).add(jacobian[4][4].multiply(aY)).add(jacobian[4][5].multiply(aZ)); - // in order to compute true anomaly derivative, we must compute - // mean anomaly derivative including Keplerian motion and convert to true anomaly + // in order to compute true longitude argument derivative, we must compute + // mean longitude argument derivative including Keplerian motion and convert to true anomaly final T lMDot = getKeplerianMeanMotion(). add(jacobian[5][3].multiply(aX)).add(jacobian[5][4].multiply(aY)).add(jacobian[5][5].multiply(aZ)); final FieldUnivariateDerivative1 exUD = new FieldUnivariateDerivative1<>(ex, exDot); final FieldUnivariateDerivative1 eyUD = new FieldUnivariateDerivative1<>(ey, eyDot); final FieldUnivariateDerivative1 lMUD = new FieldUnivariateDerivative1<>(getLM(), lMDot); - final FieldUnivariateDerivative1 lvUD = eccentricToTrue(meanToEccentric(lMUD, exUD, eyUD), exUD, eyUD); - lvDot = lvUD.getDerivative(1); + final FieldUnivariateDerivative1 lvUD = FieldEquinoctialLongitudeArgumentUtility.meanToTrue(exUD, eyUD, lMUD); + cachedLDot = lvUD.getFirstDerivative(); } else { // acceleration is either almost zero or NaN, @@ -320,7 +357,7 @@ public FieldEquinoctialOrbit(final TimeStampedFieldPVCoordinates pvCoordinate eyDot = null; hxDot = null; hyDot = null; - lvDot = null; + cachedLDot = null; } } @@ -357,14 +394,15 @@ public FieldEquinoctialOrbit(final FieldOrbit op) { ey = op.getEquinoctialEy(); hx = op.getHx(); hy = op.getHy(); - lv = op.getLv(); + cachedPositionAngleType = PositionAngleType.TRUE; + cachedL = op.getLv(); aDot = op.getADot(); exDot = op.getEquinoctialExDot(); eyDot = op.getEquinoctialEyDot(); hxDot = op.getHxDot(); hyDot = op.getHyDot(); - lvDot = op.getLvDot(); + cachedLDot = op.getLvDot(); } /** Constructor from Field and EquinoctialOrbit. @@ -374,29 +412,30 @@ public FieldEquinoctialOrbit(final FieldOrbit op) { * @since 12.0 */ public FieldEquinoctialOrbit(final Field field, final EquinoctialOrbit op) { - super(op.getFrame(), new FieldAbsoluteDate<>(field, op.getDate()), field.getZero().add(op.getMu())); + super(op.getFrame(), new FieldAbsoluteDate<>(field, op.getDate()), field.getZero().newInstance(op.getMu())); - a = getZero().add(op.getA()); - ex = getZero().add(op.getEquinoctialEx()); - ey = getZero().add(op.getEquinoctialEy()); - hx = getZero().add(op.getHx()); - hy = getZero().add(op.getHy()); - lv = getZero().add(op.getLv()); + a = getZero().newInstance(op.getA()); + ex = getZero().newInstance(op.getEquinoctialEx()); + ey = getZero().newInstance(op.getEquinoctialEy()); + hx = getZero().newInstance(op.getHx()); + hy = getZero().newInstance(op.getHy()); + cachedPositionAngleType = op.getCachedPositionAngleType(); + cachedL = getZero().newInstance(op.getL(cachedPositionAngleType)); if (op.hasDerivatives()) { - aDot = getZero().add(op.getADot()); - exDot = getZero().add(op.getEquinoctialExDot()); - eyDot = getZero().add(op.getEquinoctialEyDot()); - hxDot = getZero().add(op.getHxDot()); - hyDot = getZero().add(op.getHyDot()); - lvDot = getZero().add(op.getLvDot()); + aDot = getZero().newInstance(op.getADot()); + exDot = getZero().newInstance(op.getEquinoctialExDot()); + eyDot = getZero().newInstance(op.getEquinoctialEyDot()); + hxDot = getZero().newInstance(op.getHxDot()); + hyDot = getZero().newInstance(op.getHyDot()); + cachedLDot = getZero().newInstance(op.getLDot(cachedPositionAngleType)); } else { aDot = null; exDot = null; eyDot = null; hxDot = null; hyDot = null; - lvDot = null; + cachedLDot = null; } } @@ -408,112 +447,222 @@ public FieldEquinoctialOrbit(final Field field, final EquinoctialOrbit op) { * @since 12.0 */ public FieldEquinoctialOrbit(final Field field, final Orbit op) { - this(field, new EquinoctialOrbit(op)); + this(field, (EquinoctialOrbit) OrbitType.EQUINOCTIAL.convertType(op)); } /** {@inheritDoc} */ + @Override public OrbitType getType() { return OrbitType.EQUINOCTIAL; } /** {@inheritDoc} */ + @Override public T getA() { return a; } /** {@inheritDoc} */ + @Override public T getADot() { return aDot; } /** {@inheritDoc} */ + @Override public T getEquinoctialEx() { return ex; } /** {@inheritDoc} */ + @Override public T getEquinoctialExDot() { return exDot; } /** {@inheritDoc} */ + @Override public T getEquinoctialEy() { return ey; } /** {@inheritDoc} */ + @Override public T getEquinoctialEyDot() { return eyDot; } /** {@inheritDoc} */ + @Override public T getHx() { return hx; } /** {@inheritDoc} */ + @Override public T getHxDot() { return hxDot; } /** {@inheritDoc} */ + @Override public T getHy() { return hy; } /** {@inheritDoc} */ + @Override public T getHyDot() { return hyDot; } /** {@inheritDoc} */ + @Override public T getLv() { - return lv; + switch (cachedPositionAngleType) { + case TRUE: + return cachedL; + + case ECCENTRIC: + return FieldEquinoctialLongitudeArgumentUtility.eccentricToTrue(ex, ey, cachedL); + + case MEAN: + return FieldEquinoctialLongitudeArgumentUtility.meanToTrue(ex, ey, cachedL); + + default: + throw new OrekitInternalError(null); + } } /** {@inheritDoc} */ + @Override public T getLvDot() { - return lvDot; + + if (!hasDerivatives()) { + return null; + } + switch (cachedPositionAngleType) { + case ECCENTRIC: + final FieldUnivariateDerivative1 lEUD = new FieldUnivariateDerivative1<>(cachedL, cachedLDot); + final FieldUnivariateDerivative1 exUD = new FieldUnivariateDerivative1<>(ex, exDot); + final FieldUnivariateDerivative1 eyUD = new FieldUnivariateDerivative1<>(ey, eyDot); + final FieldUnivariateDerivative1 lvUD = FieldEquinoctialLongitudeArgumentUtility.eccentricToTrue(exUD, eyUD, + lEUD); + return lvUD.getFirstDerivative(); + + case TRUE: + return cachedLDot; + + case MEAN: + final FieldUnivariateDerivative1 lMUD = new FieldUnivariateDerivative1<>(cachedL, cachedLDot); + final FieldUnivariateDerivative1 exUD2 = new FieldUnivariateDerivative1<>(ex, exDot); + final FieldUnivariateDerivative1 eyUD2 = new FieldUnivariateDerivative1<>(ey, eyDot); + final FieldUnivariateDerivative1 lvUD2 = FieldEquinoctialLongitudeArgumentUtility.meanToTrue(exUD2, + eyUD2, lMUD); + return lvUD2.getFirstDerivative(); + + default: + throw new OrekitInternalError(null); + } } /** {@inheritDoc} */ + @Override public T getLE() { - return trueToEccentric(lv, ex, ey); + switch (cachedPositionAngleType) { + case TRUE: + return FieldEquinoctialLongitudeArgumentUtility.trueToEccentric(ex, ey, cachedL); + + case ECCENTRIC: + return cachedL; + + case MEAN: + return FieldEquinoctialLongitudeArgumentUtility.meanToEccentric(ex, ey, cachedL); + + default: + throw new OrekitInternalError(null); + } } /** {@inheritDoc} */ + @Override public T getLEDot() { if (!hasDerivatives()) { return null; } - - final FieldUnivariateDerivative1 lVUD = new FieldUnivariateDerivative1<>(lv, lvDot); - final FieldUnivariateDerivative1 exUD = new FieldUnivariateDerivative1<>(ex, exDot); - final FieldUnivariateDerivative1 eyUD = new FieldUnivariateDerivative1<>(ey, eyDot); - final FieldUnivariateDerivative1 lEUD = trueToEccentric(lVUD, exUD, eyUD); - return lEUD.getDerivative(1); - + switch (cachedPositionAngleType) { + case TRUE: + final FieldUnivariateDerivative1 lvUD = new FieldUnivariateDerivative1<>(cachedL, cachedLDot); + final FieldUnivariateDerivative1 exUD = new FieldUnivariateDerivative1<>(ex, exDot); + final FieldUnivariateDerivative1 eyUD = new FieldUnivariateDerivative1<>(ey, eyDot); + final FieldUnivariateDerivative1 lEUD = FieldEquinoctialLongitudeArgumentUtility.trueToEccentric(exUD, eyUD, + lvUD); + return lEUD.getFirstDerivative(); + + case ECCENTRIC: + return cachedLDot; + + case MEAN: + final FieldUnivariateDerivative1 lMUD = new FieldUnivariateDerivative1<>(cachedL, cachedLDot); + final FieldUnivariateDerivative1 exUD2 = new FieldUnivariateDerivative1<>(ex, exDot); + final FieldUnivariateDerivative1 eyUD2 = new FieldUnivariateDerivative1<>(ey, eyDot); + final FieldUnivariateDerivative1 lEUD2 = FieldEquinoctialLongitudeArgumentUtility.meanToEccentric(exUD2, + eyUD2, lMUD); + return lEUD2.getFirstDerivative(); + + default: + throw new OrekitInternalError(null); + } } /** {@inheritDoc} */ + @Override public T getLM() { - return eccentricToMean(trueToEccentric(lv, ex, ey), ex, ey); + switch (cachedPositionAngleType) { + case TRUE: + return FieldEquinoctialLongitudeArgumentUtility.trueToMean(ex, ey, cachedL); + + case MEAN: + return cachedL; + + case ECCENTRIC: + return FieldEquinoctialLongitudeArgumentUtility.eccentricToMean(ex, ey, cachedL); + + default: + throw new OrekitInternalError(null); + } } /** {@inheritDoc} */ + @Override public T getLMDot() { if (!hasDerivatives()) { return null; } - - final FieldUnivariateDerivative1 lVUD = new FieldUnivariateDerivative1<>(lv, lvDot); - final FieldUnivariateDerivative1 exUD = new FieldUnivariateDerivative1<>(ex, exDot); - final FieldUnivariateDerivative1 eyUD = new FieldUnivariateDerivative1<>(ey, eyDot); - final FieldUnivariateDerivative1 lMUD = eccentricToMean(trueToEccentric(lVUD, exUD, eyUD), exUD, eyUD); - return lMUD.getDerivative(1); - + switch (cachedPositionAngleType) { + case TRUE: + final FieldUnivariateDerivative1 lvUD = new FieldUnivariateDerivative1<>(cachedL, cachedLDot); + final FieldUnivariateDerivative1 exUD = new FieldUnivariateDerivative1<>(ex, exDot); + final FieldUnivariateDerivative1 eyUD = new FieldUnivariateDerivative1<>(ey, eyDot); + final FieldUnivariateDerivative1 lMUD = FieldEquinoctialLongitudeArgumentUtility.trueToMean(exUD, eyUD, lvUD); + return lMUD.getFirstDerivative(); + + case MEAN: + return cachedLDot; + + case ECCENTRIC: + final FieldUnivariateDerivative1 lEUD = new FieldUnivariateDerivative1<>(cachedL, cachedLDot); + final FieldUnivariateDerivative1 exUD2 = new FieldUnivariateDerivative1<>(ex, exDot); + final FieldUnivariateDerivative1 eyUD2 = new FieldUnivariateDerivative1<>(ey, eyDot); + final FieldUnivariateDerivative1 lMUD2 = FieldEquinoctialLongitudeArgumentUtility.eccentricToMean(exUD2, + eyUD2, lEUD); + return lMUD2.getFirstDerivative(); + + default: + throw new OrekitInternalError(null); + } } /** Get the longitude argument. @@ -549,14 +698,9 @@ public boolean hasDerivatives() { * @param Type of the field elements * @return the true longitude argument */ + @Deprecated public static > T eccentricToTrue(final T lE, final T ex, final T ey) { - final T epsilon = ex.multiply(ex).add(ey.multiply(ey)).negate().add(1).sqrt(); - final FieldSinCos scLE = FastMath.sinCos(lE); - final T cosLE = scLE.cos(); - final T sinLE = scLE.sin(); - final T num = ex.multiply(sinLE).subtract(ey.multiply(cosLE)); - final T den = epsilon.add(1).subtract(ex.multiply(cosLE)).subtract(ey.multiply(sinLE)); - return lE.add(num.divide(den).atan().multiply(2)); + return FieldEquinoctialLongitudeArgumentUtility.eccentricToTrue(ex, ey, lE); } /** Computes the eccentric longitude argument from the true longitude argument. @@ -566,14 +710,9 @@ public static > T eccentricToTrue(final T lE, * @param Type of the field elements * @return the eccentric longitude argument */ + @Deprecated public static > T trueToEccentric(final T lv, final T ex, final T ey) { - final T epsilon = ex.multiply(ex).add(ey.multiply(ey)).negate().add(1).sqrt(); - final FieldSinCos scLv = FastMath.sinCos(lv); - final T cosLv = scLv.cos(); - final T sinLv = scLv.sin(); - final T num = ey.multiply(cosLv).subtract(ex.multiply(sinLv)); - final T den = epsilon.add(1).add(ex.multiply(cosLv)).add(ey.multiply(sinLv)); - return lv.add(num.divide(den).atan().multiply(2)); + return FieldEquinoctialLongitudeArgumentUtility.trueToEccentric(ex, ey, lv); } /** Computes the eccentric longitude argument from the mean longitude argument. @@ -583,31 +722,9 @@ public static > T trueToEccentric(final T lv, * @param Type of the field elements * @return the eccentric longitude argument */ + @Deprecated public static > T meanToEccentric(final T lM, final T ex, final T ey) { - // Generalization of Kepler equation to equinoctial parameters - // with lE = PA + RAAN + E and - // lM = PA + RAAN + M = lE - ex.sin(lE) + ey.cos(lE) - T lE = lM; - T shift = lM.getField().getZero(); - T lEmlM = lM.getField().getZero(); - FieldSinCos scLE = FastMath.sinCos(lE); - int iter = 0; - do { - final T f2 = ex.multiply(scLE.sin()).subtract(ey.multiply(scLE.cos())); - final T f1 = ex.multiply(scLE.cos()).add(ey.multiply(scLE.sin())).negate().add(1); - final T f0 = lEmlM.subtract(f2); - - final T f12 = f1.multiply(2.0); - shift = f0.multiply(f12).divide(f1.multiply(f12).subtract(f0.multiply(f2))); - - lEmlM = lEmlM.subtract(shift); - lE = lM.add(lEmlM); - scLE = FastMath.sinCos(lE); - - } while (++iter < 50 && FastMath.abs(shift.getReal()) > 1.0e-12); - - return lE; - + return FieldEquinoctialLongitudeArgumentUtility.meanToEccentric(ex, ey, lM); } /** Computes the mean longitude argument from the eccentric longitude argument. @@ -617,40 +734,44 @@ public static > T meanToEccentric(final T lM, * @param Type of the field elements * @return the mean longitude argument */ + @Deprecated public static > T eccentricToMean(final T lE, final T ex, final T ey) { - final FieldSinCos scLE = FastMath.sinCos(lE); - return lE.subtract(ex.multiply(scLE.sin())).add(ey.multiply(scLE.cos())); + return FieldEquinoctialLongitudeArgumentUtility.eccentricToMean(ex, ey, lE); } /** {@inheritDoc} */ + @Override public T getE() { - return ex.multiply(ex).add(ey.multiply(ey)).sqrt(); + return ex.square().add(ey.square()).sqrt(); } /** {@inheritDoc} */ + @Override public T getEDot() { if (!hasDerivatives()) { return null; } - return ex.multiply(exDot).add(ey.multiply(eyDot)).divide(ex.multiply(ex).add(ey.multiply(ey)).sqrt()); + return ex.multiply(exDot).add(ey.multiply(eyDot)).divide(ex.square().add(ey.square()).sqrt()); } /** {@inheritDoc} */ + @Override public T getI() { - return hx.multiply(hx).add(hy.multiply(hy)).sqrt().atan().multiply(2); + return hx.square().add(hy.square()).sqrt().atan().multiply(2); } /** {@inheritDoc} */ + @Override public T getIDot() { if (!hasDerivatives()) { return null; } - final T h2 = hx.multiply(hx).add(hy.multiply(hy)); + final T h2 = hx.square().add(hy.square()); final T h = h2.sqrt(); return hx.multiply(hxDot).add(hy.multiply(hyDot)).multiply(2).divide(h.multiply(h2.add(1))); @@ -669,8 +790,8 @@ private void computePVWithoutA() { final T lE = getLE(); // inclination-related intermediate parameters - final T hx2 = hx.multiply(hx); - final T hy2 = hy.multiply(hy); + final T hx2 = hx.square(); + final T hy2 = hy.square(); final T factH = getOne().divide(hx2.add(1.0).add(hy2)); // reference axes defining the orbital plane @@ -683,9 +804,9 @@ private void computePVWithoutA() { final T vz = hx.multiply(factH).multiply(2); // eccentricity-related intermediate parameters - final T ex2 = ex.multiply(ex); + final T ex2 = ex.square(); final T exey = ex.multiply(ey); - final T ey2 = ey.multiply(ey); + final T ey2 = ey.square(); final T e2 = ex2.add(ey2); final T eta = getOne().subtract(e2).sqrt().add(1); final T beta = getOne().divide(eta); @@ -715,6 +836,95 @@ private void computePVWithoutA() { } + /** Initialize cached argument of longitude with rate. + * @param l input argument of longitude + * @param lDot rate of input argument of longitude + * @param inputType position angle type passed as input + * @return argument of longitude to cache with rate + * @since 12.1 + */ + private FieldUnivariateDerivative1 initializeCachedL(final T l, final T lDot, + final PositionAngleType inputType) { + if (cachedPositionAngleType == inputType) { + return new FieldUnivariateDerivative1<>(l, lDot); + + } else { + final FieldUnivariateDerivative1 exUD = new FieldUnivariateDerivative1<>(ex, exDot); + final FieldUnivariateDerivative1 eyUD = new FieldUnivariateDerivative1<>(ey, eyDot); + final FieldUnivariateDerivative1 lUD = new FieldUnivariateDerivative1<>(l, lDot); + + switch (cachedPositionAngleType) { + + case ECCENTRIC: + if (inputType == PositionAngleType.MEAN) { + return FieldEquinoctialLongitudeArgumentUtility.meanToEccentric(exUD, eyUD, lUD); + } else { + return FieldEquinoctialLongitudeArgumentUtility.trueToEccentric(exUD, eyUD, lUD); + } + + case TRUE: + if (inputType == PositionAngleType.MEAN) { + return FieldEquinoctialLongitudeArgumentUtility.meanToTrue(exUD, eyUD, lUD); + } else { + return FieldEquinoctialLongitudeArgumentUtility.eccentricToTrue(exUD, eyUD, lUD); + } + + case MEAN: + if (inputType == PositionAngleType.TRUE) { + return FieldEquinoctialLongitudeArgumentUtility.trueToMean(exUD, eyUD, lUD); + } else { + return FieldEquinoctialLongitudeArgumentUtility.eccentricToMean(exUD, eyUD, lUD); + } + + default: + throw new OrekitInternalError(null); + + } + + } + + } + + /** Initialize cached argument of longitude. + * @param l input argument of longitude + * @param positionAngleType position angle type passed as input + * @return argument of longitude to cache + * @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); + } + } + } + /** Compute non-Keplerian part of the acceleration from first time derivatives. *

        * This method should be called only when {@link #hasDerivatives()} returns true. @@ -751,14 +961,15 @@ private FieldVector3D nonKeplerianAcceleration() { } /** {@inheritDoc} */ + @Override protected FieldVector3D initPosition() { // get equinoctial parameters final T lE = getLE(); // inclination-related intermediate parameters - final T hx2 = hx.multiply(hx); - final T hy2 = hy.multiply(hy); + final T hx2 = hx.square(); + final T hy2 = hy.square(); final T factH = getOne().divide(hx2.add(1.0).add(hy2)); // reference axes defining the orbital plane @@ -771,9 +982,9 @@ protected FieldVector3D initPosition() { final T vz = hx.multiply(factH).multiply(2); // eccentricity-related intermediate parameters - final T ex2 = ex.multiply(ex); + final T ex2 = ex.square(); final T exey = ex.multiply(ey); - final T ey2 = ey.multiply(ey); + final T ey2 = ey.square(); final T e2 = ex2.add(ey2); final T eta = getOne().subtract(e2).sqrt().add(1); final T beta = getOne().divide(eta); @@ -794,6 +1005,7 @@ protected FieldVector3D initPosition() { } /** {@inheritDoc} */ + @Override protected TimeStampedFieldPVCoordinates initPVCoordinates() { // position and velocity @@ -812,17 +1024,19 @@ protected TimeStampedFieldPVCoordinates initPVCoordinates() { } /** {@inheritDoc} */ + @Override public FieldEquinoctialOrbit shiftedBy(final double dt) { - return shiftedBy(getZero().add(dt)); + return shiftedBy(getZero().newInstance(dt)); } /** {@inheritDoc} */ + @Override public FieldEquinoctialOrbit shiftedBy(final T dt) { // use Keplerian-only motion final FieldEquinoctialOrbit keplerianShifted = new FieldEquinoctialOrbit<>(a, ex, ey, hx, hy, getLM().add(getKeplerianMeanMotion().multiply(dt)), - PositionAngleType.MEAN, getFrame(), + PositionAngleType.MEAN, cachedPositionAngleType, getFrame(), getDate().shiftedBy(dt), getMu()); if (hasDerivatives()) { @@ -833,7 +1047,7 @@ PositionAngleType.MEAN, getFrame(), // add quadratic effect of non-Keplerian acceleration to Keplerian-only shift keplerianShifted.computePVWithoutA(); final FieldVector3D fixedP = new FieldVector3D<>(getOne(), keplerianShifted.partialPV.getPosition(), - dt.multiply(dt).multiply(0.5), nonKeplerianAcceleration); + dt.square().multiply(0.5), nonKeplerianAcceleration); final T fixedR2 = fixedP.getNormSq(); final T fixedR = fixedR2.sqrt(); final FieldVector3D fixedV = new FieldVector3D<>(getOne(), keplerianShifted.partialPV.getVelocity(), @@ -855,6 +1069,7 @@ PositionAngleType.MEAN, getFrame(), } /** {@inheritDoc} */ + @Override protected T[][] computeJacobianMeanWrtCartesian() { final T[][] jacobian = MathArrays.buildArray(getField(), 6, 6); @@ -869,16 +1084,16 @@ protected T[][] computeJacobianMeanWrtCartesian() { final T mu = getMu(); final T sqrtMuA = a.multiply(mu).sqrt(); - final T a2 = a.multiply(a); + final T a2 = a.square(); - final T e2 = ex.multiply(ex).add(ey.multiply(ey)); + final T e2 = ex.square().add(ey.square()); final T oMe2 = getOne().subtract(e2); final T epsilon = oMe2.sqrt(); final T beta = getOne().divide(epsilon.add(1)); final T ratio = epsilon.multiply(beta); - final T hx2 = hx.multiply(hx); - final T hy2 = hy.multiply(hy); + final T hx2 = hx.square(); + final T hy2 = hy.square(); final T hxhy = hx.multiply(hy); // precomputing equinoctial frame unit vectors (f, g, w) @@ -937,13 +1152,14 @@ protected T[][] computeJacobianMeanWrtCartesian() { // dLambdaM final T l = ratio.negate().divide(sqrtMuA); fillHalfRow(getOne().negate().divide(sqrtMuA), velocity, d2, w, l.multiply(ex), drDotSdEx, l.multiply(ey), drDotSdEy, jacobian[5], 0); - fillHalfRow(getZero().add(-2).divide(sqrtMuA), position, ex.multiply(beta), vectorEyRDot, ey.negate().multiply(beta), vectorExRDot, d3, w, jacobian[5], 3); + fillHalfRow(getZero().newInstance(-2).divide(sqrtMuA), position, ex.multiply(beta), vectorEyRDot, ey.negate().multiply(beta), vectorExRDot, d3, w, jacobian[5], 3); return jacobian; } /** {@inheritDoc} */ + @Override protected T[][] computeJacobianEccentricWrtCartesian() { // start by computing the Jacobian with mean angle @@ -971,6 +1187,7 @@ protected T[][] computeJacobianEccentricWrtCartesian() { } /** {@inheritDoc} */ + @Override protected T[][] computeJacobianTrueWrtCartesian() { // start by computing the Jacobian with eccentric angle @@ -993,7 +1210,7 @@ protected T[][] computeJacobianTrueWrtCartesian() { final T sinLe = scLe.sin(); final T eSinE = ex.multiply(sinLe).subtract(ey.multiply(cosLe)); final T ecosE = ex.multiply(cosLe).add(ey.multiply(sinLe)); - final T e2 = ex.multiply(ex).add(ey.multiply(ey)); + final T e2 = ex.square().add(ey.square()); final T epsilon = getOne().subtract(e2).sqrt(); final T onePeps = epsilon.add(1); final T d = onePeps.subtract(ecosE); @@ -1018,23 +1235,25 @@ 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 = gm.divide(a).sqrt().divide(a); - final FieldSinCos sc = FastMath.sinCos(lv); + final FieldSinCos sc; switch (type) { case MEAN : pDot[5] = pDot[5].add(n); break; case ECCENTRIC : - oMe2 = getOne().subtract(ex.multiply(ex)).subtract(ey.multiply(ey)); - ksi = ex.multiply(sc.cos()).add(1).add(ey.multiply(sc.sin())); - pDot[5] = pDot[5].add(n.multiply(ksi).divide(oMe2)); + 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 : - oMe2 = getOne().subtract(ex.multiply(ex)).subtract(ey.multiply(ey)); + 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; @@ -1051,14 +1270,14 @@ public String toString() { append("a: ").append(a.getReal()). append("; ex: ").append(ex.getReal()).append("; ey: ").append(ey.getReal()). append("; hx: ").append(hx.getReal()).append("; hy: ").append(hy.getReal()). - append("; lv: ").append(FastMath.toDegrees(lv.getReal())). + append("; lv: ").append(FastMath.toDegrees(getLv().getReal())). append(";}").toString(); } /** {@inheritDoc} */ @Override public PositionAngleType getCachedPositionAngleType() { - return PositionAngleType.TRUE; + return cachedPositionAngleType; } /** {@inheritDoc} */ @@ -1070,25 +1289,25 @@ public boolean hasRates() { /** {@inheritDoc} */ @Override public FieldEquinoctialOrbit removeRates() { - final PositionAngleType positionAngleType = getCachedPositionAngleType(); return new FieldEquinoctialOrbit<>(getA(), getEquinoctialEx(), getEquinoctialEy(), getHx(), getHy(), - getL(positionAngleType), positionAngleType, getFrame(), getDate(), getMu()); + cachedL, cachedPositionAngleType, getFrame(), getDate(), getMu()); } /** {@inheritDoc} */ @Override public EquinoctialOrbit toOrbit() { + final double cachedPositionAngle = cachedL.getReal(); if (hasDerivatives()) { return new EquinoctialOrbit(a.getReal(), ex.getReal(), ey.getReal(), - hx.getReal(), hy.getReal(), lv.getReal(), + hx.getReal(), hy.getReal(), cachedPositionAngle, aDot.getReal(), exDot.getReal(), eyDot.getReal(), - hxDot.getReal(), hyDot.getReal(), lvDot.getReal(), - PositionAngleType.TRUE, getFrame(), + hxDot.getReal(), hyDot.getReal(), cachedLDot.getReal(), + cachedPositionAngleType, getFrame(), getDate().toAbsoluteDate(), getMu().getReal()); } else { return new EquinoctialOrbit(a.getReal(), ex.getReal(), ey.getReal(), - hx.getReal(), hy.getReal(), lv.getReal(), - PositionAngleType.TRUE, getFrame(), + hx.getReal(), hy.getReal(), cachedPositionAngle, + cachedPositionAngleType, getFrame(), getDate().toAbsoluteDate(), getMu().getReal()); } } diff --git a/src/main/java/org/orekit/orbits/FieldKeplerianAnomalyUtility.java b/src/main/java/org/orekit/orbits/FieldKeplerianAnomalyUtility.java index 9d1613c7a8..f9142043bd 100644 --- a/src/main/java/org/orekit/orbits/FieldKeplerianAnomalyUtility.java +++ b/src/main/java/org/orekit/orbits/FieldKeplerianAnomalyUtility.java @@ -195,13 +195,13 @@ public static > T ellipticMeanToEccentric(fina */ private static > T eMeSinE(final T e, final T E) { T x = (e.negate().add(1)).multiply(E.sin()); - final T mE2 = E.negate().multiply(E); + final T mE2 = E.square().negate(); T term = E; double d = 0; // the inequality test below IS intentional and should NOT be replaced by a // check with a small tolerance for (T x0 = E.getField().getZero().add(Double.NaN); !Double.valueOf(x.getReal()) - .equals(Double.valueOf(x0.getReal()));) { + .equals(x0.getReal());) { d += 2; term = term.multiply(mE2.divide(d * (d + 1))); x0 = x; @@ -290,11 +290,11 @@ public static > T hyperbolicMeanToEccentric(fi final Field field = e.getField(); final T zero = field.getZero(); final T one = field.getOne(); - final T two = zero.add(2.0); - final T three = zero.add(3.0); - final T half = zero.add(0.5); - final T onePointFive = zero.add(1.5); - final T fourThirds = zero.add(4.0).divide(zero.add(3.0)); + final T two = zero.newInstance(2.0); + final T three = zero.newInstance(3.0); + final T half = zero.newInstance(0.5); + final T onePointFive = zero.newInstance(1.5); + final T fourThirds = zero.newInstance(4.0).divide(3.0); // Solve L = S - g * asinh(S) for S = sinh(H). final T L = M.divide(e); @@ -324,14 +324,14 @@ public static > T hyperbolicMeanToEccentric(fi T f; T fd; - if (s0.divide(zero.add(6.0)).add(g1).getReal() >= 0.5) { + if (s0.divide(6.0).add(g1).getReal() >= 0.5) { f = S.subtract(g.multiply(S.asinh())).subtract(L); fd = one.subtract(g.divide(s2)); } else { // Accurate computation of S - (1 - g1) * asinh(S) // when (g1, S) is close to (0, 0). final T t = S.divide(one.add(one.add(S.multiply(S)).sqrt())); - final T tsq = t.multiply(t); + final T tsq = t.square(); T x = S.multiply(g1.add(g.multiply(tsq))); T term = two.multiply(g).multiply(t); T twoI1 = one; diff --git a/src/main/java/org/orekit/orbits/FieldKeplerianOrbit.java b/src/main/java/org/orekit/orbits/FieldKeplerianOrbit.java index 2041d9c128..c4c2113a5a 100644 --- a/src/main/java/org/orekit/orbits/FieldKeplerianOrbit.java +++ b/src/main/java/org/orekit/orbits/FieldKeplerianOrbit.java @@ -98,8 +98,8 @@ public class FieldKeplerianOrbit> extends Fiel /** Right Ascension of Ascending Node (rad). */ private final T raan; - /** True anomaly (rad). */ - private final T v; + /** Cached anomaly (rad). */ + private final T cachedAnomaly; /** Semi-major axis derivative (m/s). */ private final T aDot; @@ -116,8 +116,11 @@ public class FieldKeplerianOrbit> extends Fiel /** Right Ascension of Ascending Node derivative (rad/s). */ private final T raanDot; - /** True anomaly derivative (rad/s). */ - private final T vDot; + /** Derivative of cached anomaly (rad/s). */ + private final T cachedAnomalyDot; + + /** Cached type of position angle. */ + private final PositionAngleType cachedPositionAngleType; /** Partial Cartesian coordinates (position and velocity are valid, acceleration may be missing). */ private FieldPVCoordinates partialPV; @@ -133,6 +136,7 @@ public class FieldKeplerianOrbit> extends Fiel * @param raan right ascension of ascending node (Ω, rad) * @param anomaly mean, eccentric or true anomaly (rad) * @param type type of anomaly + * @param cachedPositionAngleType type of cached anomaly * @param frame the frame in which the parameters are defined * (must be a {@link Frame#isPseudoInertial pseudo-inertial frame}) * @param date date of the orbital parameters @@ -140,15 +144,42 @@ public class FieldKeplerianOrbit> extends Fiel * @exception IllegalArgumentException if frame is not a {@link * Frame#isPseudoInertial pseudo-inertial frame} or a and e don't match for hyperbolic orbits, * or v is out of range for hyperbolic orbits + * @since 12.1 */ public FieldKeplerianOrbit(final T a, final T e, final T i, final T pa, final T raan, final T anomaly, final PositionAngleType type, + final PositionAngleType cachedPositionAngleType, final Frame frame, final FieldAbsoluteDate date, final T mu) throws IllegalArgumentException { this(a, e, i, pa, raan, anomaly, null, null, null, null, null, null, - type, frame, date, mu); + type, cachedPositionAngleType, frame, date, mu); + } + + /** Creates a new instance. + * @param a semi-major axis (m), negative for hyperbolic orbits + * @param e eccentricity (positive or equal to 0) + * @param i inclination (rad) + * @param pa perigee argument (ω, rad) + * @param raan right ascension of ascending node (Ω, rad) + * @param anomaly mean, eccentric or true anomaly (rad) + * @param type type of anomaly + * @param frame the frame in which the parameters are defined + * (must be a {@link Frame#isPseudoInertial pseudo-inertial frame}) + * @param date date of the orbital parameters + * @param mu central attraction coefficient (m³/s²) + * @exception IllegalArgumentException if frame is not a {@link + * Frame#isPseudoInertial pseudo-inertial frame} or a and e don't match for hyperbolic orbits, + * or v is out of range for hyperbolic orbits + * @since 12.1 + */ + public FieldKeplerianOrbit(final T a, final T e, final T i, + final T pa, final T raan, + final T anomaly, final PositionAngleType type, + final Frame frame, final FieldAbsoluteDate date, final T mu) + throws IllegalArgumentException { + this(a, e, i, pa, raan, anomaly, type, type, frame, date, mu); } /** Creates a new instance. @@ -165,6 +196,7 @@ public FieldKeplerianOrbit(final T a, final T e, final T i, * @param raanDot right ascension of ascending node derivative, null if unknown (rad/s) * @param anomalyDot mean, eccentric or true anomaly derivative, null if unknown (rad/s) * @param type type of anomaly + * @param cachedPositionAngleType type of cached anomaly * @param frame the frame in which the parameters are defined * (must be a {@link Frame#isPseudoInertial pseudo-inertial frame}) * @param date date of the orbital parameters @@ -172,15 +204,18 @@ public FieldKeplerianOrbit(final T a, final T e, final T i, * @exception IllegalArgumentException if frame is not a {@link * Frame#isPseudoInertial pseudo-inertial frame} or a and e don't match for hyperbolic orbits, * or v is out of range for hyperbolic orbits + * @since 12.1 */ public FieldKeplerianOrbit(final T a, final T e, final T i, final T pa, final T raan, final T anomaly, final T aDot, final T eDot, final T iDot, final T paDot, final T raanDot, final T anomalyDot, - final PositionAngleType type, + final PositionAngleType type, final PositionAngleType cachedPositionAngleType, final Frame frame, final FieldAbsoluteDate date, final T mu) throws IllegalArgumentException { super(frame, date, mu); + this.cachedPositionAngleType = cachedPositionAngleType; + if (a.multiply(e.negate().add(1)).getReal() < 0) { throw new OrekitIllegalArgumentException(OrekitMessages.ORBIT_A_E_MISMATCH_WITH_CONIC_TYPE, a.getReal(), e.getReal()); } @@ -202,63 +237,61 @@ public FieldKeplerianOrbit(final T a, final T e, final T i, this.PLUS_K = FieldVector3D.getPlusK(a.getField()); // third canonical vector if (hasDerivatives()) { - final FieldUnivariateDerivative1 eUD = new FieldUnivariateDerivative1<>(e, eDot); - final FieldUnivariateDerivative1 anomalyUD = new FieldUnivariateDerivative1<>(anomaly, anomalyDot); - final FieldUnivariateDerivative1 vUD; - switch (type) { - case MEAN : - vUD = (a.getReal() < 0) ? - FieldKeplerianAnomalyUtility.hyperbolicMeanToTrue(eUD, anomalyUD) : - FieldKeplerianAnomalyUtility.ellipticMeanToTrue(eUD, anomalyUD); - break; - case ECCENTRIC : - vUD = (a.getReal() < 0) ? - FieldKeplerianAnomalyUtility.hyperbolicEccentricToTrue(eUD, anomalyUD) : - FieldKeplerianAnomalyUtility.ellipticEccentricToTrue(eUD, anomalyUD); - break; - case TRUE : - vUD = anomalyUD; - break; - default : // this should never happen - throw new OrekitInternalError(null); - } - this.v = vUD.getValue(); - this.vDot = vUD.getDerivative(1); + final FieldUnivariateDerivative1 cachedAnomalyUD = initializeCachedAnomaly(anomaly, anomalyDot, type); + this.cachedAnomaly = cachedAnomalyUD.getValue(); + this.cachedAnomalyDot = cachedAnomalyUD.getFirstDerivative(); } else { - switch (type) { - case MEAN : - - this.v = (a.getReal() < 0) ? - FieldKeplerianAnomalyUtility.hyperbolicMeanToTrue(e, anomaly) : - FieldKeplerianAnomalyUtility.ellipticMeanToTrue(e, anomaly); - - break; - case ECCENTRIC : - this.v = (a.getReal() < 0) ? - FieldKeplerianAnomalyUtility.hyperbolicEccentricToTrue(e, anomaly) : - FieldKeplerianAnomalyUtility.ellipticEccentricToTrue(e, anomaly); - - break; - case TRUE : - this.v = anomaly; - break; - default : // this should never happen - throw new OrekitInternalError(null); - } - this.vDot = null; + this.cachedAnomaly = initializeCachedAnomaly(anomaly, type); + this.cachedAnomalyDot = null; } // check true anomaly range - if (e.multiply(v.cos()).add(1).getReal() <= 0) { - final double vMax = e.reciprocal().negate().acos().getReal(); - throw new OrekitIllegalArgumentException(OrekitMessages.ORBIT_ANOMALY_OUT_OF_HYPERBOLIC_RANGE, - v.getReal(), e.getReal(), -vMax, vMax); + if (!isElliptical()) { + final T trueAnomaly = getTrueAnomaly(); + if (e.multiply(trueAnomaly.cos()).add(1).getReal() <= 0) { + final double vMax = e.reciprocal().negate().acos().getReal(); + throw new OrekitIllegalArgumentException(OrekitMessages.ORBIT_ANOMALY_OUT_OF_HYPERBOLIC_RANGE, + trueAnomaly.getReal(), e.getReal(), -vMax, vMax); + } } this.partialPV = null; } + /** Creates a new instance. + * @param a semi-major axis (m), negative for hyperbolic orbits + * @param e eccentricity (positive or equal to 0) + * @param i inclination (rad) + * @param pa perigee argument (ω, rad) + * @param raan right ascension of ascending node (Ω, rad) + * @param anomaly mean, eccentric or true anomaly (rad) + * @param aDot semi-major axis derivative, null if unknown (m/s) + * @param eDot eccentricity derivative, null if unknown + * @param iDot inclination derivative, null if unknown (rad/s) + * @param paDot perigee argument derivative, null if unknown (rad/s) + * @param raanDot right ascension of ascending node derivative, null if unknown (rad/s) + * @param anomalyDot mean, eccentric or true anomaly derivative, null if unknown (rad/s) + * @param type type of anomaly + * @param frame the frame in which the parameters are defined + * (must be a {@link Frame#isPseudoInertial pseudo-inertial frame}) + * @param date date of the orbital parameters + * @param mu central attraction coefficient (m³/s²) + * @exception IllegalArgumentException if frame is not a {@link + * Frame#isPseudoInertial pseudo-inertial frame} or a and e don't match for hyperbolic orbits, + * or v is out of range for hyperbolic orbits + */ + public FieldKeplerianOrbit(final T a, final T e, final T i, + final T pa, final T raan, final T anomaly, + final T aDot, final T eDot, final T iDot, + final T paDot, final T raanDot, final T anomalyDot, + final PositionAngleType type, + final Frame frame, final FieldAbsoluteDate date, final T mu) + throws IllegalArgumentException { + this(a, e, i, pa, raan, anomaly, aDot, eDot, iDot, paDot, raanDot, anomalyDot, + type, type, frame, date, mu); + } + /** Constructor from Cartesian parameters. * *

        The acceleration provided in {@code FieldPVCoordinates} is accessible using @@ -331,13 +364,15 @@ private FieldKeplerianOrbit(final TimeStampedFieldPVCoordinates pvCoordinates final T eSE = FieldVector3D.dotProduct(pvP, pvV).divide(muA.sqrt()); final T eCE = rV2OnMu.subtract(1); e = (eSE.multiply(eSE).add(eCE.multiply(eCE))).sqrt(); - v = FieldKeplerianAnomalyUtility.ellipticEccentricToTrue(e, eSE.atan2(eCE)); + this.cachedPositionAngleType = PositionAngleType.ECCENTRIC; + cachedAnomaly = eSE.atan2(eCE); } else { // hyperbolic orbit final T eSH = FieldVector3D.dotProduct(pvP, pvV).divide(muA.negate().sqrt()); final T eCH = rV2OnMu.subtract(1); e = (m2.negate().divide(muA).add(1)).sqrt(); - v = FieldKeplerianAnomalyUtility.hyperbolicEccentricToTrue(e, (eCH.add(eSH)).divide(eCH.subtract(eSH)).log().divide(2)); + this.cachedPositionAngleType = PositionAngleType.TRUE; + cachedAnomaly = FieldKeplerianAnomalyUtility.hyperbolicEccentricToTrue(e, (eCH.add(eSH)).divide(eCH.subtract(eSH)).log().divide(2)); } // Checking eccentricity range @@ -347,7 +382,7 @@ private FieldKeplerianOrbit(final TimeStampedFieldPVCoordinates pvCoordinates final FieldVector3D node = new FieldVector3D<>(raan, getZero()); final T px = FieldVector3D.dotProduct(pvP, node); final T py = FieldVector3D.dotProduct(pvP, FieldVector3D.crossProduct(momentum, node)).divide(m2.sqrt()); - pa = py.atan2(px).subtract(v); + pa = py.atan2(px).subtract(getTrueAnomaly()); partialPV = pvCoordinates; @@ -374,10 +409,17 @@ private FieldKeplerianOrbit(final TimeStampedFieldPVCoordinates pvCoordinates add(jacobian[5][3].multiply(aX)).add(jacobian[5][4].multiply(aY)).add(jacobian[5][5].multiply(aZ)); final FieldUnivariateDerivative1 eUD = new FieldUnivariateDerivative1<>(e, eDot); final FieldUnivariateDerivative1 MUD = new FieldUnivariateDerivative1<>(getMeanAnomaly(), MDot); - final FieldUnivariateDerivative1 vUD = (a.getReal() < 0) ? - FieldKeplerianAnomalyUtility.hyperbolicMeanToTrue(eUD, MUD) : - FieldKeplerianAnomalyUtility.ellipticMeanToTrue(eUD, MUD); - vDot = vUD.getDerivative(1); + if (cachedPositionAngleType == PositionAngleType.ECCENTRIC) { + final FieldUnivariateDerivative1 EUD = (a.getReal() < 0) ? + FieldKeplerianAnomalyUtility.hyperbolicMeanToEccentric(eUD, MUD) : + FieldKeplerianAnomalyUtility.ellipticMeanToEccentric(eUD, MUD); + cachedAnomalyDot = EUD.getFirstDerivative(); + } else { // TRUE + final FieldUnivariateDerivative1 vUD = (a.getReal() < 0) ? + FieldKeplerianAnomalyUtility.hyperbolicMeanToTrue(eUD, MUD) : + FieldKeplerianAnomalyUtility.ellipticMeanToTrue(eUD, MUD); + cachedAnomalyDot = vUD.getFirstDerivative(); + } } else { // acceleration is either almost zero or NaN, @@ -388,7 +430,7 @@ private FieldKeplerianOrbit(final TimeStampedFieldPVCoordinates pvCoordinates iDot = null; paDot = null; raanDot = null; - vDot = null; + cachedAnomalyDot = null; } } @@ -428,16 +470,17 @@ public FieldKeplerianOrbit(final FieldOrbit op) { * @since 12.0 */ public FieldKeplerianOrbit(final Field field, final KeplerianOrbit op) { - this(field.getZero().add(op.getA()), field.getZero().add(op.getE()), field.getZero().add(op.getI()), - field.getZero().add(op.getPerigeeArgument()), field.getZero().add(op.getRightAscensionOfAscendingNode()), - field.getZero().add(op.getTrueAnomaly()), - (op.hasDerivatives()) ? field.getZero().add(op.getADot()) : null, - (op.hasDerivatives()) ? field.getZero().add(op.getEDot()) : null, - (op.hasDerivatives()) ? field.getZero().add(op.getIDot()) : null, - (op.hasDerivatives()) ? field.getZero().add(op.getPerigeeArgumentDot()) : null, - (op.hasDerivatives()) ? field.getZero().add(op.getRightAscensionOfAscendingNodeDot()) : null, - (op.hasDerivatives()) ? field.getZero().add(op.getTrueAnomalyDot()) : null, PositionAngleType.TRUE, - op.getFrame(), new FieldAbsoluteDate<>(field, op.getDate()), field.getZero().add(op.getMu())); + this(field.getZero().newInstance(op.getA()), field.getZero().newInstance(op.getE()), field.getZero().newInstance(op.getI()), + field.getZero().newInstance(op.getPerigeeArgument()), field.getZero().newInstance(op.getRightAscensionOfAscendingNode()), + field.getZero().newInstance(op.getAnomaly(op.getCachedPositionAngleType())), + (op.hasDerivatives()) ? field.getZero().newInstance(op.getADot()) : null, + (op.hasDerivatives()) ? field.getZero().newInstance(op.getEDot()) : null, + (op.hasDerivatives()) ? field.getZero().newInstance(op.getIDot()) : null, + (op.hasDerivatives()) ? field.getZero().newInstance(op.getPerigeeArgumentDot()) : null, + (op.hasDerivatives()) ? field.getZero().newInstance(op.getRightAscensionOfAscendingNodeDot()) : null, + (op.hasDerivatives()) ? field.getZero().newInstance(op.getAnomalyDot(op.getCachedPositionAngleType())) : null, + op.getCachedPositionAngleType(), op.getFrame(), + new FieldAbsoluteDate<>(field, op.getDate()), field.getZero().newInstance(op.getMu())); } /** Constructor from Field and Orbit. @@ -447,40 +490,47 @@ public FieldKeplerianOrbit(final Field field, final KeplerianOrbit op) { * @since 12.0 */ public FieldKeplerianOrbit(final Field field, final Orbit op) { - this(field, new KeplerianOrbit(op)); + this(field, (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(op)); } /** {@inheritDoc} */ + @Override public OrbitType getType() { return OrbitType.KEPLERIAN; } /** {@inheritDoc} */ + @Override public T getA() { return a; } /** {@inheritDoc} */ + @Override public T getADot() { return aDot; } /** {@inheritDoc} */ + @Override public T getE() { return e; } /** {@inheritDoc} */ + @Override public T getEDot() { return eDot; } /** {@inheritDoc} */ + @Override public T getI() { return i; } /** {@inheritDoc} */ + @Override public T getIDot() { return iDot; } @@ -523,7 +573,17 @@ public T getRightAscensionOfAscendingNodeDot() { * @return true anomaly (rad) */ public T getTrueAnomaly() { - return v; + switch (cachedPositionAngleType) { + case MEAN: return (a.getReal() < 0) ? FieldKeplerianAnomalyUtility.hyperbolicMeanToTrue(e, cachedAnomaly) : + FieldKeplerianAnomalyUtility.ellipticMeanToTrue(e, cachedAnomaly); + + case TRUE: return cachedAnomaly; + + case ECCENTRIC: return (a.getReal() < 0) ? FieldKeplerianAnomalyUtility.hyperbolicEccentricToTrue(e, cachedAnomaly) : + FieldKeplerianAnomalyUtility.ellipticEccentricToTrue(e, cachedAnomaly); + + default: throw new OrekitInternalError(null); + } } /** Get the true anomaly derivative. @@ -533,16 +593,54 @@ public T getTrueAnomaly() { * @return true anomaly derivative (rad/s) */ public T getTrueAnomalyDot() { - return vDot; + if (hasDerivatives()) { + switch (cachedPositionAngleType) { + case MEAN: + final FieldUnivariateDerivative1 eUD = new FieldUnivariateDerivative1<>(e, eDot); + final FieldUnivariateDerivative1 MUD = new FieldUnivariateDerivative1<>(cachedAnomaly, cachedAnomalyDot); + final FieldUnivariateDerivative1 vUD = (a.getReal() < 0) ? + FieldKeplerianAnomalyUtility.hyperbolicMeanToTrue(eUD, MUD) : + FieldKeplerianAnomalyUtility.ellipticMeanToTrue(eUD, MUD); + return vUD.getFirstDerivative(); + + case TRUE: + return cachedAnomalyDot; + + case ECCENTRIC: + final FieldUnivariateDerivative1 eUD2 = new FieldUnivariateDerivative1<>(e, eDot); + final FieldUnivariateDerivative1 EUD = new FieldUnivariateDerivative1<>(cachedAnomaly, cachedAnomalyDot); + final FieldUnivariateDerivative1 vUD2 = (a.getReal() < 0) ? + FieldKeplerianAnomalyUtility.hyperbolicEccentricToTrue(eUD2, EUD) : + FieldKeplerianAnomalyUtility.ellipticEccentricToTrue(eUD2, EUD); + return vUD2.getFirstDerivative(); + + default: + throw new OrekitInternalError(null); + } + } else { + return null; + } } /** Get the eccentric anomaly. * @return eccentric anomaly (rad) */ public T getEccentricAnomaly() { - return (a.getReal() < 0) ? - FieldKeplerianAnomalyUtility.hyperbolicTrueToEccentric(e, v) : - FieldKeplerianAnomalyUtility.ellipticTrueToEccentric(e, v); + switch (cachedPositionAngleType) { + case MEAN: + return (a.getReal() < 0) ? FieldKeplerianAnomalyUtility.hyperbolicMeanToEccentric(e, cachedAnomaly) : + FieldKeplerianAnomalyUtility.ellipticMeanToEccentric(e, cachedAnomaly); + + case ECCENTRIC: + return cachedAnomaly; + + case TRUE: + return (a.getReal() < 0) ? FieldKeplerianAnomalyUtility.hyperbolicTrueToEccentric(e, cachedAnomaly) : + FieldKeplerianAnomalyUtility.ellipticTrueToEccentric(e, cachedAnomaly); + + default: + throw new OrekitInternalError(null); + } } /** Get the eccentric anomaly derivative. @@ -552,27 +650,50 @@ public T getEccentricAnomaly() { * @return eccentric anomaly derivative (rad/s) */ public T getEccentricAnomalyDot() { - - if (!hasDerivatives()) { + if (hasDerivatives()) { + switch (cachedPositionAngleType) { + case ECCENTRIC: + return cachedAnomalyDot; + + case TRUE: + final FieldUnivariateDerivative1 eUD = new FieldUnivariateDerivative1<>(e, eDot); + final FieldUnivariateDerivative1 vUD = new FieldUnivariateDerivative1<>(cachedAnomaly, cachedAnomalyDot); + final FieldUnivariateDerivative1 EUD = (a.getReal() < 0) ? + FieldKeplerianAnomalyUtility.hyperbolicTrueToEccentric(eUD, vUD) : + FieldKeplerianAnomalyUtility.ellipticTrueToEccentric(eUD, vUD); + return EUD.getFirstDerivative(); + + case MEAN: + final FieldUnivariateDerivative1 eUD2 = new FieldUnivariateDerivative1<>(e, eDot); + final FieldUnivariateDerivative1 MUD = new FieldUnivariateDerivative1<>(cachedAnomaly, cachedAnomalyDot); + final FieldUnivariateDerivative1 EUD2 = (a.getReal() < 0) ? + FieldKeplerianAnomalyUtility.hyperbolicMeanToEccentric(eUD2, MUD) : + FieldKeplerianAnomalyUtility.ellipticMeanToEccentric(eUD2, MUD); + return EUD2.getFirstDerivative(); + + default: + throw new OrekitInternalError(null); + } + } else { return null; } - - final FieldUnivariateDerivative1 eUD = new FieldUnivariateDerivative1<>(e, eDot); - final FieldUnivariateDerivative1 vUD = new FieldUnivariateDerivative1<>(v, vDot); - final FieldUnivariateDerivative1 EUD = (a.getReal() < 0) ? - FieldKeplerianAnomalyUtility.hyperbolicTrueToEccentric(eUD, vUD) : - FieldKeplerianAnomalyUtility.ellipticTrueToEccentric(eUD, vUD); - return EUD.getDerivative(1); - } /** Get the mean anomaly. * @return mean anomaly (rad) */ public T getMeanAnomaly() { - return (a.getReal() < 0) ? - FieldKeplerianAnomalyUtility.hyperbolicTrueToMean(e, v) : - FieldKeplerianAnomalyUtility.ellipticTrueToMean(e, v); + switch (cachedPositionAngleType) { + case ECCENTRIC: return (a.getReal() < 0) ? FieldKeplerianAnomalyUtility.hyperbolicEccentricToMean(e, cachedAnomaly) : + FieldKeplerianAnomalyUtility.ellipticEccentricToMean(e, cachedAnomaly); + + case MEAN: return cachedAnomaly; + + case TRUE: return (a.getReal() < 0) ? FieldKeplerianAnomalyUtility.hyperbolicTrueToMean(e, cachedAnomaly) : + FieldKeplerianAnomalyUtility.ellipticTrueToMean(e, cachedAnomaly); + + default: throw new OrekitInternalError(null); + } } /** Get the mean anomaly derivative. @@ -582,18 +703,33 @@ public T getMeanAnomaly() { * @return mean anomaly derivative (rad/s) */ public T getMeanAnomalyDot() { - - if (!hasDerivatives()) { + if (hasDerivatives()) { + switch (cachedPositionAngleType) { + case MEAN: + return cachedAnomalyDot; + + case ECCENTRIC: + final FieldUnivariateDerivative1 eUD = new FieldUnivariateDerivative1<>(e, eDot); + final FieldUnivariateDerivative1 EUD = new FieldUnivariateDerivative1<>(cachedAnomaly, cachedAnomalyDot); + final FieldUnivariateDerivative1 MUD = (a.getReal() < 0) ? + FieldKeplerianAnomalyUtility.hyperbolicEccentricToMean(eUD, EUD) : + FieldKeplerianAnomalyUtility.ellipticEccentricToMean(eUD, EUD); + return MUD.getFirstDerivative(); + + case TRUE: + final FieldUnivariateDerivative1 eUD2 = new FieldUnivariateDerivative1<>(e, eDot); + final FieldUnivariateDerivative1 vUD = new FieldUnivariateDerivative1<>(cachedAnomaly, cachedAnomalyDot); + final FieldUnivariateDerivative1 MUD2 = (a.getReal() < 0) ? + FieldKeplerianAnomalyUtility.hyperbolicTrueToMean(eUD2, vUD) : + FieldKeplerianAnomalyUtility.ellipticTrueToMean(eUD2, vUD); + return MUD2.getFirstDerivative(); + + default: + throw new OrekitInternalError(null); + } + } else { return null; } - - final FieldUnivariateDerivative1 eUD = new FieldUnivariateDerivative1<>(e, eDot); - final FieldUnivariateDerivative1 vUD = new FieldUnivariateDerivative1<>(v, vDot); - final FieldUnivariateDerivative1 MUD = (a.getReal() < 0) ? - FieldKeplerianAnomalyUtility.hyperbolicTrueToMean(eUD, vUD) : - FieldKeplerianAnomalyUtility.ellipticTrueToMean(eUD, vUD); - return MUD.getDerivative(1); - } /** Get the anomaly. @@ -626,11 +762,13 @@ public boolean hasDerivatives() { } /** {@inheritDoc} */ + @Override public T getEquinoctialEx() { return e.multiply(pa.add(raan).cos()); } /** {@inheritDoc} */ + @Override public T getEquinoctialExDot() { if (!hasDerivatives()) { @@ -640,16 +778,18 @@ public T getEquinoctialExDot() { final FieldUnivariateDerivative1 eUD = new FieldUnivariateDerivative1<>(e, eDot); final FieldUnivariateDerivative1 paUD = new FieldUnivariateDerivative1<>(pa, paDot); final FieldUnivariateDerivative1 raanUD = new FieldUnivariateDerivative1<>(raan, raanDot); - return eUD.multiply(paUD.add(raanUD).cos()).getDerivative(1); + return eUD.multiply(paUD.add(raanUD).cos()).getFirstDerivative(); } /** {@inheritDoc} */ + @Override public T getEquinoctialEy() { - return e.multiply((pa.add(raan)).sin()); + return e.multiply((pa.add(raan)).sin()); } /** {@inheritDoc} */ + @Override public T getEquinoctialEyDot() { if (!hasDerivatives()) { @@ -659,20 +799,22 @@ public T getEquinoctialEyDot() { final FieldUnivariateDerivative1 eUD = new FieldUnivariateDerivative1<>(e, eDot); final FieldUnivariateDerivative1 paUD = new FieldUnivariateDerivative1<>(pa, paDot); final FieldUnivariateDerivative1 raanUD = new FieldUnivariateDerivative1<>(raan, raanDot); - return eUD.multiply(paUD.add(raanUD).sin()).getDerivative(1); + return eUD.multiply(paUD.add(raanUD).sin()).getFirstDerivative(); } /** {@inheritDoc} */ + @Override public T getHx() { // Check for equatorial retrograde orbit if (FastMath.abs(i.subtract(i.getPi()).getReal()) < 1.0e-10) { return getZero().add(Double.NaN); } - return raan.cos().multiply(i.divide(2).tan()); + return raan.cos().multiply(i.divide(2).tan()); } /** {@inheritDoc} */ + @Override public T getHxDot() { if (!hasDerivatives()) { @@ -686,20 +828,22 @@ public T getHxDot() { final FieldUnivariateDerivative1 iUD = new FieldUnivariateDerivative1<>(i, iDot); final FieldUnivariateDerivative1 raanUD = new FieldUnivariateDerivative1<>(raan, raanDot); - return raanUD.cos().multiply(iUD.multiply(0.5).tan()).getDerivative(1); + return raanUD.cos().multiply(iUD.multiply(0.5).tan()).getFirstDerivative(); } /** {@inheritDoc} */ + @Override public T getHy() { // Check for equatorial retrograde orbit if (FastMath.abs(i.subtract(i.getPi()).getReal()) < 1.0e-10) { return getZero().add(Double.NaN); } - return raan.sin().multiply(i.divide(2).tan()); + return raan.sin().multiply(i.divide(2).tan()); } /** {@inheritDoc} */ + @Override public T getHyDot() { if (!hasDerivatives()) { @@ -713,28 +857,32 @@ public T getHyDot() { final FieldUnivariateDerivative1 iUD = new FieldUnivariateDerivative1<>(i, iDot); final FieldUnivariateDerivative1 raanUD = new FieldUnivariateDerivative1<>(raan, raanDot); - return raanUD.sin().multiply(iUD.multiply(0.5).tan()).getDerivative(1); + return raanUD.sin().multiply(iUD.multiply(0.5).tan()).getFirstDerivative(); } /** {@inheritDoc} */ + @Override public T getLv() { - return pa.add(raan).add(v); + return pa.add(raan).add(getTrueAnomaly()); } /** {@inheritDoc} */ + @Override public T getLvDot() { return hasDerivatives() ? - paDot.add(raanDot).add(vDot) : + paDot.add(raanDot).add(getTrueAnomalyDot()) : null; } /** {@inheritDoc} */ + @Override public T getLE() { return pa.add(raan).add(getEccentricAnomaly()); } /** {@inheritDoc} */ + @Override public T getLEDot() { return hasDerivatives() ? paDot.add(raanDot).add(getEccentricAnomalyDot()) : @@ -742,17 +890,164 @@ public T getLEDot() { } /** {@inheritDoc} */ + @Override public T getLM() { return pa.add(raan).add(getMeanAnomaly()); } /** {@inheritDoc} */ + @Override public T getLMDot() { return hasDerivatives() ? paDot.add(raanDot).add(getMeanAnomalyDot()) : null; } + /** Initialize cached anomaly with rate. + * @param anomaly input anomaly + * @param anomalyDot rate of input anomaly + * @param inputType position angle type passed as input + * @return anomaly to cache with rate + * @since 12.1 + */ + private FieldUnivariateDerivative1 initializeCachedAnomaly(final T anomaly, final T anomalyDot, + final PositionAngleType inputType) { + if (cachedPositionAngleType == inputType) { + return new FieldUnivariateDerivative1<>(anomaly, anomalyDot); + + } else { + final FieldUnivariateDerivative1 eUD = new FieldUnivariateDerivative1<>(e, eDot); + final FieldUnivariateDerivative1 anomalyUD = new FieldUnivariateDerivative1<>(anomaly, anomalyDot); + + if (a.getReal() < 0) { + switch (cachedPositionAngleType) { + case MEAN: + if (inputType == PositionAngleType.ECCENTRIC) { + return FieldKeplerianAnomalyUtility.hyperbolicEccentricToMean(eUD, anomalyUD); + } else { + return FieldKeplerianAnomalyUtility.hyperbolicTrueToMean(eUD, anomalyUD); + } + + case ECCENTRIC: + if (inputType == PositionAngleType.MEAN) { + return FieldKeplerianAnomalyUtility.hyperbolicMeanToEccentric(eUD, anomalyUD); + } else { + return FieldKeplerianAnomalyUtility.hyperbolicTrueToEccentric(eUD, anomalyUD); + } + + case TRUE: + if (inputType == PositionAngleType.MEAN) { + return FieldKeplerianAnomalyUtility.hyperbolicMeanToTrue(eUD, anomalyUD); + } else { + return FieldKeplerianAnomalyUtility.hyperbolicEccentricToTrue(eUD, anomalyUD); + } + + default: + break; + } + + } else { + switch (cachedPositionAngleType) { + case MEAN: + if (inputType == PositionAngleType.ECCENTRIC) { + return FieldKeplerianAnomalyUtility.ellipticEccentricToMean(eUD, anomalyUD); + } else { + return FieldKeplerianAnomalyUtility.ellipticTrueToMean(eUD, anomalyUD); + } + + case ECCENTRIC: + if (inputType == PositionAngleType.MEAN) { + return FieldKeplerianAnomalyUtility.ellipticMeanToEccentric(eUD, anomalyUD); + } else { + return FieldKeplerianAnomalyUtility.ellipticTrueToEccentric(eUD, anomalyUD); + } + + case TRUE: + if (inputType == PositionAngleType.MEAN) { + return FieldKeplerianAnomalyUtility.ellipticMeanToTrue(eUD, anomalyUD); + } else { + return FieldKeplerianAnomalyUtility.ellipticEccentricToTrue(eUD, anomalyUD); + } + + default: + break; + } + + } + throw new OrekitInternalError(null); + } + + } + + /** Initialize cached anomaly. + * @param anomaly input anomaly + * @param inputType position angle type passed as input + * @return anomaly to cache + * @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); + } + } + /** Compute reference axes. * @return referecne axes * @since 12.0 @@ -822,10 +1117,10 @@ private void computePVWithoutA() { // hyperbolic case // compute position and velocity factors - final FieldSinCos scV = FastMath.sinCos(v); + final FieldSinCos scV = FastMath.sinCos(getTrueAnomaly()); final T sinV = scV.sin(); final T cosV = scV.cos(); - final T f = a.multiply(e.multiply(e).negate().add(1)); + final T f = a.multiply(e.square().negate().add(1)); final T posFactor = f.divide(e.multiply(cosV).add(1)); final T velFactor = FastMath.sqrt(getMu().divide(f)); @@ -873,6 +1168,7 @@ private FieldVector3D nonKeplerianAcceleration() { } /** {@inheritDoc} */ + @Override protected FieldVector3D initPosition() { final FieldVector3D[] axes = referenceAxes(); @@ -894,10 +1190,10 @@ protected FieldVector3D initPosition() { // hyperbolic case // compute position and velocity factors - final FieldSinCos scV = FastMath.sinCos(v); + final FieldSinCos scV = FastMath.sinCos(getTrueAnomaly()); final T sinV = scV.sin(); final T cosV = scV.cos(); - final T f = a.multiply(e.multiply(e).negate().add(1)); + final T f = a.multiply(e.square().negate().add(1)); final T posFactor = f.divide(e.multiply(cosV).add(1)); return new FieldVector3D<>(posFactor.multiply(cosV), axes[0], posFactor.multiply(sinV), axes[1]); @@ -907,6 +1203,7 @@ protected FieldVector3D initPosition() { } /** {@inheritDoc} */ + @Override protected TimeStampedFieldPVCoordinates initPVCoordinates() { // position and velocity @@ -925,17 +1222,19 @@ protected TimeStampedFieldPVCoordinates initPVCoordinates() { } /** {@inheritDoc} */ + @Override public FieldKeplerianOrbit shiftedBy(final double dt) { - return shiftedBy(getZero().add(dt)); + return shiftedBy(getZero().newInstance(dt)); } /** {@inheritDoc} */ + @Override public FieldKeplerianOrbit shiftedBy(final T dt) { // use Keplerian-only motion final FieldKeplerianOrbit keplerianShifted = new FieldKeplerianOrbit<>(a, e, i, pa, raan, getKeplerianMeanMotion().multiply(dt).add(getMeanAnomaly()), - PositionAngleType.MEAN, getFrame(), getDate().shiftedBy(dt), getMu()); + PositionAngleType.MEAN, cachedPositionAngleType, getFrame(), getDate().shiftedBy(dt), getMu()); if (hasDerivatives()) { @@ -945,7 +1244,7 @@ public FieldKeplerianOrbit shiftedBy(final T dt) { // add quadratic effect of non-Keplerian acceleration to Keplerian-only shift keplerianShifted.computePVWithoutA(); final FieldVector3D fixedP = new FieldVector3D<>(getOne(), keplerianShifted.partialPV.getPosition(), - dt.multiply(dt).multiply(0.5), nonKeplerianAcceleration); + dt.square().multiply(0.5), nonKeplerianAcceleration); final T fixedR2 = fixedP.getNormSq(); final T fixedR = fixedR2.sqrt(); final FieldVector3D fixedV = new FieldVector3D<>(getOne(), keplerianShifted.partialPV.getVelocity(), @@ -967,6 +1266,7 @@ public FieldKeplerianOrbit shiftedBy(final T dt) { } /** {@inheritDoc} */ + @Override protected T[][] computeJacobianMeanWrtCartesian() { if (isElliptical()) { return computeJacobianMeanWrtCartesianElliptical(); @@ -1010,11 +1310,11 @@ private T[][] computeJacobianMeanWrtCartesianElliptical() { final T mu = getMu(); final T sqrtMuA = FastMath.sqrt(a.multiply(mu)); final T sqrtAoMu = FastMath.sqrt(a.divide(mu)); - final T a2 = a.multiply(a); + final T a2 = a.square(); final T twoA = a.multiply(2); final T rOnA = r.divide(a); - final T oMe2 = e.multiply(e).negate().add(1); + final T oMe2 = e.square().negate().add(1); final T epsilon = oMe2.sqrt(); final T sqrtRec = epsilon.reciprocal(); @@ -1149,7 +1449,7 @@ private T[][] computeJacobianMeanWrtCartesianHyperbolic() { final T mu = getMu(); final T absA = a.negate(); final T sqrtMuA = absA.multiply(mu).sqrt(); - final T a2 = a.multiply(a); + final T a2 = a.square(); final T rOa = r.divide(absA); final FieldSinCos scI = FastMath.sinCos(i); @@ -1208,7 +1508,7 @@ private T[][] computeJacobianMeanWrtCartesianHyperbolic() { final FieldVector3D dpoP = new FieldVector3D<>(cP6, dacP, cP7, this.PLUS_K); final FieldVector3D dpoV = new FieldVector3D<>(cP6, dacV); - final T re2 = r2.multiply(e).multiply(e); + final T re2 = r2.multiply(e.square()); final T recOre2 = p.subtract(r).divide(re2); final T resOre2 = pv.multiply(mOMu).divide(re2); final FieldVector3D dreP = new FieldVector3D<>(mOMu, velocity, pv.divide(mu), dCP); @@ -1219,7 +1519,7 @@ private T[][] computeJacobianMeanWrtCartesianHyperbolic() { fillHalfRow(getOne(), dpoV, getOne().negate(), davV, jacobian[3], 3); // dRAAN - final T cO0 = cI1.multiply(cI1); + final T cO0 = cI1.square(); final T cO1 = mx.multiply(cO0); final T cO2 = my.negate().multiply(cO0); fillHalfRow(cO1, dcYP, cO2, dcXP, jacobian[4], 0); @@ -1227,7 +1527,7 @@ private T[][] computeJacobianMeanWrtCartesianHyperbolic() { // dM final T s2a = pv.divide(absA.multiply(2)); - final T oObux = m.multiply(m).add(absA.multiply(mu)).sqrt().reciprocal(); + final T oObux = m.square().add(absA.multiply(mu)).sqrt().reciprocal(); final T scasbu = pv.multiply(oObux); final FieldVector3D dauP = new FieldVector3D<>(sqrtMuA.reciprocal(), velocity, s2a.negate().divide(sqrtMuA), vectorAR); final FieldVector3D dauV = new FieldVector3D<>(sqrtMuA.reciprocal(), position, s2a.negate().divide(sqrtMuA), vectorARDot); @@ -1243,6 +1543,7 @@ private T[][] computeJacobianMeanWrtCartesianHyperbolic() { } /** {@inheritDoc} */ + @Override protected T[][] computeJacobianEccentricWrtCartesian() { if (isElliptical()) { return computeJacobianEccentricWrtCartesianElliptical(); @@ -1317,6 +1618,7 @@ private T[][] computeJacobianEccentricWrtCartesianHyperbolic() { } /** {@inheritDoc} */ + @Override protected T[][] computeJacobianTrueWrtCartesian() { if (isElliptical()) { return computeJacobianTrueWrtCartesianElliptical(); @@ -1342,7 +1644,7 @@ private T[][] computeJacobianTrueWrtCartesianElliptical() { // dE = [sqrt (1 - e^2) / (1 + e cos v)] dv - [sin E / (1 - e^2)] de // which is inverted and rewritten as: // dv = sqrt (1 - e^2) a/r dE + [sin E / sqrt (1 - e^2)] a/r de - final T e2 = e.multiply(e); + final T e2 = e.square(); final T oMe2 = e2.negate().add(1); final T epsilon = oMe2.sqrt(); final FieldSinCos scE = FastMath.sinCos(getEccentricAnomaly()); @@ -1378,7 +1680,7 @@ private T[][] computeJacobianTrueWrtCartesianHyperbolic() { // dH = [sqrt (e^2 - 1) / (1 + e cos v)] dv + [sinh H / (e^2 - 1)] de // which is inverted and rewritten as: // dv = sqrt (1 - e^2) a/r dH - [sinh H / sqrt (e^2 - 1)] a/r de - final T e2 = e.multiply(e); + final T e2 = e.square(); final T e2Mo = e2.subtract(1); final T epsilon = e2Mo.sqrt(); final T H = getEccentricAnomaly(); @@ -1386,7 +1688,7 @@ private T[][] computeJacobianTrueWrtCartesianHyperbolic() { final T sinhH = H.sinh(); final T aOr = e.multiply(coshH).subtract(1).reciprocal(); final T aFactor = epsilon.multiply(aOr); - final T eFactor = sinhH .multiply(aOr).divide(epsilon); + final T eFactor = sinhH.multiply(aOr).divide(epsilon); // update anomaly row final T[] eRow = jacobian[1]; @@ -1400,6 +1702,7 @@ private T[][] computeJacobianTrueWrtCartesianHyperbolic() { } /** {@inheritDoc} */ + @Override public void addKeplerContribution(final PositionAngleType type, final T gm, final T[] pDot) { final T oMe2; @@ -1411,13 +1714,13 @@ public void addKeplerContribution(final PositionAngleType type, final T gm, pDot[5] = pDot[5].add(n); break; case ECCENTRIC : - oMe2 = e.multiply(e).negate().add(1).abs(); - ksi = e.multiply(v.cos()).add(1); + 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.multiply(e).negate().add(1).abs(); - ksi = e.multiply(v.cos()).add(1); + 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 : @@ -1435,14 +1738,14 @@ public String toString() { append("; i: ").append(FastMath.toDegrees(i.getReal())). append("; pa: ").append(FastMath.toDegrees(pa.getReal())). append("; raan: ").append(FastMath.toDegrees(raan.getReal())). - append("; v: ").append(FastMath.toDegrees(v.getReal())). + append("; v: ").append(FastMath.toDegrees(getTrueAnomaly().getReal())). append(";}").toString(); } /** {@inheritDoc} */ @Override public PositionAngleType getCachedPositionAngleType() { - return PositionAngleType.TRUE; + return cachedPositionAngleType; } /** {@inheritDoc} */ @@ -1454,12 +1757,10 @@ public boolean hasRates() { /** {@inheritDoc} */ @Override public FieldKeplerianOrbit removeRates() { - final PositionAngleType positionAngleType = getCachedPositionAngleType(); return new FieldKeplerianOrbit<>(getA(), getE(), getI(), getPerigeeArgument(), getRightAscensionOfAscendingNode(), - getAnomaly(positionAngleType), positionAngleType, getFrame(), getDate(), getMu()); + cachedAnomaly, cachedPositionAngleType, getFrame(), getDate(), getMu()); } - /** Check if the given parameter is within an acceptable range. * The bounds are inclusive: an exception is raised when either of those conditions are met: *

          @@ -1485,17 +1786,19 @@ private void checkParameterRangeInclusive(final String parameterName, final doub /** {@inheritDoc} */ @Override public KeplerianOrbit toOrbit() { + final double cachedPositionAngle = cachedAnomaly.getReal(); if (hasDerivatives()) { return new KeplerianOrbit(a.getReal(), e.getReal(), i.getReal(), - pa.getReal(), raan.getReal(), v.getReal(), + pa.getReal(), raan.getReal(), cachedPositionAngle, aDot.getReal(), eDot.getReal(), iDot.getReal(), - paDot.getReal(), raanDot.getReal(), vDot.getReal(), - PositionAngleType.TRUE, + paDot.getReal(), raanDot.getReal(), + cachedAnomalyDot.getReal(), + cachedPositionAngleType, cachedPositionAngleType, getFrame(), getDate().toAbsoluteDate(), getMu().getReal()); } else { return new KeplerianOrbit(a.getReal(), e.getReal(), i.getReal(), - pa.getReal(), raan.getReal(), v.getReal(), - PositionAngleType.TRUE, + pa.getReal(), raan.getReal(), cachedPositionAngle, + cachedPositionAngleType, cachedPositionAngleType, getFrame(), getDate().toAbsoluteDate(), getMu().getReal()); } } diff --git a/src/main/java/org/orekit/orbits/FieldOrbit.java b/src/main/java/org/orekit/orbits/FieldOrbit.java index 18026cbbb9..a414c26f38 100644 --- a/src/main/java/org/orekit/orbits/FieldOrbit.java +++ b/src/main/java/org/orekit/orbits/FieldOrbit.java @@ -20,6 +20,7 @@ import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; +import org.hipparchus.analysis.differentiation.Derivative; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.linear.FieldDecompositionSolver; import org.hipparchus.linear.FieldLUDecomposition; @@ -152,32 +153,32 @@ protected FieldOrbit(final Frame frame, final FieldAbsoluteDate date, final T * use {@code mu} and the position to compute the acceleration, including * {@link #shiftedBy(CalculusFieldElement)} and {@link #getPVCoordinates(FieldAbsoluteDate, Frame)}. * - * @param FieldPVCoordinates the position and velocity in the inertial frame + * @param fieldPVCoordinates the position and velocity in the inertial frame * @param frame the frame in which the {@link TimeStampedPVCoordinates} are defined * (must be a {@link Frame#isPseudoInertial pseudo-inertial frame}) * @param mu central attraction coefficient (m^3/s^2) * @exception IllegalArgumentException if frame is not a {@link * Frame#isPseudoInertial pseudo-inertial frame} */ - protected FieldOrbit(final TimeStampedFieldPVCoordinates FieldPVCoordinates, final Frame frame, final T mu) + protected FieldOrbit(final TimeStampedFieldPVCoordinates fieldPVCoordinates, final Frame frame, final T mu) throws IllegalArgumentException { ensurePseudoInertialFrame(frame); this.field = mu.getField(); this.zero = this.field.getZero(); this.one = this.field.getOne(); - this.date = FieldPVCoordinates.getDate(); + this.date = fieldPVCoordinates.getDate(); this.mu = mu; - if (FieldPVCoordinates.getAcceleration().getNormSq().getReal() == 0.0) { + if (fieldPVCoordinates.getAcceleration().getNormSq().getReal() == 0.0) { // the acceleration was not provided, // compute it from Newtonian attraction - final T r2 = FieldPVCoordinates.getPosition().getNormSq(); + final T r2 = fieldPVCoordinates.getPosition().getNormSq(); final T r3 = r2.multiply(r2.sqrt()); - this.pvCoordinates = new TimeStampedFieldPVCoordinates<>(FieldPVCoordinates.getDate(), - FieldPVCoordinates.getPosition(), - FieldPVCoordinates.getVelocity(), - new FieldVector3D<>(r3.reciprocal().multiply(mu.negate()), FieldPVCoordinates.getPosition())); + this.pvCoordinates = new TimeStampedFieldPVCoordinates<>(fieldPVCoordinates.getDate(), + fieldPVCoordinates.getPosition(), + fieldPVCoordinates.getVelocity(), + new FieldVector3D<>(r3.reciprocal().multiply(mu.negate()), fieldPVCoordinates.getPosition())); } else { - this.pvCoordinates = FieldPVCoordinates; + this.pvCoordinates = fieldPVCoordinates; } this.frame = frame; } @@ -190,21 +191,32 @@ protected FieldOrbit(final TimeStampedFieldPVCoordinates FieldPVCoordinates, */ protected static > boolean hasNonKeplerianAcceleration(final FieldPVCoordinates pva, final T mu) { - final FieldVector3D a = pva.getAcceleration(); - if (a == null) { - return false; - } + if (mu.getField().getZero() instanceof Derivative) { + return Orbit.hasNonKeplerianAcceleration(pva.toPVCoordinates(), mu.getReal()); // for performance - final FieldVector3D p = pva.getPosition(); - final T r2 = p.getNormSq(); - final T r = r2.sqrt(); - final FieldVector3D keplerianAcceleration = new FieldVector3D<>(r.multiply(r2).reciprocal().multiply(mu.negate()), p); - if (a.getNorm().getReal() > 1.0e-9 * keplerianAcceleration.getNorm().getReal()) { - // we have a relevant acceleration, we can compute derivatives - return true; } else { - // the provided acceleration is either too small to be reliable (probably even 0), or NaN - return false; + final FieldVector3D a = pva.getAcceleration(); + if (a == null) { + return false; + } + + final FieldVector3D p = pva.getPosition(); + final T r2 = p.getNormSq(); + + // Check if acceleration is relatively close to 0 compared to the Keplerian acceleration + final double tolerance = mu.getReal() * 1e-9; + final FieldVector3D aTimesR2 = a.scalarMultiply(r2); + if (aTimesR2.getNorm().getReal() < tolerance) { + return false; + } + + if ((aTimesR2.add(p.normalize().scalarMultiply(mu))).getNorm().getReal() > tolerance) { + // we have a relevant acceleration, we can compute derivatives + return true; + } else { + // the provided acceleration is either too small to be reliable (probably even 0), or NaN + return false; + } } } @@ -463,6 +475,12 @@ public TimeStampedFieldPVCoordinates getPVCoordinates(final FieldAbsoluteDate return shiftedBy(otherDate.durationFrom(getDate())).getPVCoordinates(otherFrame); } + /** {@inheritDoc} */ + @Override + public FieldVector3D getPosition(final FieldAbsoluteDate otherDate, final Frame otherFrame) { + return shiftedBy(otherDate.durationFrom(getDate())).getPosition(otherFrame); + } + /** Get the position in a specified frame. * @param outputFrame frame in which the position coordinates shall be computed * @return position in the specified output frame diff --git a/src/main/java/org/orekit/orbits/KeplerianAnomalyUtility.java b/src/main/java/org/orekit/orbits/KeplerianAnomalyUtility.java index 01dd249080..49936e2454 100644 --- a/src/main/java/org/orekit/orbits/KeplerianAnomalyUtility.java +++ b/src/main/java/org/orekit/orbits/KeplerianAnomalyUtility.java @@ -176,7 +176,7 @@ private static double eMeSinE(final double e, final double E) { double d = 0; // the inequality test below IS intentional and should NOT be replaced by a // check with a small tolerance - for (double x0 = Double.NaN; !Double.valueOf(x).equals(Double.valueOf(x0));) { + for (double x0 = Double.NaN; !Double.valueOf(x).equals(x0);) { d += 2; term *= mE2 / (d * (d + 1)); x0 = x; diff --git a/src/main/java/org/orekit/orbits/KeplerianMotionCartesianUtility.java b/src/main/java/org/orekit/orbits/KeplerianMotionCartesianUtility.java new file mode 100644 index 0000000000..6e27fa993b --- /dev/null +++ b/src/main/java/org/orekit/orbits/KeplerianMotionCartesianUtility.java @@ -0,0 +1,320 @@ +/* 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.orbits; + +import org.hipparchus.CalculusFieldElement; +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.RotationConvention; +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.utils.FieldPVCoordinates; +import org.orekit.utils.PVCoordinates; + +/** + * Utility class to predict position and velocity under Keplerian motion, using lightweight routines based on Cartesian + * coordinates. Computations do not require a reference frame or an epoch. + * + * @author Andrew Goetz + * @author Romain Serra + * @see org.orekit.propagation.analytical.KeplerianPropagator + * @see org.orekit.propagation.analytical.FieldKeplerianPropagator + * @see CartesianOrbit + * @see FieldCartesianOrbit + * @since 12.1 + */ +public class KeplerianMotionCartesianUtility { + + private KeplerianMotionCartesianUtility() { + // utility class + } + + /** + * Method to propagate position and velocity according to Keplerian dynamics. + * For long time of flights, it is preferable to use {@link org.orekit.propagation.analytical.KeplerianPropagator}. + * @param dt time of flight + * @param position initial position vector + * @param velocity initial velocity vector + * @param mu central body gravitational parameter + * @return predicted position-velocity + */ + public static PVCoordinates predictPositionVelocity(final double dt, final Vector3D position, final Vector3D velocity, + final double mu) { + final double r = position.getNorm(); + final double a = r / (2 - r * velocity.getNormSq() / mu); + if (a >= 0.) { + return predictPVElliptic(dt, position, velocity, mu, a, r); + } else { + return predictPVHyperbolic(dt, position, velocity, mu, a, r); + } + } + + /** + * Method to propagate position and velocity according to Keplerian dynamics, in the case of an elliptic trajectory. + * @param dt time of flight + * @param position initial position vector + * @param velocity initial velocity vector + * @param mu central body gravitational parameter + * @param a semi-major axis + * @param r initial radius + * @return predicted position-velocity + */ + private static PVCoordinates predictPVElliptic(final double dt, final Vector3D position, final Vector3D velocity, + final double mu, final double a, final double r) { + // preliminary computation + final Vector3D pvM = position.crossProduct(velocity); + final double muA = mu * a; + + // compute mean anomaly + final double eSE = position.dotProduct(velocity) / FastMath.sqrt(muA); + final double eCE = 1. - r / a; + final double E0 = FastMath.atan2(eSE, eCE); + final double M0 = E0 - eSE; + + final double e = FastMath.sqrt(eCE * eCE + eSE * eSE); + final double sqrt = FastMath.sqrt((1 + e) / (1 - e)); + + // find canonical 2D frame with p pointing to perigee + final double v0 = 2 * FastMath.atan(sqrt * FastMath.tan(E0 / 2)); + final Rotation rotation = new Rotation(pvM, v0, RotationConvention.FRAME_TRANSFORM); + final Vector3D p = rotation.applyTo(position).normalize(); + final Vector3D q = pvM.crossProduct(p).normalize(); + + // compute shifted eccentric anomaly + final double sqrtRatio = FastMath.sqrt(mu / a); + final double meanMotion = sqrtRatio / a; + final double M1 = M0 + meanMotion * dt; + final double E1 = KeplerianAnomalyUtility.ellipticMeanToEccentric(e, M1); + + // compute shifted in-plane Cartesian coordinates + final SinCos scE = FastMath.sinCos(E1); + final double cE = scE.cos(); + final double sE = scE.sin(); + final double sE2m1 = FastMath.sqrt((1 - e) * (1 + e)); + + // coordinates of position and velocity in the orbital plane + final double x = a * (cE - e); + final double y = a * sE2m1 * sE; + final double factor = sqrtRatio / (1 - e * cE); + final double xDot = -factor * sE; + final double yDot = factor * sE2m1 * cE; + + final Vector3D predictedPosition = new Vector3D(x, p, y, q); + final Vector3D predictedVelocity = new Vector3D(xDot, p, yDot, q); + return new PVCoordinates(predictedPosition, predictedVelocity); + } + + /** + * Method to propagate position and velocity according to Keplerian dynamics, in the case of a hyperbolic trajectory. + * @param dt time of flight + * @param position initial position vector + * @param velocity initial velocity vector + * @param mu central body gravitational parameter + * @param a semi-major axis + * @param r initial radius + * @return predicted position-velocity + */ + private static PVCoordinates predictPVHyperbolic(final double dt, final Vector3D position, final Vector3D velocity, + final double mu, final double a, final double r) { + // preliminary computations + final Vector3D pvM = position.crossProduct(velocity); + final double muA = mu * a; + final double e = FastMath.sqrt(1 - pvM.getNormSq() / muA); + final double sqrt = FastMath.sqrt((e + 1) / (e - 1)); + + // compute mean anomaly + final double eSH = position.dotProduct(velocity) / FastMath.sqrt(-muA); + final double eCH = 1. - r / a; + final double H0 = FastMath.log((eCH + eSH) / (eCH - eSH)) / 2; + final double M0 = e * FastMath.sinh(H0) - H0; + + // find canonical 2D frame with p pointing to perigee + final double v0 = 2 * FastMath.atan(sqrt * FastMath.tanh(H0 / 2)); + final Rotation rotation = new Rotation(pvM, v0, RotationConvention.FRAME_TRANSFORM); + final Vector3D p = rotation.applyTo(position).normalize(); + final Vector3D q = pvM.crossProduct(p).normalize(); + + // compute shifted eccentric anomaly + final double absA = FastMath.abs(a); + final double sqrtRatio = FastMath.sqrt(mu / absA); + final double meanMotion = sqrtRatio / absA; + final double M1 = M0 + meanMotion * dt; + final double H1 = KeplerianAnomalyUtility.hyperbolicMeanToEccentric(e, M1); + + // compute shifted in-plane Cartesian coordinates + final double cH = FastMath.cosh(H1); + final double sH = FastMath.sinh(H1); + final double sE2m1 = FastMath.sqrt((e - 1) * (e + 1)); + + // coordinates of position and velocity in the orbital plane + final double x = a * (cH - e); + final double y = -a * sE2m1 * sH; + final double factor = sqrtRatio / (e * cH - 1); + final double xDot = -factor * sH; + final double yDot = factor * sE2m1 * cH; + + final Vector3D predictedPosition = new Vector3D(x, p, y, q); + final Vector3D predictedVelocity = new Vector3D(xDot, p, yDot, q); + return new PVCoordinates(predictedPosition, predictedVelocity); + } + + /** + * Method to propagate position and velocity according to Keplerian dynamics. + * For long time of flights, it is preferable to use {@link org.orekit.propagation.analytical.KeplerianPropagator}. + * @param field type + * @param dt time of flight + * @param position initial position vector + * @param velocity initial velocity vector + * @param mu central body gravitational parameter + * @return predicted position-velocity + */ + public static > FieldPVCoordinates predictPositionVelocity(final T dt, + final FieldVector3D position, + final FieldVector3D velocity, + final T mu) { + final T r = position.getNorm(); + final T a = r.divide(r.multiply(velocity.getNormSq()).divide(mu).negate().add(2)); + if (a.getReal() >= 0.) { + return predictPVElliptic(dt, position, velocity, mu, a, r); + } else { + return predictPVHyperbolic(dt, position, velocity, mu, a, r); + } + } + + /** + * Method to propagate position and velocity according to Keplerian dynamics, in the case of an elliptic trajectory. + * @param field type + * @param dt time of flight + * @param position initial position vector + * @param velocity initial velocity vector + * @param mu central body gravitational parameter + * @param a semi-major axis + * @param r initial radius + * @return predicted position-velocity + */ + private static > FieldPVCoordinates predictPVElliptic(final T dt, + final FieldVector3D position, + final FieldVector3D velocity, + final T mu, final T a, + final T r) { + // preliminary computation0 + final FieldVector3D pvM = position.crossProduct(velocity); + final T muA = mu.multiply(a); + + // compute mean anomaly + final T eSE = position.dotProduct(velocity).divide(muA.sqrt()); + final T eCE = r.divide(a).negate().add(1); + final T E0 = FastMath.atan2(eSE, eCE); + final T M0 = E0.subtract(eSE); + + final T e = eCE.square().add(eSE.square()).sqrt(); + final T ePlusOne = e.add(1); + final T oneMinusE = e.negate().add(1); + final T sqrt = ePlusOne.divide(oneMinusE).sqrt(); + + // find canonical 2D frame with p pointing to perigee + final T v0 = sqrt.multiply((E0.divide(2)).tan()).atan().multiply(2); + final FieldRotation rotation = new FieldRotation<>(pvM, v0, RotationConvention.FRAME_TRANSFORM); + final FieldVector3D p = rotation.applyTo(position).normalize(); + final FieldVector3D q = pvM.crossProduct(p).normalize(); + + // compute shifted eccentric anomaly + final T sqrtRatio = (a.reciprocal().multiply(mu)).sqrt(); + final T meanMotion = sqrtRatio.divide(a); + final T M1 = M0.add(meanMotion.multiply(dt)); + final T E1 = FieldKeplerianAnomalyUtility.ellipticMeanToEccentric(e, M1); + + // compute shifted in-plane Cartesian coordinates + final FieldSinCos scE = FastMath.sinCos(E1); + final T cE = scE.cos(); + final T sE = scE.sin(); + final T sE2m1 = oneMinusE.multiply(ePlusOne).sqrt(); + + // coordinates of position and velocity in the orbital plane + final T x = a.multiply(cE.subtract(e)); + final T y = a.multiply(sE2m1).multiply(sE); + final T factor = sqrtRatio.divide(e.multiply(cE).negate().add(1)); + final T xDot = factor.multiply(sE).negate(); + final T yDot = factor.multiply(sE2m1).multiply(cE); + + final FieldVector3D predictedPosition = new FieldVector3D<>(x, p, y, q); + final FieldVector3D predictedVelocity = new FieldVector3D<>(xDot, p, yDot, q); + return new FieldPVCoordinates<>(predictedPosition, predictedVelocity); + } + + /** + * Method to propagate position and velocity according to Keplerian dynamics, in the case of a hyperbolic trajectory. + * @param field type + * @param dt time of flight + * @param position initial position vector + * @param velocity initial velocity vector + * @param mu central body gravitational parameter + * @param a semi-major axis + * @param r initial radius + * @return predicted position-velocity + */ + private static > FieldPVCoordinates predictPVHyperbolic(final T dt, + final FieldVector3D position, + final FieldVector3D velocity, + final T mu, final T a, + final T r) { + // preliminary computations + final FieldVector3D pvM = position.crossProduct(velocity); + final T muA = a.multiply(mu); + final T e = a.newInstance(1.).subtract(pvM.getNormSq().divide(muA)).sqrt(); + final T ePlusOne = e.add(1); + final T eMinusOne = e.subtract(1); + final T sqrt = ePlusOne.divide(eMinusOne).sqrt(); + + // compute mean anomaly + final T eSH = position.dotProduct(velocity).divide(muA.negate().sqrt()); + final T eCH = r.divide(a).negate().add(1); + final T H0 = eCH.add(eSH).divide(eCH.subtract(eSH)).log().divide(2); + final T M0 = e.multiply(H0.sinh()).subtract(H0); + + // find canonical 2D frame with p pointing to perigee + final T v0 = sqrt.multiply(H0.divide(2).tanh()).atan().multiply(2); + final FieldRotation rotation = new FieldRotation<>(pvM, v0, RotationConvention.FRAME_TRANSFORM); + final FieldVector3D p = rotation.applyTo(position).normalize(); + final FieldVector3D q = pvM.crossProduct(p).normalize(); + + // compute shifted eccentric anomaly + final T sqrtRatio = (a.reciprocal().negate().multiply(mu)).sqrt(); + final T meanMotion = sqrtRatio.divide(a).negate(); + final T M1 = M0.add(meanMotion.multiply(dt)); + final T H1 = FieldKeplerianAnomalyUtility.hyperbolicMeanToEccentric(e, M1); + + // compute shifted in-plane Cartesian coordinates + final T cH = H1.cosh(); + final T sH = H1.sinh(); + final T sE2m1 = eMinusOne.multiply(ePlusOne).sqrt(); + + // coordinates of position and velocity in the orbital plane + final T x = a.multiply(cH.subtract(e)); + final T y = a.negate().multiply(sE2m1).multiply(sH); + final T factor = sqrtRatio.divide(e.multiply(cH).subtract(1)); + final T xDot = factor.negate().multiply(sH); + final T yDot = factor.multiply(sE2m1).multiply(cH); + + final FieldVector3D predictedPosition = new FieldVector3D<>(x, p, y, q); + final FieldVector3D predictedVelocity = new FieldVector3D<>(xDot, p, yDot, q); + return new FieldPVCoordinates<>(predictedPosition, predictedVelocity); + } +} diff --git a/src/main/java/org/orekit/orbits/KeplerianOrbit.java b/src/main/java/org/orekit/orbits/KeplerianOrbit.java index ea9ae6e7e6..f3cb4a06d6 100644 --- a/src/main/java/org/orekit/orbits/KeplerianOrbit.java +++ b/src/main/java/org/orekit/orbits/KeplerianOrbit.java @@ -76,7 +76,7 @@ public class KeplerianOrbit extends Orbit implements PositionAngleBased { /** Serializable UID. */ - private static final long serialVersionUID = 20170414L; + private static final long serialVersionUID = 20231217L; /** Name of the eccentricity parameter. */ private static final String ECCENTRICITY = "eccentricity"; @@ -96,8 +96,8 @@ public class KeplerianOrbit extends Orbit implements PositionAngleBased { /** Right Ascension of Ascending Node (rad). */ private final double raan; - /** True anomaly (rad). */ - private final double v; + /** Cached anomaly (rad). */ + private final double cachedAnomaly; /** Semi-major axis derivative (m/s). */ private final double aDot; @@ -114,8 +114,11 @@ public class KeplerianOrbit extends Orbit implements PositionAngleBased { /** Right Ascension of Ascending Node derivative (rad/s). */ private final double raanDot; - /** True anomaly derivative (rad/s). */ - private final double vDot; + /** Derivative of cached anomaly (rad/s). */ + private final double cachedAnomalyDot; + + /** Cached type of position angle. */ + private final PositionAngleType cachedPositionAngleType; /** Partial Cartesian coordinates (position and velocity are valid, acceleration may be missing). */ private transient PVCoordinates partialPV; @@ -128,6 +131,7 @@ public class KeplerianOrbit extends Orbit implements PositionAngleBased { * @param raan right ascension of ascending node (Ω, rad) * @param anomaly mean, eccentric or true anomaly (rad) * @param type type of anomaly + * @param cachedPositionAngleType type of cached anomaly * @param frame the frame in which the parameters are defined * (must be a {@link Frame#isPseudoInertial pseudo-inertial frame}) * @param date date of the orbital parameters @@ -135,15 +139,40 @@ public class KeplerianOrbit extends Orbit implements PositionAngleBased { * @exception IllegalArgumentException if frame is not a {@link * Frame#isPseudoInertial pseudo-inertial frame} or a and e don't match for hyperbolic orbits, * or v is out of range for hyperbolic orbits + * @since 12.1 */ public KeplerianOrbit(final double a, final double e, final double i, final double pa, final double raan, final double anomaly, - final PositionAngleType type, + final PositionAngleType type, final PositionAngleType cachedPositionAngleType, final Frame frame, final AbsoluteDate date, final double mu) - throws IllegalArgumentException { + throws IllegalArgumentException { this(a, e, i, pa, raan, anomaly, - Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN, - type, frame, date, mu); + Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN, + type, cachedPositionAngleType, frame, date, mu); + } + + /** Creates a new instance without derivatives and with cached position angle same as value inputted. + * @param a semi-major axis (m), negative for hyperbolic orbits + * @param e eccentricity (positive or equal to 0) + * @param i inclination (rad) + * @param pa perigee argument (ω, rad) + * @param raan right ascension of ascending node (Ω, rad) + * @param anomaly mean, eccentric or true anomaly (rad) + * @param type type of anomaly + * @param frame the frame in which the parameters are defined + * (must be a {@link Frame#isPseudoInertial pseudo-inertial frame}) + * @param date date of the orbital parameters + * @param mu central attraction coefficient (m³/s²) + * @exception IllegalArgumentException if frame is not a {@link + * Frame#isPseudoInertial pseudo-inertial frame} or a and e don't match for hyperbolic orbits, + * or v is out of range for hyperbolic orbits + */ + public KeplerianOrbit(final double a, final double e, final double i, + final double pa, final double raan, final double anomaly, + final PositionAngleType type, + final Frame frame, final AbsoluteDate date, final double mu) + throws IllegalArgumentException { + this(a, e, i, pa, raan, anomaly, type, type, frame, date, mu); } /** Creates a new instance. @@ -159,7 +188,8 @@ public KeplerianOrbit(final double a, final double e, final double i, * @param paDot perigee argument derivative (rad/s) * @param raanDot right ascension of ascending node derivative (rad/s) * @param anomalyDot mean, eccentric or true anomaly derivative (rad/s) - * @param type type of anomaly + * @param type type of input anomaly + * @param cachedPositionAngleType type of cached anomaly * @param frame the frame in which the parameters are defined * (must be a {@link Frame#isPseudoInertial pseudo-inertial frame}) * @param date date of the orbital parameters @@ -167,16 +197,17 @@ public KeplerianOrbit(final double a, final double e, final double i, * @exception IllegalArgumentException if frame is not a {@link * Frame#isPseudoInertial pseudo-inertial frame} or a and e don't match for hyperbolic orbits, * or v is out of range for hyperbolic orbits - * @since 9.0 + * @since 12.1 */ public KeplerianOrbit(final double a, final double e, final double i, final double pa, final double raan, final double anomaly, final double aDot, final double eDot, final double iDot, final double paDot, final double raanDot, final double anomalyDot, - final PositionAngleType type, + final PositionAngleType type, final PositionAngleType cachedPositionAngleType, final Frame frame, final AbsoluteDate date, final double mu) - throws IllegalArgumentException { + throws IllegalArgumentException { super(frame, date, mu); + this.cachedPositionAngleType = cachedPositionAngleType; if (a * (1 - e) < 0) { throw new OrekitIllegalArgumentException(OrekitMessages.ORBIT_A_E_MISMATCH_WITH_CONIC_TYPE, a, e); @@ -197,60 +228,62 @@ public KeplerianOrbit(final double a, final double e, final double i, this.raanDot = raanDot; if (hasDerivatives()) { - final UnivariateDerivative1 eUD = new UnivariateDerivative1(e, eDot); - final UnivariateDerivative1 anomalyUD = new UnivariateDerivative1(anomaly, anomalyDot); - final UnivariateDerivative1 vUD; - switch (type) { - case MEAN : - vUD = (a < 0) ? - FieldKeplerianAnomalyUtility.hyperbolicMeanToTrue(eUD, anomalyUD) : - FieldKeplerianAnomalyUtility.ellipticMeanToTrue(eUD, anomalyUD); - break; - case ECCENTRIC : - vUD = (a < 0) ? - FieldKeplerianAnomalyUtility.hyperbolicEccentricToTrue(eUD, anomalyUD) : - FieldKeplerianAnomalyUtility.ellipticEccentricToTrue(eUD, anomalyUD); - break; - case TRUE : - vUD = anomalyUD; - break; - default : // this should never happen - throw new OrekitInternalError(null); - } - this.v = vUD.getValue(); - this.vDot = vUD.getDerivative(1); + final UnivariateDerivative1 cachedAnomalyUD = initializeCachedAnomaly(anomaly, anomalyDot, type); + this.cachedAnomaly = cachedAnomalyUD.getValue(); + this.cachedAnomalyDot = cachedAnomalyUD.getFirstDerivative(); } else { - switch (type) { - case MEAN : - this.v = (a < 0) ? - KeplerianAnomalyUtility.hyperbolicMeanToTrue(e, anomaly) : - KeplerianAnomalyUtility.ellipticMeanToTrue(e, anomaly); - break; - case ECCENTRIC : - this.v = (a < 0) ? - KeplerianAnomalyUtility.hyperbolicEccentricToTrue(e, anomaly) : - KeplerianAnomalyUtility.ellipticEccentricToTrue(e, anomaly); - break; - case TRUE : - this.v = anomaly; - break; - default : // this should never happen - throw new OrekitInternalError(null); - } - this.vDot = Double.NaN; + this.cachedAnomaly = initializeCachedAnomaly(anomaly, type); + this.cachedAnomalyDot = Double.NaN; } // check true anomaly range - if (1 + e * FastMath.cos(v) <= 0) { - final double vMax = FastMath.acos(-1 / e); - throw new OrekitIllegalArgumentException(OrekitMessages.ORBIT_ANOMALY_OUT_OF_HYPERBOLIC_RANGE, - v, e, -vMax, vMax); + if (!isElliptical()) { + final double trueAnomaly = getTrueAnomaly(); + if (1 + e * FastMath.cos(trueAnomaly) <= 0) { + final double vMax = FastMath.acos(-1 / e); + throw new OrekitIllegalArgumentException(OrekitMessages.ORBIT_ANOMALY_OUT_OF_HYPERBOLIC_RANGE, + trueAnomaly, e, -vMax, vMax); + } } this.partialPV = null; } + /** Creates a new instance with cached position angle same as value inputted. + * @param a semi-major axis (m), negative for hyperbolic orbits + * @param e eccentricity (positive or equal to 0) + * @param i inclination (rad) + * @param pa perigee argument (ω, rad) + * @param raan right ascension of ascending node (Ω, rad) + * @param anomaly mean, eccentric or true anomaly (rad) + * @param aDot semi-major axis derivative (m/s) + * @param eDot eccentricity derivative + * @param iDot inclination derivative (rad/s) + * @param paDot perigee argument derivative (rad/s) + * @param raanDot right ascension of ascending node derivative (rad/s) + * @param anomalyDot mean, eccentric or true anomaly derivative (rad/s) + * @param type type of anomaly + * @param frame the frame in which the parameters are defined + * (must be a {@link Frame#isPseudoInertial pseudo-inertial frame}) + * @param date date of the orbital parameters + * @param mu central attraction coefficient (m³/s²) + * @exception IllegalArgumentException if frame is not a {@link + * Frame#isPseudoInertial pseudo-inertial frame} or a and e don't match for hyperbolic orbits, + * or v is out of range for hyperbolic orbits + * @since 9.0 + */ + public KeplerianOrbit(final double a, final double e, final double i, + final double pa, final double raan, final double anomaly, + final double aDot, final double eDot, final double iDot, + final double paDot, final double raanDot, final double anomalyDot, + final PositionAngleType type, + final Frame frame, final AbsoluteDate date, final double mu) + throws IllegalArgumentException { + this(a, e, i, pa, raan, anomaly, aDot, eDot, iDot, paDot, raanDot, anomalyDot, type, type, + frame, date, mu); + } + /** Constructor from Cartesian parameters. * *

          The acceleration provided in {@code pvCoordinates} is accessible using @@ -267,7 +300,7 @@ public KeplerianOrbit(final double a, final double e, final double i, */ public KeplerianOrbit(final TimeStampedPVCoordinates pvCoordinates, final Frame frame, final double mu) - throws IllegalArgumentException { + throws IllegalArgumentException { this(pvCoordinates, frame, mu, hasNonKeplerianAcceleration(pvCoordinates, mu)); } @@ -289,7 +322,7 @@ public KeplerianOrbit(final TimeStampedPVCoordinates pvCoordinates, private KeplerianOrbit(final TimeStampedPVCoordinates pvCoordinates, final Frame frame, final double mu, final boolean reliableAcceleration) - throws IllegalArgumentException { + throws IllegalArgumentException { super(pvCoordinates, frame, mu); // compute inclination @@ -313,19 +346,21 @@ private KeplerianOrbit(final TimeStampedPVCoordinates pvCoordinates, a = r / (2 - rV2OnMu); final double muA = mu * a; - // compute true anomaly + // compute cached anomaly if (isElliptical()) { // elliptic or circular orbit final double eSE = Vector3D.dotProduct(pvP, pvV) / FastMath.sqrt(muA); final double eCE = rV2OnMu - 1; e = FastMath.sqrt(eSE * eSE + eCE * eCE); - v = KeplerianAnomalyUtility.ellipticEccentricToTrue(e, FastMath.atan2(eSE, eCE)); + this.cachedPositionAngleType = PositionAngleType.ECCENTRIC; + cachedAnomaly = FastMath.atan2(eSE, eCE); } else { // hyperbolic orbit final double eSH = Vector3D.dotProduct(pvP, pvV) / FastMath.sqrt(-muA); final double eCH = rV2OnMu - 1; e = FastMath.sqrt(1 - m2 / muA); - v = KeplerianAnomalyUtility.hyperbolicEccentricToTrue(e, FastMath.log((eCH + eSH) / (eCH - eSH)) / 2); + this.cachedPositionAngleType = PositionAngleType.TRUE; + cachedAnomaly = KeplerianAnomalyUtility.hyperbolicEccentricToTrue(e, FastMath.log((eCH + eSH) / (eCH - eSH)) / 2); } // Checking eccentricity range @@ -335,7 +370,7 @@ private KeplerianOrbit(final TimeStampedPVCoordinates pvCoordinates, final Vector3D node = new Vector3D(raan, 0.0); final double px = Vector3D.dotProduct(pvP, node); final double py = Vector3D.dotProduct(pvP, Vector3D.crossProduct(momentum, node)) / FastMath.sqrt(m2); - pa = FastMath.atan2(py, px) - v; + pa = FastMath.atan2(py, px) - getTrueAnomaly(); partialPV = pvCoordinates; @@ -356,16 +391,23 @@ private KeplerianOrbit(final TimeStampedPVCoordinates pvCoordinates, paDot = jacobian[3][3] * aX + jacobian[3][4] * aY + jacobian[3][5] * aZ; raanDot = jacobian[4][3] * aX + jacobian[4][4] * aY + jacobian[4][5] * aZ; - // in order to compute true anomaly derivative, we must compute - // mean anomaly derivative including Keplerian motion and convert to true anomaly + // in order to compute cached anomaly derivative, we must compute + // mean anomaly derivative including Keplerian motion and convert to required anomaly final double MDot = getKeplerianMeanMotion() + - jacobian[5][3] * aX + jacobian[5][4] * aY + jacobian[5][5] * aZ; + jacobian[5][3] * aX + jacobian[5][4] * aY + jacobian[5][5] * aZ; final UnivariateDerivative1 eUD = new UnivariateDerivative1(e, eDot); final UnivariateDerivative1 MUD = new UnivariateDerivative1(getMeanAnomaly(), MDot); - final UnivariateDerivative1 vUD = (a < 0) ? - FieldKeplerianAnomalyUtility.hyperbolicMeanToTrue(eUD, MUD) : - FieldKeplerianAnomalyUtility.ellipticMeanToTrue(eUD, MUD); - vDot = vUD.getDerivative(1); + if (cachedPositionAngleType == PositionAngleType.ECCENTRIC) { + final UnivariateDerivative1 EUD = (a < 0) ? + FieldKeplerianAnomalyUtility.hyperbolicMeanToEccentric(eUD, MUD) : + FieldKeplerianAnomalyUtility.ellipticMeanToEccentric(eUD, MUD); + cachedAnomalyDot = EUD.getFirstDerivative(); + } else { // TRUE + final UnivariateDerivative1 vUD = (a < 0) ? + FieldKeplerianAnomalyUtility.hyperbolicMeanToTrue(eUD, MUD) : + FieldKeplerianAnomalyUtility.ellipticMeanToTrue(eUD, MUD); + cachedAnomalyDot = vUD.getFirstDerivative(); + } } else { // acceleration is either almost zero or NaN, @@ -376,7 +418,7 @@ private KeplerianOrbit(final TimeStampedPVCoordinates pvCoordinates, iDot = Double.NaN; paDot = Double.NaN; raanDot = Double.NaN; - vDot = Double.NaN; + cachedAnomalyDot = Double.NaN; } } @@ -398,7 +440,7 @@ private KeplerianOrbit(final TimeStampedPVCoordinates pvCoordinates, */ public KeplerianOrbit(final PVCoordinates pvCoordinates, final Frame frame, final AbsoluteDate date, final double mu) - throws IllegalArgumentException { + throws IllegalArgumentException { this(new TimeStampedPVCoordinates(date, pvCoordinates), frame, mu); } @@ -410,36 +452,43 @@ public KeplerianOrbit(final Orbit op) { } /** {@inheritDoc} */ + @Override public OrbitType getType() { return OrbitType.KEPLERIAN; } /** {@inheritDoc} */ + @Override public double getA() { return a; } /** {@inheritDoc} */ + @Override public double getADot() { return aDot; } /** {@inheritDoc} */ + @Override public double getE() { return e; } /** {@inheritDoc} */ + @Override public double getEDot() { return eDot; } /** {@inheritDoc} */ + @Override public double getI() { return i; } /** {@inheritDoc} */ + @Override public double getIDot() { return iDot; } @@ -484,23 +533,71 @@ public double getRightAscensionOfAscendingNodeDot() { * @return true anomaly (rad) */ public double getTrueAnomaly() { - return v; + switch (cachedPositionAngleType) { + case MEAN: return (a < 0.) ? KeplerianAnomalyUtility.hyperbolicMeanToTrue(e, cachedAnomaly) : + KeplerianAnomalyUtility.ellipticMeanToTrue(e, cachedAnomaly); + + case TRUE: return cachedAnomaly; + + case ECCENTRIC: return (a < 0.) ? KeplerianAnomalyUtility.hyperbolicEccentricToTrue(e, cachedAnomaly) : + KeplerianAnomalyUtility.ellipticEccentricToTrue(e, cachedAnomaly); + + default: throw new OrekitInternalError(null); + } } /** Get the true anomaly derivative. * @return true anomaly derivative (rad/s) */ public double getTrueAnomalyDot() { - return vDot; + if (hasDerivatives()) { + switch (cachedPositionAngleType) { + case MEAN: + final UnivariateDerivative1 eUD = new UnivariateDerivative1(e, eDot); + final UnivariateDerivative1 MUD = new UnivariateDerivative1(cachedAnomaly, cachedAnomalyDot); + final UnivariateDerivative1 vUD = (a < 0) ? + FieldKeplerianAnomalyUtility.hyperbolicMeanToTrue(eUD, MUD) : + FieldKeplerianAnomalyUtility.ellipticMeanToTrue(eUD, MUD); + return vUD.getFirstDerivative(); + + case TRUE: + return cachedAnomalyDot; + + case ECCENTRIC: + final UnivariateDerivative1 eUD2 = new UnivariateDerivative1(e, eDot); + final UnivariateDerivative1 EUD = new UnivariateDerivative1(cachedAnomaly, cachedAnomalyDot); + final UnivariateDerivative1 vUD2 = (a < 0) ? + FieldKeplerianAnomalyUtility.hyperbolicEccentricToTrue(eUD2, EUD) : + FieldKeplerianAnomalyUtility.ellipticEccentricToTrue(eUD2, EUD); + return vUD2.getFirstDerivative(); + + default: + throw new OrekitInternalError(null); + } + } else { + return Double.NaN; + } } /** Get the eccentric anomaly. * @return eccentric anomaly (rad) */ public double getEccentricAnomaly() { - return (a < 0) ? - KeplerianAnomalyUtility.hyperbolicTrueToEccentric(e, v) : - KeplerianAnomalyUtility.ellipticTrueToEccentric(e, v); + switch (cachedPositionAngleType) { + case MEAN: + return (a < 0.) ? KeplerianAnomalyUtility.hyperbolicMeanToEccentric(e, cachedAnomaly) : + KeplerianAnomalyUtility.ellipticMeanToEccentric(e, cachedAnomaly); + + case ECCENTRIC: + return cachedAnomaly; + + case TRUE: + return (a < 0.) ? KeplerianAnomalyUtility.hyperbolicTrueToEccentric(e, cachedAnomaly) : + KeplerianAnomalyUtility.ellipticTrueToEccentric(e, cachedAnomaly); + + default: + throw new OrekitInternalError(null); + } } /** Get the eccentric anomaly derivative. @@ -508,21 +605,50 @@ public double getEccentricAnomaly() { * @since 9.0 */ public double getEccentricAnomalyDot() { - final UnivariateDerivative1 eUD = new UnivariateDerivative1(e, eDot); - final UnivariateDerivative1 vUD = new UnivariateDerivative1(v, vDot); - final UnivariateDerivative1 EUD = (a < 0) ? - FieldKeplerianAnomalyUtility.hyperbolicTrueToEccentric(eUD, vUD) : - FieldKeplerianAnomalyUtility.ellipticTrueToEccentric(eUD, vUD); - return EUD.getDerivative(1); + if (hasDerivatives()) { + switch (cachedPositionAngleType) { + case ECCENTRIC: + return cachedAnomalyDot; + + case TRUE: + final UnivariateDerivative1 eUD = new UnivariateDerivative1(e, eDot); + final UnivariateDerivative1 vUD = new UnivariateDerivative1(cachedAnomaly, cachedAnomalyDot); + final UnivariateDerivative1 EUD = (a < 0) ? + FieldKeplerianAnomalyUtility.hyperbolicTrueToEccentric(eUD, vUD) : + FieldKeplerianAnomalyUtility.ellipticTrueToEccentric(eUD, vUD); + return EUD.getFirstDerivative(); + + case MEAN: + final UnivariateDerivative1 eUD2 = new UnivariateDerivative1(e, eDot); + final UnivariateDerivative1 MUD = new UnivariateDerivative1(cachedAnomaly, cachedAnomalyDot); + final UnivariateDerivative1 EUD2 = (a < 0) ? + FieldKeplerianAnomalyUtility.hyperbolicMeanToEccentric(eUD2, MUD) : + FieldKeplerianAnomalyUtility.ellipticMeanToEccentric(eUD2, MUD); + return EUD2.getFirstDerivative(); + + default: + throw new OrekitInternalError(null); + } + } else { + return Double.NaN; + } } /** Get the mean anomaly. * @return mean anomaly (rad) */ public double getMeanAnomaly() { - return (a < 0) ? - KeplerianAnomalyUtility.hyperbolicTrueToMean(e, v) : - KeplerianAnomalyUtility.ellipticTrueToMean(e, v); + switch (cachedPositionAngleType) { + case ECCENTRIC: return (a < 0.) ? KeplerianAnomalyUtility.hyperbolicEccentricToMean(e, cachedAnomaly) : + KeplerianAnomalyUtility.ellipticEccentricToMean(e, cachedAnomaly); + + case MEAN: return cachedAnomaly; + + case TRUE: return (a < 0.) ? KeplerianAnomalyUtility.hyperbolicTrueToMean(e, cachedAnomaly) : + KeplerianAnomalyUtility.ellipticTrueToMean(e, cachedAnomaly); + + default: throw new OrekitInternalError(null); + } } /** Get the mean anomaly derivative. @@ -530,12 +656,33 @@ public double getMeanAnomaly() { * @since 9.0 */ public double getMeanAnomalyDot() { - final UnivariateDerivative1 eUD = new UnivariateDerivative1(e, eDot); - final UnivariateDerivative1 vUD = new UnivariateDerivative1(v, vDot); - final UnivariateDerivative1 MUD = (a < 0) ? - FieldKeplerianAnomalyUtility.hyperbolicTrueToMean(eUD, vUD) : - FieldKeplerianAnomalyUtility.ellipticTrueToMean(eUD, vUD); - return MUD.getDerivative(1); + if (hasDerivatives()) { + switch (cachedPositionAngleType) { + case MEAN: + return cachedAnomalyDot; + + case ECCENTRIC: + final UnivariateDerivative1 eUD = new UnivariateDerivative1(e, eDot); + final UnivariateDerivative1 EUD = new UnivariateDerivative1(cachedAnomaly, cachedAnomalyDot); + final UnivariateDerivative1 MUD = (a < 0) ? + FieldKeplerianAnomalyUtility.hyperbolicEccentricToMean(eUD, EUD) : + FieldKeplerianAnomalyUtility.ellipticEccentricToMean(eUD, EUD); + return MUD.getFirstDerivative(); + + case TRUE: + final UnivariateDerivative1 eUD2 = new UnivariateDerivative1(e, eDot); + final UnivariateDerivative1 vUD = new UnivariateDerivative1(cachedAnomaly, cachedAnomalyDot); + final UnivariateDerivative1 MUD2 = (a < 0) ? + FieldKeplerianAnomalyUtility.hyperbolicTrueToMean(eUD2, vUD) : + FieldKeplerianAnomalyUtility.ellipticTrueToMean(eUD2, vUD); + return MUD2.getFirstDerivative(); + + default: + throw new OrekitInternalError(null); + } + } else { + return Double.NaN; + } } /** Get the anomaly. @@ -544,8 +691,8 @@ public double getMeanAnomalyDot() { */ public double getAnomaly(final PositionAngleType type) { return (type == PositionAngleType.MEAN) ? getMeanAnomaly() : - ((type == PositionAngleType.ECCENTRIC) ? getEccentricAnomaly() : - getTrueAnomaly()); + ((type == PositionAngleType.ECCENTRIC) ? getEccentricAnomaly() : + getTrueAnomaly()); } /** Get the anomaly derivative. @@ -555,16 +702,18 @@ public double getAnomaly(final PositionAngleType type) { */ public double getAnomalyDot(final PositionAngleType type) { return (type == PositionAngleType.MEAN) ? getMeanAnomalyDot() : - ((type == PositionAngleType.ECCENTRIC) ? getEccentricAnomalyDot() : - getTrueAnomalyDot()); + ((type == PositionAngleType.ECCENTRIC) ? getEccentricAnomalyDot() : + getTrueAnomalyDot()); } /** {@inheritDoc} */ + @Override public double getEquinoctialEx() { return e * FastMath.cos(pa + raan); } /** {@inheritDoc} */ + @Override public double getEquinoctialExDot() { final double paPraan = pa + raan; final SinCos sc = FastMath.sinCos(paPraan); @@ -572,11 +721,13 @@ public double getEquinoctialExDot() { } /** {@inheritDoc} */ + @Override public double getEquinoctialEy() { return e * FastMath.sin(pa + raan); } /** {@inheritDoc} */ + @Override public double getEquinoctialEyDot() { final double paPraan = pa + raan; final SinCos sc = FastMath.sinCos(paPraan); @@ -584,6 +735,7 @@ public double getEquinoctialEyDot() { } /** {@inheritDoc} */ + @Override public double getHx() { // Check for equatorial retrograde orbit if (FastMath.abs(i - FastMath.PI) < 1.0e-10) { @@ -593,6 +745,7 @@ public double getHx() { } /** {@inheritDoc} */ + @Override public double getHxDot() { // Check for equatorial retrograde orbit if (FastMath.abs(i - FastMath.PI) < 1.0e-10) { @@ -604,15 +757,17 @@ public double getHxDot() { } /** {@inheritDoc} */ + @Override public double getHy() { // Check for equatorial retrograde orbit if (FastMath.abs(i - FastMath.PI) < 1.0e-10) { return Double.NaN; } - return FastMath.sin(raan) * FastMath.tan(0.5 * i); + return FastMath.sin(raan) * FastMath.tan(0.5 * i); } /** {@inheritDoc} */ + @Override public double getHyDot() { // Check for equatorial retrograde orbit if (FastMath.abs(i - FastMath.PI) < 1.0e-10) { @@ -624,35 +779,186 @@ public double getHyDot() { } /** {@inheritDoc} */ + @Override public double getLv() { - return pa + raan + v; + return pa + raan + getTrueAnomaly(); } /** {@inheritDoc} */ + @Override public double getLvDot() { - return paDot + raanDot + vDot; + return paDot + raanDot + getTrueAnomalyDot(); } /** {@inheritDoc} */ + @Override public double getLE() { return pa + raan + getEccentricAnomaly(); } /** {@inheritDoc} */ + @Override public double getLEDot() { return paDot + raanDot + getEccentricAnomalyDot(); } /** {@inheritDoc} */ + @Override public double getLM() { return pa + raan + getMeanAnomaly(); } /** {@inheritDoc} */ + @Override public double getLMDot() { return paDot + raanDot + getMeanAnomalyDot(); } + /** Initialize cached anomaly with rate. + * @param anomaly input anomaly + * @param anomalyDot rate of input anomaly + * @param inputType position angle type passed as input + * @return anomaly to cache with rate + * @since 12.1 + */ + private UnivariateDerivative1 initializeCachedAnomaly(final double anomaly, final double anomalyDot, + final PositionAngleType inputType) { + if (cachedPositionAngleType == inputType) { + return new UnivariateDerivative1(anomaly, anomalyDot); + + } else { + final UnivariateDerivative1 eUD = new UnivariateDerivative1(e, eDot); + final UnivariateDerivative1 anomalyUD = new UnivariateDerivative1(anomaly, anomalyDot); + + if (a < 0) { + switch (cachedPositionAngleType) { + case MEAN: + if (inputType == PositionAngleType.ECCENTRIC) { + return FieldKeplerianAnomalyUtility.hyperbolicEccentricToMean(eUD, anomalyUD); + } else { + return FieldKeplerianAnomalyUtility.hyperbolicTrueToMean(eUD, anomalyUD); + } + + case ECCENTRIC: + if (inputType == PositionAngleType.MEAN) { + return FieldKeplerianAnomalyUtility.hyperbolicMeanToEccentric(eUD, anomalyUD); + } else { + return FieldKeplerianAnomalyUtility.hyperbolicTrueToEccentric(eUD, anomalyUD); + } + + case TRUE: + if (inputType == PositionAngleType.MEAN) { + return FieldKeplerianAnomalyUtility.hyperbolicMeanToTrue(eUD, anomalyUD); + } else { + return FieldKeplerianAnomalyUtility.hyperbolicEccentricToTrue(eUD, anomalyUD); + } + + default: + break; + } + + } else { + switch (cachedPositionAngleType) { + case MEAN: + if (inputType == PositionAngleType.ECCENTRIC) { + return FieldKeplerianAnomalyUtility.ellipticEccentricToMean(eUD, anomalyUD); + } else { + return FieldKeplerianAnomalyUtility.ellipticTrueToMean(eUD, anomalyUD); + } + + case ECCENTRIC: + if (inputType == PositionAngleType.MEAN) { + return FieldKeplerianAnomalyUtility.ellipticMeanToEccentric(eUD, anomalyUD); + } else { + return FieldKeplerianAnomalyUtility.ellipticTrueToEccentric(eUD, anomalyUD); + } + + case TRUE: + if (inputType == PositionAngleType.MEAN) { + return FieldKeplerianAnomalyUtility.ellipticMeanToTrue(eUD, anomalyUD); + } else { + return FieldKeplerianAnomalyUtility.ellipticEccentricToTrue(eUD, anomalyUD); + } + + default: + break; + } + + } + throw new OrekitInternalError(null); + } + + } + + /** Initialize cached anomaly. + * @param anomaly input anomaly + * @param inputType position angle type passed as input + * @return anomaly to cache + * @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); + } + } + /** Compute reference axes. * @return referecne axes * @since 12.0 @@ -720,7 +1026,7 @@ private void computePVWithoutA() { // hyperbolic case // compute position and velocity factors - final SinCos scV = FastMath.sinCos(v); + final SinCos scV = FastMath.sinCos(getTrueAnomaly()); final double sinV = scV.sin(); final double cosV = scV.cos(); final double f = a * (1 - e * e); @@ -753,17 +1059,18 @@ private Vector3D nonKeplerianAcceleration() { final double nonKeplerianMeanMotion = getMeanAnomalyDot() - getKeplerianMeanMotion(); final double nonKeplerianAx = dCdP[3][0] * aDot + dCdP[3][1] * eDot + dCdP[3][2] * iDot + - dCdP[3][3] * paDot + dCdP[3][4] * raanDot + dCdP[3][5] * nonKeplerianMeanMotion; + dCdP[3][3] * paDot + dCdP[3][4] * raanDot + dCdP[3][5] * nonKeplerianMeanMotion; final double nonKeplerianAy = dCdP[4][0] * aDot + dCdP[4][1] * eDot + dCdP[4][2] * iDot + - dCdP[4][3] * paDot + dCdP[4][4] * raanDot + dCdP[4][5] * nonKeplerianMeanMotion; + dCdP[4][3] * paDot + dCdP[4][4] * raanDot + dCdP[4][5] * nonKeplerianMeanMotion; final double nonKeplerianAz = dCdP[5][0] * aDot + dCdP[5][1] * eDot + dCdP[5][2] * iDot + - dCdP[5][3] * paDot + dCdP[5][4] * raanDot + dCdP[5][5] * nonKeplerianMeanMotion; + dCdP[5][3] * paDot + dCdP[5][4] * raanDot + dCdP[5][5] * nonKeplerianMeanMotion; return new Vector3D(nonKeplerianAx, nonKeplerianAy, nonKeplerianAz); } /** {@inheritDoc} */ + @Override protected Vector3D initPosition() { final Vector3D[] axes = referenceAxes(); @@ -786,7 +1093,7 @@ protected Vector3D initPosition() { // hyperbolic case // compute position and velocity factors - final SinCos scV = FastMath.sinCos(v); + final SinCos scV = FastMath.sinCos(getTrueAnomaly()); final double sinV = scV.sin(); final double cosV = scV.cos(); final double f = a * (1 - e * e); @@ -799,6 +1106,7 @@ protected Vector3D initPosition() { } /** {@inheritDoc} */ + @Override protected TimeStampedPVCoordinates initPVCoordinates() { // position and velocity @@ -808,21 +1116,21 @@ protected TimeStampedPVCoordinates initPVCoordinates() { final double r2 = partialPV.getPosition().getNormSq(); final Vector3D keplerianAcceleration = new Vector3D(-getMu() / (r2 * FastMath.sqrt(r2)), partialPV.getPosition()); final Vector3D acceleration = hasDerivatives() ? - keplerianAcceleration.add(nonKeplerianAcceleration()) : - keplerianAcceleration; + keplerianAcceleration.add(nonKeplerianAcceleration()) : + keplerianAcceleration; return new TimeStampedPVCoordinates(getDate(), partialPV.getPosition(), partialPV.getVelocity(), acceleration); } /** {@inheritDoc} */ + @Override public KeplerianOrbit shiftedBy(final double dt) { // use Keplerian-only motion final KeplerianOrbit keplerianShifted = new KeplerianOrbit(a, e, i, pa, raan, - getMeanAnomaly() + getKeplerianMeanMotion() * dt, - PositionAngleType.MEAN, getFrame(), - getDate().shiftedBy(dt), getMu()); + getMeanAnomaly() + getKeplerianMeanMotion() * dt, PositionAngleType.MEAN, + cachedPositionAngleType, getFrame(), getDate().shiftedBy(dt), getMu()); if (hasDerivatives()) { @@ -832,18 +1140,18 @@ PositionAngleType.MEAN, getFrame(), // add quadratic effect of non-Keplerian acceleration to Keplerian-only shift keplerianShifted.computePVWithoutA(); final Vector3D fixedP = new Vector3D(1, keplerianShifted.partialPV.getPosition(), - 0.5 * dt * dt, nonKeplerianAcceleration); + 0.5 * dt * dt, nonKeplerianAcceleration); final double fixedR2 = fixedP.getNormSq(); final double fixedR = FastMath.sqrt(fixedR2); final Vector3D fixedV = new Vector3D(1, keplerianShifted.partialPV.getVelocity(), - dt, nonKeplerianAcceleration); + dt, nonKeplerianAcceleration); final Vector3D fixedA = new Vector3D(-getMu() / (fixedR2 * fixedR), keplerianShifted.partialPV.getPosition(), - 1, nonKeplerianAcceleration); + 1, nonKeplerianAcceleration); // build a new orbit, taking non-Keplerian acceleration into account return new KeplerianOrbit(new TimeStampedPVCoordinates(keplerianShifted.getDate(), - fixedP, fixedV, fixedA), - keplerianShifted.getFrame(), keplerianShifted.getMu()); + fixedP, fixedV, fixedA), + keplerianShifted.getFrame(), keplerianShifted.getMu()); } else { // Keplerian-only motion is all we can do @@ -853,6 +1161,7 @@ PositionAngleType.MEAN, getFrame(), } /** {@inheritDoc} */ + @Override protected double[][] computeJacobianMeanWrtCartesian() { if (isElliptical()) { return computeJacobianMeanWrtCartesianElliptical(); @@ -924,24 +1233,24 @@ private double[][] computeJacobianMeanWrtCartesianElliptical() { // de final double factorER3 = pv / twoA; final Vector3D vectorER = new Vector3D(cosE * v2 / (r * mu), position, - sinE / sqrtMuA, velocity, - -factorER3 * sinE / sqrtMuA, vectorAR); + sinE / sqrtMuA, velocity, + -factorER3 * sinE / sqrtMuA, vectorAR); final Vector3D vectorERDot = new Vector3D(sinE / sqrtMuA, position, - cosE * 2 * r / mu, velocity, - -factorER3 * sinE / sqrtMuA, vectorARDot); + cosE * 2 * r / mu, velocity, + -factorER3 * sinE / sqrtMuA, vectorARDot); fillHalfRow(1, vectorER, jacobian[1], 0); fillHalfRow(1, vectorERDot, jacobian[1], 3); // dE / dr (Eccentric anomaly) final double coefE = cosE / (e * sqrtMuA); final Vector3D vectorEAnR = - new Vector3D(-sinE * v2 / (e * r * mu), position, coefE, velocity, - -factorER3 * coefE, vectorAR); + new Vector3D(-sinE * v2 / (e * r * mu), position, coefE, velocity, + -factorER3 * coefE, vectorAR); // dE / drDot final Vector3D vectorEAnRDot = - new Vector3D(-sinE * 2 * r / (e * mu), velocity, coefE, position, - -factorER3 * coefE, vectorARDot); + new Vector3D(-sinE * 2 * r / (e * mu), velocity, coefE, position, + -factorER3 * coefE, vectorARDot); // precomputing some more factors final double s1 = -sinE * pz / r - cosE * vz * sqrtAoMu; @@ -952,21 +1261,21 @@ private double[][] computeJacobianMeanWrtCartesianElliptical() { final double t3 = sqrtRec * (cosE - e) * vz / (2 * sqrtMuA); final double t4 = sqrtRec * (e * sinI * cosPA * sqrtRec - vz * sqrtAoMu); final Vector3D s = new Vector3D(cosE / r, Vector3D.PLUS_K, - s1, vectorEAnR, - s2, position, - s3, vectorAR); + s1, vectorEAnR, + s2, position, + s3, vectorAR); final Vector3D sDot = new Vector3D(-sinE * sqrtAoMu, Vector3D.PLUS_K, - s1, vectorEAnRDot, - s3, vectorARDot); + s1, vectorEAnRDot, + s3, vectorARDot); final Vector3D t = - new Vector3D(sqrtRec * sinE / r, Vector3D.PLUS_K).add(new Vector3D(t1, vectorEAnR, - t2, position, - t3, vectorAR, - t4, vectorER)); + new Vector3D(sqrtRec * sinE / r, Vector3D.PLUS_K).add(new Vector3D(t1, vectorEAnR, + t2, position, + t3, vectorAR, + t4, vectorER)); final Vector3D tDot = new Vector3D(sqrtRec * (cosE - e) * sqrtAoMu, Vector3D.PLUS_K, - t1, vectorEAnRDot, - t3, vectorARDot, - t4, vectorERDot); + t1, vectorEAnRDot, + t3, vectorARDot, + t4, vectorERDot); // di final double factorI1 = -sinI * sqrtRec / sqrtMuA; @@ -976,9 +1285,9 @@ private double[][] computeJacobianMeanWrtCartesianElliptical() { final double i4 = cosI * sinPA; final double i5 = cosI * cosPA; fillHalfRow(i1, new Vector3D(vy, -vx, 0), i2, vectorAR, i3, vectorER, i4, s, i5, t, - jacobian[2], 0); + jacobian[2], 0); fillHalfRow(i1, new Vector3D(-py, px, 0), i2, vectorARDot, i3, vectorERDot, i4, sDot, i5, tDot, - jacobian[2], 3); + jacobian[2], 3); // dpa fillHalfRow(cosPA / sinI, s, -sinPA / sinI, t, jacobian[3], 0); @@ -987,11 +1296,11 @@ private double[][] computeJacobianMeanWrtCartesianElliptical() { // dRaan final double factorRaanR = 1 / (mu * a * oMe2 * sinI * sinI); fillHalfRow(-factorRaanR * my, new Vector3D( 0, vz, -vy), - factorRaanR * mx, new Vector3D(-vz, 0, vx), - jacobian[4], 0); + factorRaanR * mx, new Vector3D(-vz, 0, vx), + jacobian[4], 0); fillHalfRow(-factorRaanR * my, new Vector3D( 0, -pz, py), - factorRaanR * mx, new Vector3D(pz, 0, -px), - jacobian[4], 3); + factorRaanR * mx, new Vector3D(pz, 0, -px), + jacobian[4], 3); // dM fillHalfRow(rOnA, vectorEAnR, -sinE, vectorER, jacobian[5], 0); @@ -1128,6 +1437,7 @@ private double[][] computeJacobianMeanWrtCartesianHyperbolic() { } /** {@inheritDoc} */ + @Override protected double[][] computeJacobianEccentricWrtCartesian() { if (isElliptical()) { return computeJacobianEccentricWrtCartesianElliptical(); @@ -1201,6 +1511,7 @@ private double[][] computeJacobianEccentricWrtCartesianHyperbolic() { } /** {@inheritDoc} */ + @Override protected double[][] computeJacobianTrueWrtCartesian() { if (isElliptical()) { return computeJacobianTrueWrtCartesianElliptical(); @@ -1286,6 +1597,7 @@ private double[][] computeJacobianTrueWrtCartesianHyperbolic() { } /** {@inheritDoc} */ + @Override public void addKeplerContribution(final PositionAngleType type, final double gm, final double[] pDot) { final double oMe2; @@ -1298,12 +1610,12 @@ public void addKeplerContribution(final PositionAngleType type, final double gm, break; case ECCENTRIC : oMe2 = FastMath.abs(1 - e * e); - ksi = 1 + e * FastMath.cos(v); + 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(v); + ksi = 1 + e * FastMath.cos(getTrueAnomaly()); pDot[5] += n * ksi * ksi / (oMe2 * FastMath.sqrt(oMe2)); break; default : @@ -1316,19 +1628,19 @@ public void addKeplerContribution(final PositionAngleType type, final double gm, */ public String toString() { return new StringBuilder().append("Keplerian parameters: ").append('{'). - append("a: ").append(a). - append("; e: ").append(e). - append("; i: ").append(FastMath.toDegrees(i)). - append("; pa: ").append(FastMath.toDegrees(pa)). - append("; raan: ").append(FastMath.toDegrees(raan)). - append("; v: ").append(FastMath.toDegrees(v)). - append(";}").toString(); + append("a: ").append(a). + append("; e: ").append(e). + append("; i: ").append(FastMath.toDegrees(i)). + append("; pa: ").append(FastMath.toDegrees(pa)). + append("; raan: ").append(FastMath.toDegrees(raan)). + append("; v: ").append(FastMath.toDegrees(getTrueAnomaly())). + append(";}").toString(); } /** {@inheritDoc} */ @Override public PositionAngleType getCachedPositionAngleType() { - return PositionAngleType.TRUE; + return cachedPositionAngleType; } /** {@inheritDoc} */ @@ -1341,8 +1653,8 @@ public boolean hasRates() { @Override public KeplerianOrbit removeRates() { final PositionAngleType positionAngleType = getCachedPositionAngleType(); - return new KeplerianOrbit(getA(), getE(), getI(), getPerigeeArgument(), getRightAscensionOfAscendingNode(), - getAnomaly(positionAngleType), positionAngleType, getFrame(), getDate(), getMu()); + return new KeplerianOrbit(a, e, i, pa, raan, cachedAnomaly, positionAngleType, positionAngleType, + getFrame(), getDate(), getMu()); } /** Check if the given parameter is within an acceptable range. @@ -1363,7 +1675,7 @@ private void checkParameterRangeInclusive(final String parameterName, final doub final double lowerBound, final double upperBound) { if (parameter < lowerBound || parameter > upperBound) { throw new OrekitException(OrekitMessages.INVALID_PARAMETER_RANGE, parameterName, - parameter, lowerBound, upperBound); + parameter, lowerBound, upperBound); } } @@ -1380,10 +1692,13 @@ private Object writeReplace() { private static class DTO implements Serializable { /** Serializable UID. */ - private static final long serialVersionUID = 20170414L; + private static final long serialVersionUID = 20231217L; /** Double values. */ - private double[] d; + private final double[] d; + + /** Type of position angle whose value is cached. */ + private final PositionAngleType positionAngleType; /** Frame in which are defined the orbital parameters. */ private final Frame frame; @@ -1394,6 +1709,7 @@ private static class DTO implements Serializable { private DTO(final KeplerianOrbit orbit) { final TimeStampedPVCoordinates pv = orbit.getPVCoordinates(); + this.positionAngleType = orbit.cachedPositionAngleType; // decompose date final AbsoluteDate j2000Epoch = @@ -1406,16 +1722,16 @@ private DTO(final KeplerianOrbit orbit) { this.d = new double[] { epoch, offset, orbit.getMu(), orbit.a, orbit.e, orbit.i, - orbit.pa, orbit.raan, orbit.v, + orbit.pa, orbit.raan, orbit.cachedAnomaly, orbit.aDot, orbit.eDot, orbit.iDot, - orbit.paDot, orbit.raanDot, orbit.vDot + orbit.paDot, orbit.raanDot, orbit.cachedAnomalyDot }; } else { // we don't have derivatives this.d = new double[] { epoch, offset, orbit.getMu(), orbit.a, orbit.e, orbit.i, - orbit.pa, orbit.raan, orbit.v + orbit.pa, orbit.raan, orbit.cachedAnomaly }; } @@ -1432,15 +1748,14 @@ private Object readResolve() { if (d.length >= 15) { // we have derivatives return new KeplerianOrbit(d[ 3], d[ 4], d[ 5], d[ 6], d[ 7], d[ 8], - d[ 9], d[10], d[11], d[12], d[13], d[14], - PositionAngleType.TRUE, - frame, j2000Epoch.shiftedBy(d[0]).shiftedBy(d[1]), - d[2]); + d[ 9], d[10], d[11], d[12], d[13], d[14], + positionAngleType, positionAngleType, + frame, j2000Epoch.shiftedBy(d[0]).shiftedBy(d[1]), d[2]); } else { // we don't have derivatives - return new KeplerianOrbit(d[3], d[4], d[5], d[6], d[7], d[8], PositionAngleType.TRUE, - frame, j2000Epoch.shiftedBy(d[0]).shiftedBy(d[1]), - d[2]); + return new KeplerianOrbit(d[3], d[4], d[5], d[6], d[7], d[8], + positionAngleType, positionAngleType, + frame, j2000Epoch.shiftedBy(d[0]).shiftedBy(d[1]), d[2]); } } diff --git a/src/main/java/org/orekit/orbits/Orbit.java b/src/main/java/org/orekit/orbits/Orbit.java index 35b6937ff4..598818af96 100644 --- a/src/main/java/org/orekit/orbits/Orbit.java +++ b/src/main/java/org/orekit/orbits/Orbit.java @@ -167,20 +167,22 @@ protected Orbit(final TimeStampedPVCoordinates pvCoordinates, final Frame frame, */ protected static boolean hasNonKeplerianAcceleration(final PVCoordinates pva, final double mu) { + final Vector3D a = pva.getAcceleration(); + if (a == null) { + return false; + } + final Vector3D p = pva.getPosition(); final double r2 = p.getNormSq(); - final double r = FastMath.sqrt(r2); - final Vector3D keplerianAcceleration = new Vector3D(-mu / (r * r2), p); - // Check if acceleration is null or relatively close to 0 compared to the keplerain acceleration - final Vector3D a = pva.getAcceleration(); - if (a == null || a.getNorm() < 1e-9 * keplerianAcceleration.getNorm()) { + // Check if acceleration is relatively close to 0 compared to the Keplerian acceleration + final double tolerance = mu * 1e-9; + final Vector3D aTimesR2 = a.scalarMultiply(r2); + if (aTimesR2.getNorm() < tolerance) { return false; } - final Vector3D nonKeplerianAcceleration = a.subtract(keplerianAcceleration); - - if ( nonKeplerianAcceleration.getNorm() > 1e-9 * keplerianAcceleration.getNorm()) { + if ((aTimesR2.add(p.normalize().scalarMultiply(mu))).getNorm() > tolerance) { // we have a relevant acceleration, we can compute derivatives return true; } else { @@ -461,6 +463,12 @@ public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate otherDate, f return shiftedBy(otherDate.durationFrom(getDate())).getPVCoordinates(otherFrame); } + /** {@inheritDoc} */ + @Override + public Vector3D getPosition(final AbsoluteDate otherDate, final Frame otherFrame) { + return shiftedBy(otherDate.durationFrom(getDate())).getPosition(otherFrame); + } + /** Get the position in a specified frame. * @param outputFrame frame in which the position coordinates shall be computed * @return position in the specified output frame diff --git a/src/main/java/org/orekit/orbits/OrbitType.java b/src/main/java/org/orekit/orbits/OrbitType.java index 28a87bd4ca..a8a592fa05 100644 --- a/src/main/java/org/orekit/orbits/OrbitType.java +++ b/src/main/java/org/orekit/orbits/OrbitType.java @@ -345,6 +345,7 @@ public CircularOrbit normalize(final Orbit orbit, final Orbit reference) { // convert input to proper type final CircularOrbit cO = convertType(orbit); final CircularOrbit cR = convertType(reference); + final PositionAngleType cachedPositionAngleType = cO.getCachedPositionAngleType(); // perform normalization if (cO.hasDerivatives()) { @@ -352,15 +353,17 @@ public CircularOrbit normalize(final Orbit orbit, final Orbit reference) { cO.getCircularEx(), cO.getCircularEy(), cO.getI(), - MathUtils.normalizeAngle(cO.getRightAscensionOfAscendingNode(), cR.getRightAscensionOfAscendingNode()), - MathUtils.normalizeAngle(cO.getAlphaV(), cR.getAlphaV()), + MathUtils.normalizeAngle(cO.getRightAscensionOfAscendingNode(), + cR.getRightAscensionOfAscendingNode()), + MathUtils.normalizeAngle(cO.getAlpha(cachedPositionAngleType), + cR.getAlpha(cachedPositionAngleType)), cO.getADot(), cO.getCircularExDot(), cO.getCircularEyDot(), cO.getIDot(), cO.getRightAscensionOfAscendingNodeDot(), - cO.getAlphaVDot(), - PositionAngleType.TRUE, + cO.getAlphaDot(cachedPositionAngleType), + cachedPositionAngleType, cO.getFrame(), cO.getDate(), cO.getMu()); @@ -369,9 +372,11 @@ public CircularOrbit normalize(final Orbit orbit, final Orbit reference) { cO.getCircularEx(), cO.getCircularEy(), cO.getI(), - MathUtils.normalizeAngle(cO.getRightAscensionOfAscendingNode(), cR.getRightAscensionOfAscendingNode()), - MathUtils.normalizeAngle(cO.getAlphaV(), cR.getAlphaV()), - PositionAngleType.TRUE, + MathUtils.normalizeAngle(cO.getRightAscensionOfAscendingNode(), + cR.getRightAscensionOfAscendingNode()), + MathUtils.normalizeAngle(cO.getAlpha(cachedPositionAngleType), + cR.getAlpha(cachedPositionAngleType)), + cachedPositionAngleType, cO.getFrame(), cO.getDate(), cO.getMu()); @@ -386,6 +391,7 @@ public > FieldCircularOrbit normalize(final // convert input to proper type final FieldCircularOrbit cO = convertType(orbit); final FieldCircularOrbit cR = convertType(reference); + final PositionAngleType positionAngleType = cO.getCachedPositionAngleType(); // perform normalization if (cO.hasDerivatives()) { @@ -393,15 +399,17 @@ public > FieldCircularOrbit normalize(final cO.getCircularEx(), cO.getCircularEy(), cO.getI(), - MathUtils.normalizeAngle(cO.getRightAscensionOfAscendingNode(), cR.getRightAscensionOfAscendingNode()), - MathUtils.normalizeAngle(cO.getAlphaV(), cR.getAlphaV()), + MathUtils.normalizeAngle(cO.getRightAscensionOfAscendingNode(), + cR.getRightAscensionOfAscendingNode()), + MathUtils.normalizeAngle(cO.getAlpha(positionAngleType), + cR.getAlpha(positionAngleType)), cO.getADot(), cO.getCircularExDot(), cO.getCircularEyDot(), cO.getIDot(), cO.getRightAscensionOfAscendingNodeDot(), - cO.getAlphaVDot(), - PositionAngleType.TRUE, + cO.getAlphaDot(positionAngleType), + positionAngleType, cO.getFrame(), cO.getDate(), cO.getMu()); @@ -410,9 +418,11 @@ public > FieldCircularOrbit normalize(final cO.getCircularEx(), cO.getCircularEy(), cO.getI(), - MathUtils.normalizeAngle(cO.getRightAscensionOfAscendingNode(), cR.getRightAscensionOfAscendingNode()), - MathUtils.normalizeAngle(cO.getAlphaV(), cR.getAlphaV()), - PositionAngleType.TRUE, + MathUtils.normalizeAngle(cO.getRightAscensionOfAscendingNode(), + cR.getRightAscensionOfAscendingNode()), + MathUtils.normalizeAngle(cO.getAlpha(positionAngleType), + cR.getAlpha(positionAngleType)), + positionAngleType, cO.getFrame(), cO.getDate(), cO.getMu()); @@ -579,6 +589,7 @@ public EquinoctialOrbit normalize(final Orbit orbit, final Orbit reference) { // convert input to proper type final EquinoctialOrbit eO = convertType(orbit); final EquinoctialOrbit eR = convertType(reference); + final PositionAngleType cachedPositionAngleType = eO.getCachedPositionAngleType(); // perform normalization if (eO.hasDerivatives()) { @@ -587,14 +598,15 @@ public EquinoctialOrbit normalize(final Orbit orbit, final Orbit reference) { eO.getEquinoctialEy(), eO.getHx(), eO.getHy(), - MathUtils.normalizeAngle(eO.getLv(), eR.getLv()), + MathUtils.normalizeAngle(eO.getL(cachedPositionAngleType), + eR.getL(cachedPositionAngleType)), eO.getADot(), eO.getEquinoctialExDot(), eO.getEquinoctialEyDot(), eO.getHxDot(), eO.getHyDot(), - eO.getLvDot(), - PositionAngleType.TRUE, + eO.getLDot(cachedPositionAngleType), + cachedPositionAngleType, eO.getFrame(), eO.getDate(), eO.getMu()); @@ -604,8 +616,9 @@ public EquinoctialOrbit normalize(final Orbit orbit, final Orbit reference) { eO.getEquinoctialEy(), eO.getHx(), eO.getHy(), - MathUtils.normalizeAngle(eO.getLv(), eR.getLv()), - PositionAngleType.TRUE, + MathUtils.normalizeAngle(eO.getL(cachedPositionAngleType), + eR.getL(cachedPositionAngleType)), + cachedPositionAngleType, eO.getFrame(), eO.getDate(), eO.getMu()); @@ -620,6 +633,7 @@ public > FieldEquinoctialOrbit normalize(fi // convert input to proper type final FieldEquinoctialOrbit eO = convertType(orbit); final FieldEquinoctialOrbit eR = convertType(reference); + final PositionAngleType positionAngleType = eO.getCachedPositionAngleType(); // perform normalization if (eO.hasDerivatives()) { @@ -628,14 +642,15 @@ public > FieldEquinoctialOrbit normalize(fi eO.getEquinoctialEy(), eO.getHx(), eO.getHy(), - MathUtils.normalizeAngle(eO.getLv(), eR.getLv()), + MathUtils.normalizeAngle(eO.getL(positionAngleType), + eR.getL(positionAngleType)), eO.getADot(), eO.getEquinoctialExDot(), eO.getEquinoctialEyDot(), eO.getHxDot(), eO.getHyDot(), - eO.getLvDot(), - PositionAngleType.TRUE, + eO.getLDot(positionAngleType), + positionAngleType, eO.getFrame(), eO.getDate(), eO.getMu()); @@ -645,8 +660,9 @@ public > FieldEquinoctialOrbit normalize(fi eO.getEquinoctialEy(), eO.getHx(), eO.getHy(), - MathUtils.normalizeAngle(eO.getLv(), eR.getLv()), - PositionAngleType.TRUE, + MathUtils.normalizeAngle(eO.getL(positionAngleType), + eR.getL(positionAngleType)), + positionAngleType, eO.getFrame(), eO.getDate(), eO.getMu()); @@ -812,6 +828,7 @@ public KeplerianOrbit normalize(final Orbit orbit, final Orbit reference) { // convert input to proper type final KeplerianOrbit kO = convertType(orbit); final KeplerianOrbit kR = convertType(reference); + final PositionAngleType cachedPositionAngleType = kO.getCachedPositionAngleType(); // perform normalization if (kO.hasDerivatives()) { @@ -819,15 +836,17 @@ public KeplerianOrbit normalize(final Orbit orbit, final Orbit reference) { kO.getE(), kO.getI(), MathUtils.normalizeAngle(kO.getPerigeeArgument(), kR.getPerigeeArgument()), - MathUtils.normalizeAngle(kO.getRightAscensionOfAscendingNode(), kR.getRightAscensionOfAscendingNode()), - MathUtils.normalizeAngle(kO.getTrueAnomaly(), kR.getTrueAnomaly()), + MathUtils.normalizeAngle(kO.getRightAscensionOfAscendingNode(), + kR.getRightAscensionOfAscendingNode()), + MathUtils.normalizeAngle(kO.getAnomaly(cachedPositionAngleType), + kR.getAnomaly(cachedPositionAngleType)), kO.getADot(), kO.getEDot(), kO.getIDot(), kO.getPerigeeArgumentDot(), kO.getRightAscensionOfAscendingNodeDot(), - kO.getTrueAnomalyDot(), - PositionAngleType.TRUE, + kO.getAnomalyDot(cachedPositionAngleType), + cachedPositionAngleType, kO.getFrame(), kO.getDate(), kO.getMu()); @@ -837,8 +856,9 @@ public KeplerianOrbit normalize(final Orbit orbit, final Orbit reference) { kO.getI(), MathUtils.normalizeAngle(kO.getPerigeeArgument(), kR.getPerigeeArgument()), MathUtils.normalizeAngle(kO.getRightAscensionOfAscendingNode(), kR.getRightAscensionOfAscendingNode()), - MathUtils.normalizeAngle(kO.getTrueAnomaly(), kR.getTrueAnomaly()), - PositionAngleType.TRUE, + MathUtils.normalizeAngle(kO.getAnomaly(cachedPositionAngleType), + kR.getAnomaly(cachedPositionAngleType)), + cachedPositionAngleType, kO.getFrame(), kO.getDate(), kO.getMu()); @@ -853,6 +873,7 @@ public > FieldKeplerianOrbit normalize(fina // convert input to proper type final FieldKeplerianOrbit kO = convertType(orbit); final FieldKeplerianOrbit kR = convertType(reference); + final PositionAngleType positionAngleType = kO.getCachedPositionAngleType(); // perform normalization if (kO.hasDerivatives()) { @@ -860,15 +881,17 @@ public > FieldKeplerianOrbit normalize(fina kO.getE(), kO.getI(), MathUtils.normalizeAngle(kO.getPerigeeArgument(), kR.getPerigeeArgument()), - MathUtils.normalizeAngle(kO.getRightAscensionOfAscendingNode(), kR.getRightAscensionOfAscendingNode()), - MathUtils.normalizeAngle(kO.getTrueAnomaly(), kR.getTrueAnomaly()), + MathUtils.normalizeAngle(kO.getRightAscensionOfAscendingNode(), + kR.getRightAscensionOfAscendingNode()), + MathUtils.normalizeAngle(kO.getAnomaly(positionAngleType), + kR.getAnomaly(positionAngleType)), kO.getADot(), kO.getEDot(), kO.getIDot(), kO.getPerigeeArgumentDot(), kO.getRightAscensionOfAscendingNodeDot(), - kO.getTrueAnomalyDot(), - PositionAngleType.TRUE, + kO.getAnomalyDot(positionAngleType), + positionAngleType, kO.getFrame(), kO.getDate(), kO.getMu()); @@ -877,9 +900,11 @@ public > FieldKeplerianOrbit normalize(fina kO.getE(), kO.getI(), MathUtils.normalizeAngle(kO.getPerigeeArgument(), kR.getPerigeeArgument()), - MathUtils.normalizeAngle(kO.getRightAscensionOfAscendingNode(), kR.getRightAscensionOfAscendingNode()), - MathUtils.normalizeAngle(kO.getTrueAnomaly(), kR.getTrueAnomaly()), - PositionAngleType.TRUE, + MathUtils.normalizeAngle(kO.getRightAscensionOfAscendingNode(), + kR.getRightAscensionOfAscendingNode()), + MathUtils.normalizeAngle(kO.getAnomaly(positionAngleType), + kR.getAnomaly(positionAngleType)), + positionAngleType, kO.getFrame(), kO.getDate(), kO.getMu()); diff --git a/src/main/java/org/orekit/orbits/PositionAngleBased.java b/src/main/java/org/orekit/orbits/PositionAngleBased.java index 489fe4ebc2..4f4713adbf 100644 --- a/src/main/java/org/orekit/orbits/PositionAngleBased.java +++ b/src/main/java/org/orekit/orbits/PositionAngleBased.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2024 Romain Serra +/* 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. diff --git a/src/main/java/org/orekit/orbits/WalkerConstellation.java b/src/main/java/org/orekit/orbits/WalkerConstellation.java new file mode 100644 index 0000000000..81d07d3930 --- /dev/null +++ b/src/main/java/org/orekit/orbits/WalkerConstellation.java @@ -0,0 +1,183 @@ +/* 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.orbits; + +import org.hipparchus.geometry.euclidean.threed.Rotation; +import org.hipparchus.geometry.euclidean.threed.RotationConvention; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.util.MathUtils; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.utils.PVCoordinates; + +import java.util.ArrayList; +import java.util.List; + +/** Builder for orbits of satellites forming a Walker constellation. + * @author Luc Maisonobe + * @since 12.1 + */ +public class WalkerConstellation { + + /** Total number of satellites. */ + private final int t; + + /** Number of orbital planes. */ + private final int p; + + /** Phasing parameter. */ + private final int f; + + /** Simple constructor. + * @param t total number of satellites + * @param p number of orbital planes + * @param f phasing parameter + */ + public WalkerConstellation(final int t, final int p, final int f) { + this.t = t; + this.p = p; + this.f = f; + if (t % p != 0) { + throw new OrekitException(OrekitMessages.WALKER_INCONSISTENT_PLANES, p, t); + } + } + + /** Get the total number of satellites. + * @return total number of satellites + */ + public int getT() { + return t; + } + + /** Get the number of orbital planes. + * @return number of orbital planes + */ + public int getP() { + return p; + } + + /** Get the phasing parameter. + * @return phasing parameter + */ + public int getF() { + return f; + } + + /** Create the regular slots. + *

          + * This method builds the {@link #getT() T} regular satellite, with + * integer {@link WalkerConstellationSlot#getSatellite() satellite indices}. If + * additional in-orbit spare satellites must be created, the {@link + * #buildSlot(WalkerConstellationSlot, int, double) buildSlot} method must be called + * explicitly. + *

          + *

          + * The various orbits are built from the {@code referenceOrbit} using plane + * rotations and {@link Orbit#shiftedBy(double) shifts}. This implies that + * if orbit does not include {@link Orbit#hasDerivatives() derivatives}, a + * simple Keplerian motion is assumed, which is the intended use case. + *

          + * @param type of the orbits + * @param referenceOrbit orbit of the reference satellite, in + * {@link WalkerConstellationSlot#getPlane() plane} 0 and + * at {@link WalkerConstellationSlot#getSatellite()} satellite index} 0 + * @return built orbits as a list of list, organized by planes + * @see #buildReferenceSlot(Orbit) + * @see #buildSlot(WalkerConstellationSlot, int, double) + */ + public List>> buildRegularSlots(final O referenceOrbit) { + + // build the reference slot + final WalkerConstellationSlot referenceSlot = buildReferenceSlot(referenceOrbit); + + final List>> all = new ArrayList<>(p); + for (int plane = 0; plane < p; ++plane) { + + // prepare list for one plane + final List> planeSlots = new ArrayList<>(t / p); + + // build all slots belonging to this plane + for (int satellite = 0; satellite < t / p; ++satellite) { + planeSlots.add(plane == 0 && satellite == 0 ? + referenceSlot : + buildSlot(referenceSlot, plane, satellite)); + } + + // finished plane + all.add(planeSlots); + + } + + // return the complete constellation + return all; + + } + + /** Create the reference slot, which is satellite 0 in plane 0. + * @param type of the orbits + * @param referenceOrbit orbit of the reference satellite, in + * {@link WalkerConstellationSlot#getPlane() plane} 0 and + * at {@link WalkerConstellationSlot#getSatellite()} satellite index} 0 + * @return build reference slot + * @see #buildRegularSlots(Orbit) + * @see #buildSlot(WalkerConstellationSlot, int, double) + */ + public WalkerConstellationSlotbuildReferenceSlot(final O referenceOrbit) { + return new WalkerConstellationSlot<>(this, 0, 0, referenceOrbit); + } + + /** Create one offset slot from an already existing slot. + * @param type of the orbits + * @param existingSlot existing slot (may be the {@link #buildReferenceSlot(Orbit) reference slot} or not) + * @param plane plane index of the new slot (may be non-integer for in-orbit spare satellites) + * @param satellite new slot satellite index in plane (may be non-integer if needed) + * @return built slot + * @see #buildRegularSlots(Orbit) + * @see #buildReferenceSlot(Orbit) + */ + public WalkerConstellationSlot buildSlot(final WalkerConstellationSlot existingSlot, + final int plane, final double satellite) { + + // offsets from existing slot + final O refOrbit = existingSlot.getOrbit(); + final int dp = plane - existingSlot.getPlane(); + final double ds = satellite - existingSlot.getSatellite(); + + // in plane shift + final double deltaT = (dp * f + ds * p) * refOrbit.getKeplerianPeriod() / t; + final Orbit shifted = refOrbit.shiftedBy(deltaT); + + // plane rotation + final Rotation r = new Rotation(Vector3D.PLUS_K, + MathUtils.TWO_PI * dp / p, + RotationConvention.VECTOR_OPERATOR); + final PVCoordinates pv = shifted.getPVCoordinates(); + final PVCoordinates rotated = new PVCoordinates(r.applyTo(pv.getPosition()), + r.applyTo(pv.getVelocity())); + + // build orbit + final CartesianOrbit c = new CartesianOrbit(rotated, refOrbit.getFrame(), + refOrbit.getDate(), refOrbit.getMu()); + @SuppressWarnings("unchecked") + final O orbit = (O) refOrbit.getType().convertType(c); + + // build slot + return new WalkerConstellationSlot<>(this, plane, satellite, orbit); + + } + +} diff --git a/src/main/java/org/orekit/orbits/WalkerConstellationSlot.java b/src/main/java/org/orekit/orbits/WalkerConstellationSlot.java new file mode 100644 index 0000000000..d102442731 --- /dev/null +++ b/src/main/java/org/orekit/orbits/WalkerConstellationSlot.java @@ -0,0 +1,93 @@ +/* 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.orbits; + +/** Container for one satellite slot in a {@link WalkerConstellation Wlaker constellation}. + *

          + * The {@link #getSatellite()} satellite index for regular satellites is an integer, + * but it is allowed to have non-integer indices to create slots for in-orbit spare + * satellites between the regular satellites. As an example, one can consider a 24/3/1 + * Walker constellation with 8 operational satellites in each of the 3 planes at satellites + * indices 0, 1, 2, 3, 4, 5, 6 and 7, and put for example 2 additional spares in each plane + * (hence having a total of 30 satellites), by affecting them to intermediate slots 0.5 + * and 4.5. + *

          + * @param type of the orbit + * @author Luc Maisonobe + * @since 12.1 + */ +public class WalkerConstellationSlot { + + /** Constellation. */ + private final WalkerConstellation constellation; + + /** Index of the plane. */ + private final int plane; + + /** Satellite index in plane. */ + private final double satellite; + + /** Orbit. */ + private final O orbit; + + /** Simple constructor. + * @param constellation constellation + * @param plane plane index + * @param satellite satellite index in plane + * @param orbit orbit + */ + WalkerConstellationSlot(final WalkerConstellation constellation, + final int plane, final double satellite, final O orbit) { + this.constellation = constellation; + this.plane = plane; + this.satellite = satellite; + this.orbit = orbit; + } + + /** Get the constellation. + * @return constellation + */ + public WalkerConstellation getConstellation() { + return constellation; + } + + /** Get the plane index. + * @return plane index + */ + public int getPlane() { + return plane; + } + + /** Get the satellite index in plane. + *

          + * Not that the index may be non-integer, for example to deal with + * in-orbit spare satellites + *

          + * @return satellite index in plane + */ + public double getSatellite() { + return satellite; + } + + /** Get the orbit. + * @return orbit + */ + public O getOrbit() { + return orbit; + } + +} diff --git a/src/main/java/org/orekit/overview.html b/src/main/java/org/orekit/overview.html index e6aa6290d0..650c88adca 100644 --- a/src/main/java/org/orekit/overview.html +++ b/src/main/java/org/orekit/overview.html @@ -38,400 +38,417 @@

          2. Features

          orbits propagators with several perturbing forces, maneuvers and discrete events. Here is a short list of the features offered by the library:

          -
            -
          • Time
              -
            • high accuracy absolute dates
            • -
            • time scales (TAI, UTC, UT1, GPS, TT, TCG, TDB, TCB, GMST, GST, GLONASS, QZSS, BDT, IRNSS ...)
            • -
            • transparent handling of leap seconds
            • -
            • support for CCSDS time code standards
            • -
            -
          • -
          • Geometry -
              -
            • frames hierarchy supporting fixed and time-dependent (or - telemetry-dependent) frames
            • -
            • predefined frames (EME2000/J2000, ICRF, GCRF, all ITRF from 1988 to 2020 - and intermediate frames, TOD, MOD, GTOD and TEME frames, Veis, - topocentric, tnw and qsw local orbital frames, relative encounter frames, Moon, Sun, planets, solar - system barycenter, Earth-Moon barycenter, ecliptic)
            • -
            • user extensible (used operationally in real time with a set of about 60 - frames on several spacecraft)
            • -
            • transparent handling of IERS Earth Orientation Parameters (for both new - CIO-based frames following IERS 2010 conventions and old equinox-based - frames)
            • -
            • transparent handling of JPL DE 4xx (405, 406 and more recent) and INPOP - ephemerides
            • -
            • transforms including kinematic combination effects
            • -
            • composite transforms reduction and caching for efficiency
            • -
            • extensible central body shapes models (with predefined spherical and - ellipsoidic shapes)
            • -
            • cartesian and geodesic coordinates, kinematics
            • -
            • computation of Dilution Of Precision (DOP) with respect to GNSS - constellations
            • -
            • projection of sensor Field Of View footprint on ground for any FoV shape
            • -
            -
          • -
          • Spacecraft state -
              -
            • cartesian, elliptical Keplerian, circular and equinoctial parameters, - with non-Keplerian derivatives if available
            • -
            • Two-Line Elements (TLE)
            • -
            • Two-Line Elements generation using Fixed-Point algorithm or Least Squares Fitting
            • -
            • transparent conversion between all parameters
            • -
            • automatic binding with frames
            • -
            • attitude state and derivative
            • -
            • jacobians
            • -
            • mass management
            • -
            • user-defined associated state (for example battery status, or higher - order derivatives, or anything else)
            • -
            -
          • -
          • Covariance -
              -
            • covariance propagation
            • -
            • covariance extrapolation using a Keplerian model
            • -
            • covariance frame transformation (inertial, Earth fixed, and local orbital frames)
            • -
            • covariance type transformation (cartesian, keplerian, circular, and equinoctial)
            • -
            • covariance interpolation based on the blending model
            • -
            -
          • -
          • Maneuvers -
              -
            • analytical models for small maneuvers without propagation
            • -
            • impulse maneuvers for any propagator type
            • -
            • continuous maneuvers for numerical propagator type
            • -
            • configurable low thrust maneuver model based on event detectors
            • -
            • used-defined propulsion models intended to be used with maneuver class (constant and piecewise polynomials already provided by the library)
            • -
            • user-friendly interface for maneuver triggers
            • -
            -
          • -
          • Propagation -
              -
            • analytical propagation models +
            • Time
                -
              • Kepler
              • -
              • Eckstein-Heschler
              • -
              • Brouwer-Lyddane with Warren Phipps' correction for the - critical inclination of 63.4° and the perturbative - acceleration due to atmospheric drag
              • -
              • SDP4/SGP4 with 2006 corrections
              • -
              • GNSS: GPS, QZSS, Galileo, GLONASS, Beidou, IRNSS and SBAS
              • +
              • high accuracy absolute dates
              • +
              • time scales (TAI, UTC, UT1, GPS, TT, TCG, TDB, TCB, GMST, GST, GLONASS, QZSS, BDT, + IRNSS...)
              • +
              • transparent handling of leap seconds
              • +
              • support for CCSDS time code standards
            • -
            • numerical propagators +
            • Geometry
                -
              • 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)
              • -
              • 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)
              • -
              • solid tides, with or without solid pole tide
              • -
              • ocean tides, with or without ocean pole tide
              • -
              • general relativity (including Lense-Thirring and De Sitter corrections)
              • -
              • Earth's albedo and infrared
              • -
              • multiple maneuvers
              • -
              • empirical accelerations to account for the unmodeled forces
              • -
              • state of the art ODE integrators (adaptive stepsize with error - control, continuous output, switching functions, G-stop, step - normalization ...)
              • -
              • serialization mechanism to store complete results on persistent - storage for later use
              • -
              • propagation in non-inertial frames (e.g. for Lagrange point halo - orbits)
              • +
              • frames hierarchy supporting fixed and time-dependent (or telemetry-dependent) frames
              • +
              • predefined frames (EME2000/J2000, ICRF, GCRF, all ITRF from 1988 to 2020 and + intermediate frames, TOD, MOD, GTOD and TEME frames, Veis, topocentric, tnw and qsw + local orbital frames, relative encounter frames, Moon, Sun, planets, solar system + barycenter, Earth-Moon barycenter, ecliptic)
              • +
              • user extensible (used operationally in real time with a set of about 60 frames on + several spacecraft)
              • +
              • transparent handling of IERS Earth Orientation Parameters (for both new CIO-based + frames following IERS 2010 conventions and old equinox-based frames)
              • +
              • transparent handling of JPL DE 4xx (405, 406 and more recent) and INPOP ephemerides
              • +
              • transforms including kinematic combination effects
              • +
              • composite transforms reduction and caching for efficiency
              • +
              • extensible central body shapes models (with predefined spherical and ellipsoidic + shapes)
              • +
              • cartesian and geodesic coordinates, kinematics
              • +
              • computation of Dilution Of Precision (DOP) with respect to GNSS constellations
              • +
              • projection of sensor Field Of View footprint on ground for any FoV shape
            • -
            • semi-analytical propagation model (DSST) with customizable force models +
            • Spacecraft state
                -
              • central attraction
              • -
              • gravity models
              • -
              • J2-squared effect (Zeis model)
              • -
              • atmospheric drag
              • -
              • third body attraction
              • -
              • radiation pressure with eclipse
              • +
              • cartesian, elliptical Keplerian, circular and equinoctial parameters, with + non-Keplerian derivatives if available
              • +
              • Walker constellations (including in-orbit spares with shifted position)
              • +
              • Two-Line Elements (TLE)
              • +
              • Two-Line Elements generation using Fixed-Point algorithm or Least Squares Fitting
              • +
              • transparent conversion between all parameters
              • +
              • automatic binding with frames
              • +
              • attitude state and derivative
              • +
              • jacobians
              • +
              • mass management
              • +
              • user-defined associated state (for example battery status, or higher order + derivatives, or anything else)
            • -
            • computation of Jacobians with respect to orbital parameters and - selected force models parameters
            • -
            • trajectories around Lagragian points using CR3BP model
            • -
            • tabulated ephemerides +
            • Covariance
                -
              • file based
              • -
              • memory based
              • -
              • integration based
              • +
              • covariance propagation
              • +
              • covariance extrapolation using a Keplerian model
              • +
              • covariance frame transformation (inertial, Earth fixed, and local orbital frames)
              • +
              • covariance type transformation (cartesian, keplerian, circular, and equinoctial)
              • +
              • covariance interpolation based on the blending model
            • -
            • Taylor-algebra (or any other real field) version of most of the above - propagators, with all force models, events detection, orbits types, - coordinates types and frames allowing high order uncertainties and - derivatives computation or very fast Monte-Carlo analyzes
            • -
            • unified interface above analytical/numerical/tabulated propagators for - easy switch from coarse analysis to fine simulation with one line change
            • -
            • all propagators can manage the time loop by themselves and handle callback - functions (called step handlers) from the calling application at each time step +
            • Maneuvers
                -
              • step handlers can be called at discrete time at regular time steps, which are - independent of propagator time steps
              • -
              • step handlers can be called with interpolators valid throughout one propagator - time step, which can have varying sizes
              • -
              • step handlers can be switched off completely, when only final state is desired
              • -
              • special step handlers are provided for a posteriori ephemeris generation: all - intermediate results are stored during propagation and provided back to the application - which can navigate at will through them, effectively using the propagated orbit as if - it was analytical model, even if it really is a numerically propagated one, which - is ideal for search and iterative algorithms
              • -
              • several step handlers can be used simultaneously, so it is possible to have a fine - grained fixed time step to log state in a huge file, and have at the same time a - coarse grained time step to display progress for user at a more human-friendly rate, - this feature can also be used for debugging purpose, by setting up a temporary - step handler alongside the operational ones
              • +
              • analytical models for small maneuvers without propagation
              • +
              • impulse maneuvers for any propagator type
              • +
              • continuous maneuvers for numerical propagator type
              • +
              • configurable low thrust maneuver model based on event detectors
              • +
              • used-defined propulsion models intended to be used with maneuver class (constant and + piecewise polynomials already provided by the library)
              • +
              • user-friendly interface for maneuver triggers
            • -
            • handling of discrete events during integration (models changes, G-stop, - simple notifications ...)
            • -
            • predefined discrete events +
            • Propagation
                -
              • eclipse (both umbra and penumbra)
              • -
              • ascending and descending node crossing
              • -
              • anomaly, latitude argument or longitude argument crossings, with - either true, eccentric or mean angles
              • -
              • apogee and perigee crossing
              • -
              • alignment with some body in the orbital plane (with customizable - threshold angle)
              • -
              • angular separation thresholds crossing between spacecraft and - a beacon (typically the Sun) as seen from an observer (typically - a ground station)
              • -
              • raising/setting with respect to a ground location (with customizable - triggering elevation and ground mask, optionally considering - refraction)
              • -
              • date and on-the-fly resetting countdown
              • -
              • date interval with parameter-driven boundaries
              • -
              • latitude, longitude, altitude crossing
              • -
              • latitude, longitude extremum
              • -
              • elevation extremum
              • -
              • moving target detection (with optional radius) in spacecraft sensor - Field Of View (any shape, with special case for circular)
              • -
              • spacecraft detection in ground based Field Of View (any shape)
              • -
              • sensor Field Of View (any shape) overlapping complex geographic zone
              • -
              • complex geographic zones traversal
              • -
              • inter-satellites direct view (with customizable skimming altitude)
              • -
              • ground at night
              • -
              • impulse maneuvers occurrence
              • -
              • geomagnetic intensity
              • -
              • extremum approach for TCA (Time of Closest Approach) computing
              • +
              • analytical propagation models +
                  +
                • Kepler
                • +
                • Eckstein-Heschler
                • +
                • Brouwer-Lyddane with Warren Phipps' correction for the critical inclination of + 63.4° and the perturbative acceleration due to atmospheric drag
                • +
                • SDP4/SGP4 with 2006 corrections
                • +
                • GNSS: GPS, QZSS, Galileo, GLONASS, Beidou, IRNSS and SBAS
                • +
                • Intelsat's 11 elements
                • +
                +
              • +
              • numerical propagators +
                  +
                • 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)
                • +
                • 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)
                • +
                • solid tides, with or without solid pole tide
                • +
                • ocean tides, with or without ocean pole tide
                • +
                • general relativity (including Lense-Thirring and De Sitter corrections)
                • +
                • Earth's albedo and infrared
                • +
                • multiple maneuvers
                • +
                • empirical accelerations to account for the unmodeled forces
                • +
                • state of the art ODE integrators (adaptive stepsize with error control, continuous + output, switching functions, G-stop, step normalization...)
                • +
                • serialization mechanism to store complete results on persistent storage for later + use
                • +
                • propagation in non-inertial frames (e.g. for Lagrange point halo orbits)
                • +
                +
              • +
              • semi-analytical propagation model (DSST) with customizable force models +
                  +
                • central attraction
                • +
                • gravity models
                • +
                • J2-squared effect (Zeis model)
                • +
                • atmospheric drag
                • +
                • third body attraction
                • +
                • radiation pressure with eclipse
                • +
                +
              • +
              • computation of Jacobians with respect to orbital parameters and selected force models + parameters
              • +
              • trajectories around Lagragian points using CR3BP model
              • +
              • tabulated ephemerides +
                  +
                • file based
                • +
                • memory based
                • +
                • integration based
                • +
                +
              • +
              • Taylor-algebra (or any other real field) version of most of the above propagators, + with all force models, events detection, orbits types, coordinates types and frames + allowing high order uncertainties and derivatives computation or very fast Monte-Carlo + analyzes
              • +
              • unified interface above analytical/numerical/tabulated propagators for easy switch + from coarse analysis to fine simulation with one line change
              • +
              • all propagators can manage the time loop by themselves and handle callback functions + (called step handlers) from the calling application at each time step +
                  +
                • step handlers can be called at discrete time at regular time steps, which are + independent of propagator time steps
                • +
                • step handlers can be called with interpolators valid throughout one propagator + time step, which can have varying sizes
                • +
                • step handlers can be switched off completely, when only final state is desired
                • +
                • special step handlers are provided for a posteriori ephemeris generation: all + intermediate results are stored during propagation and provided back to the application + which can navigate at will through them, effectively using the propagated orbit as if + it was analytical model, even if it really is a numerically propagated one, which + is ideal for search and iterative algorithms
                • +
                • several step handlers can be used simultaneously, so it is possible to have a fine + grained fixed time step to log state in a huge file, and have at the same time + a coarse grained time step to display progress for user at a more human-friendly + rate, this feature can also be used for debugging purpose, by setting up a temporary + step handler alongside the operational ones
                • +
                +
              • +
              • handling of discrete events during integration (models changes, G-stop, simple + notifications...)
              • +
              • adaptable max checking interval for discrete events detection
              • +
              • predefined discrete events +
                  +
                • eclipse (both umbra and penumbra)
                • +
                • ascending and descending node crossing
                • +
                • anomaly, latitude argument or longitude argument crossings, with either true, + eccentric or mean angles
                • +
                • apogee and perigee crossing
                • +
                • alignment with some body in the orbital plane (with customizable threshold angle)
                • +
                • angular separation thresholds crossing between spacecraft and a beacon (typically + the Sun) as seen from an observer (typically a ground station)
                • +
                • raising/setting with respect to a ground location (with customizable triggering + elevation and ground mask, optionally considering refraction)
                • +
                • date and on-the-fly resetting countdown
                • +
                • date interval with parameter-driven boundaries
                • +
                • latitude, longitude, altitude crossing
                • +
                • latitude, longitude extremum
                • +
                • elevation extremum
                • +
                • moving target detection (with optional radius) in spacecraft sensor Field Of View + (any shape, with special case for circular)
                • +
                • spacecraft detection in ground based Field Of View (any shape)
                • +
                • sensor Field Of View (any shape) overlapping complex geographic zone
                • +
                • complex geographic zones traversal
                • +
                • inter-satellites direct view (with customizable skimming altitude)
                • +
                • ground at night
                • +
                • impulse maneuvers occurrence
                • +
                • geomagnetic intensity
                • +
                • extremum approach for TCA (Time of Closest Approach) computing
                • +
                • beta angle
                • +
                • relative distance with another object
                • +
                +
              • +
              • possibility of slightly shifting events in time (for example to switch from solar + pointing mode to something else a few minutes before eclipse entry and reverting to + solar pointing mode a few minutes after eclipse exit)
              • +
              • events filtering based on their direction (for example to detect only eclipse entries + and not eclipse exits)
              • +
              • events filtering based on an external enabling function (for example to detect events + only during selected orbits and not others)
              • +
              • events combination with boolean operators
              • +
              • ability to run several propagators in parallel and manage their states simultaneously + throughout propagation
            • -
            • possibility of slightly shifting events in time (for example to switch - from solar pointing mode to something else a few minutes before eclipse - entry and reverting to solar pointing mode a few minutes after eclipse - exit)
            • -
            • events filtering based on their direction (for example to detect only - eclipse entries and not eclipse exits)
            • -
            • events filtering based on an external enabling function (for example to - detect events only during selected orbits and not others)
            • -
            • events combination with boolean operators
            • -
            • ability to run several propagators in parallel and manage their states - simultaneously throughout propagation
            • -
            -
          • -
          • Attitude -
              -
            • extensible attitude evolution models
            • -
            • predefined laws +
            • Attitude
                -
              • 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)
              • -
              • 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
              • +
              • extensible attitude evolution models
              • +
              • predefined laws +
                  +
                • 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)
                • +
                • 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
                • +
                +
              • +
              • loading and writing of CCSDS Attitude Data Messages (both AEM, APM and ACM types are + supported, in both KVN and XML formats, standalone or in combined NDM)
              • +
              • exporting of attitude ephemeris in CCSDS AEM and ACM file format
            • -
            • loading and writing of CCSDS Attitude Data Messages (both AEM, APM and ACM types are supported, in both KVN and XML formats, standalone or in combined NDM)
            • -
            • exporting of attitude ephemeris in CCSDS AEM and ACM file format
            • -
            -
          • -
          • Orbit determination -
              -
            • batch least squares fitting +
            • Orbit determination
                -
              • optimizers choice (Levenberg-Marquardt or Gauss-Newton)
              • -
              • decomposition algorithms choice (QR, LU, SVD, Cholesky)
              • -
              • choice between forming normal equations or not
              • +
              • batch least squares fitting +
                  +
                • optimizers choice (Levenberg-Marquardt or Gauss-Newton)
                • +
                • decomposition algorithms choice (QR, LU, SVD, Cholesky)
                • +
                • choice between forming normal equations or not
                • +
                +
              • +
              • sequential batch least squares +
                  +
                • sequential Gauss-Newton optimizer
                • +
                • decomposition algorithms choice (QR, LU, SVD, Cholesky)
                • +
                • possibility to use an initial covariance matrix
                • +
                +
              • +
              • Kalman filtering +
                  +
                • customizable process noise matrices providers
                • +
                • time dependent process noise provider
                • +
                • implementation of the Extended Kalman Filter
                • +
                • implementation of the Extended Semi-analytical Kalman Filter (ESKF)
                • +
                • implementation of the Unscented Kalman Filter
                • +
                • implementation of the Unscented Semi-analytical Kalman Filter
                • +
                +
              • +
              • parameters estimation +
                  +
                • orbital parameters estimation (or only a subset if desired)
                • +
                • force model parameters estimation (drag coefficients, radiation pressure + coefficients, central attraction, maneuver thrust, flow rate or start/stop epoch)
                • +
                • measurements parameters estimation (biases, satellite clock offset, station clock + offset, station position, pole motion and rate, prime meridian correction and rate, + total zenith delay in tropospheric correction)
                • +
                +
              • +
              • orbit determination can be performed with numerical, DSST, SDP4/SGP4, + Eckstein-Hechler, Brouwer-Lyddane, or Keplerian propagators
              • +
              • ephemeris-based orbit determination to estimate measurement parameters like station + biases or clock offsets
              • +
              • multi-satellites orbit determination
              • +
              • initial orbit determination methods (Gibbs, Gooding, Lambert, Gauss, and Laplace)
              • +
              • ground stations displacements due to solid tides
              • +
              • ground stations displacements due to ocean loading (based on Onsala Space Observatory + files in BLQ format)
              • +
              • ground stations displacements due to plate tectonics
              • +
              • several predefined measurements +
                  +
                • range
                • +
                • range rate (one way and two way)
                • +
                • turn-around range
                • +
                • azimuth/elevation
                • +
                • right ascension/declination
                • +
                • position-velocity
                • +
                • position
                • +
                • inter-satellites range (one way and two way)
                • +
                • inter-satellites GNSS one way range rate
                • +
                • inter-satellites GNSS phase
                • +
                • GNSS code
                • +
                • GNSS phase with integer ambiguity resolution and wind-up effect
                • +
                • Time Difference of Arrival (TDOA)
                • +
                • Frequency Difference of Arrival (FDOA)
                • +
                • Bi-static range and range rate
                • +
                • multiplexed
                • +
                +
              • +
              • possibility to add custom measurements
              • +
              • loading of ILRS CRD laser ranging measurements file
              • +
              • loading and writing of CCSDS Tracking Data Messages (in both KVN and XML formats, + standalone or in combined NDM)
              • +
              • several predefined modifiers +
                  +
                • tropospheric effects
                • +
                • ionospheric effects
                • +
                • clock relativistic effects (including J2 correction)
                • +
                • station offsets
                • +
                • biases
                • +
                • delays
                • +
                • Antenna Phase Center
                • +
                • Phase ambiguity
                • +
                • Shapiro relativistic effect
                • +
                • aberration of light in telescope measurements
                • +
                +
              • +
              • possibility to add custom measurement modifiers (even for predefined events)
              • +
              • combination of GNSS measurements +
                  +
                • dual frequency combination of measurements (Geometry-free, Ionosphere-free, + Narrow-lane, Wide-lane and Melbourne-Wübbena)
                • +
                • single frequency combination of measurements (Phase minus code and GRAPHIC)
                • +
                +
              • +
              • measurements generation +
                  +
                • with measurements feasibility triggered by regular event detectors (ground + visibility, ground at night, sunlit satellite, inter satellites direct view, boolean + combination...)
                • +
                • with measurement scheduling as fixed step streams (optionally aligned with round + UTC time)
                • +
                • with measurement scheduling as high rate bursts rest periods (optionally aligned + with round UTC time)
                • +
                • possibility to customize measurement scheduling
                • +
                +
            • -
            • sequential batch least squares +
            • GNSS
                -
              • sequential Gauss-Newton optimizer
              • -
              • decomposition algorithms choice (QR, LU, SVD, Cholesky)
              • -
              • possibility to use an initial covariance matrix
              • +
              • computation of Dilution Of Precision
              • +
              • loading of ANTEX antenna models file
              • +
              • loading and writing of RINEX observation files (version 2, 3, and 4)
              • +
              • loading of RINEX navigation files (version 2, 3, and 4)
              • +
              • support for Hatanaka compact RINEX format
              • +
              • loading of SINEX file (can load station positions, velocities, eccentricities, + Post-Seismic Deformation models, EOPs, and Differential Code Biases)
              • +
              • loading of RINEX clock files (version 2 and version 3)
              • +
              • parsing of IGS SSR messages for all constellations (version 1)
              • +
              • parsing of RTCM messages (both ephemeris and correction messages)
              • +
              • parsing of GPS RF link binary message
              • +
              • Hatch filters for GNSS measurements smoothing
              • +
              • implementation of Ntrip protocol
              • +
              • decoding of GPS navigation messages
            • -
            • Kalman filtering +
            • Orbit file handling
                -
              • customizable process noise matrices providers
              • -
              • time dependent process noise provider
              • -
              • implementation of the Extended Kalman Filter
              • -
              • implementation of the Extended Semi-analytical Kalman Filter (ESKF)
              • -
              • implementation of the Unscented Kalman Filter
              • -
              • implementation of the Unscented Semi-analytical Kalman Filter
              • +
              • loading and writing of SP3 orbit files (from versions a to d, including extension to a few inertial frames)
              • +
              • splicing and interpolation of SP3 files
              • +
              • loading and writing of CCSDS Orbit Data Messages (both OPM, OEM, OMM, OCM types are + supported, in both KVN and XML formats, standalone or in combined NDM)
              • +
              • loading of SEM and YUMA files for GPS constellation
              • +
              • exporting of ephemeris in CCSDS OEM and OCM file formats
              • +
              • loading of ILRS CPF orbit files
              • +
              • exporting of ephemeris in STK format
            • -
            • parameters estimation +
            • Earth models
                -
              • orbital parameters estimation (or only a subset if desired)
              • -
              • force model parameters estimation (drag coefficients, radiation pressure coefficients, - central attraction, maneuver thrust, flow rate or start/stop epoch)
              • -
              • measurements parameters estimation (biases, satellite clock offset, station clock offset, - station position, pole motion and rate, prime meridian correction and rate, total - zenith delay in tropospheric correction)
              • +
              • atmospheric models (DTM2000, Jacchia-Bowman 2008, NRL MSISE 2000, Harris-Priester and + simple exponential models), and Marshall solar Activity Future Estimation, optionally + with lift component
              • +
              • support for CSSI space weather data
              • +
              • support for SOLFSMY and DTC data for JB2008 atmospheric model
              • +
              • tropospheric delay (modified Saastamoinen, estimated, fixed)
              • +
              • tropospheric mapping functions (Vienna 1, Vienna 3, Global, Niell)
              • +
              • tropospheric refraction correction angle (Recommendation ITU-R P.834-7 and + Saemundssen's formula quoted by Meeus)
              • +
              • tropospheric model for laser ranging (Marini-Murray, Mendes-Pavlis)
              • +
              • Klobuchar ionospheric model (including parsing α and β coefficients from University of + Bern Astronomical Institute files)
              • +
              • Global Ionospheric Map (GIM) model
              • +
              • NeQuick ionospheric model
              • +
              • VTEC estimated ionospheric model with Single Layer Model (SLM) ionospheric mapping + function
              • +
              • Global Pressure and Temperature models (GPT, GPT2, GPT2w, GPT3)
              • +
              • geomagnetic field (WMM, IGRF)
              • +
              • geoid model from any gravity field
              • +
              • displacement of ground points due to tides
              • +
              • tessellation of zones of interest as tiles
              • +
              • sampling of zones of interest as grids of points
              • +
              • construction of trajectories using loxodromes (commonly, a rhumb line)
            • -
            • orbit determination can be performed with numerical, DSST, SDP4/SGP4, Eckstein-Hechler, Brouwer-Lyddane, or Keplerian propagators
            • -
            • ephemeris-based orbit determination to estimate measurement parameters like station biases or clock offsets
            • -
            • multi-satellites orbit determination
            • -
            • initial orbit determination methods (Gibbs, Gooding, Lambert and Laplace)
            • -
            • ground stations displacements due to solid tides
            • -
            • ground stations displacements due to ocean loading (based on Onsala Space Observatory files in BLQ format)
            • -
            • ground stations displacements due to plate tectonics
            • -
            • several predefined measurements +
            • Collisions
                -
              • range
              • -
              • range rate (one way and two way)
              • -
              • turn-around range
              • -
              • azimuth/elevation
              • -
              • right ascension/declination
              • -
              • position-velocity
              • -
              • position
              • -
              • inter-satellites range (one way and two way)
              • -
              • inter-satellites GNSS phase
              • -
              • GNSS code
              • -
              • GNSS phase with integer ambiguity resolution and wind-up effect
              • -
              • Time Difference of Arrival (TDOA)
              • -
              • Frequency Difference of Arrival (FDOA)
              • -
              • Bi-static range and range rate
              • -
              • multiplexed
              • +
              • loading and writing of CCSDS Conjunction Data Messages (CDM in both KVN and XML + formats)
              • +
              • 2D probability of collision computing methods assuming short term encounter and + spherical bodies: +
                  +
                • Chan 1997
                • +
                • Alfriend 1999
                • +
                • Alfriend 1999 (maximum version)
                • +
                • Alfano 2005
                • +
                • Patera 2005 (custom Orekit implementation) (recommended)
                • +
                • Laas 2015 (recommended)
                • +
            • -
            • possibility to add custom measurements
            • -
            • loading of ILRS CRD laser ranging measurements file
            • -
            • loading and writing of CCSDS Tracking Data Messages (in both KVN and XML formats, standalone or in combined NDM)
            • -
            • several predefined modifiers +
            • Customizable data loading
                -
              • tropospheric effects
              • -
              • ionospheric effects
              • -
              • clock relativistic effects (including J2 correction)
              • -
              • station offsets
              • -
              • biases
              • -
              • delays
              • -
              • Antenna Phase Center
              • -
              • Shapiro relativistic effect
              • +
              • loading by exploring folders hierarchy on local disk
              • +
              • loading from explicit lists of files on local disk
              • +
              • loading from classpath
              • +
              • loading from network (even through internet proxies)
              • +
              • support for zip archives
              • +
              • automatic decompression of gzip compressed (.gz) files upon loading
              • +
              • automatic decompression of Unix compressed (.Z) files upon loading
              • +
              • automatic decompression of Hatanaka compressed files upon loading
              • +
              • plugin mechanism to add filtering like custom decompression algorithms, deciphering or + monitoring
              • +
              • plugin mechanism to delegate loading to user defined database or data access library
            • -
            • possibility to add custom measurement modifiers (even for predefined events)
            • -
            • combination of GNSS measurements -
                -
              • dual frequency combination of measurements (Geometry-free, Ionosphere-free, Narrow-lane, Wide-lane and Melbourne-Wübbena)
              • -
              • single frequency combination of measurements (Phase minus code and GRAPHIC)
              • -
              -
            • -
            • measurements generation -
                -
              • with measurements feasibility triggered by regular event detectors - (ground visibility, ground at night, sunlit satellite, inter satellites - direct view, boolean combination...)
              • -
              • with measurement scheduling as fixed step streams (optionally aligned with round UTC time)
              • -
              • with measurement scheduling as high rate bursts rest periods (optionally aligned with round UTC time)
              • -
              • possibility to customize measurement scheduling
              • -
              -
            • -
            -
          • -
          • GNSS -
              -
            • computation of Dilution Of Precision
            • -
            • loading of ANTEX antenna models file
            • -
            • loading and writing of RINEX observation files (version 2, 3, and 4)
            • -
            • loading of RINEX navigation files (version 2, 3, and 4)
            • -
            • support for Hatanaka compact RINEX format
            • -
            • loading of SINEX file (can load station positions, eccentricities, EOPs, and Differential Code Biases)
            • -
            • loading of RINEX clock files (version 2 and version 3)
            • -
            • parsing of IGS SSR messages for all constellations (version 1)
            • -
            • parsing of RTCM messages (both ephemeris and correction messages)
            • -
            • parsing of GPS RF link binary message
            • -
            • Hatch filters for GNSS measurements smoothing
            • -
            • implementation of Ntrip protocol
            • -
            • decoding of GPS navigation messages
            • -
            -
          • -
          • Orbit file handling -
              -
            • loading and writing of SP3 orbit files (from versions a to d)
            • -
            • loading and writing of CCSDS Orbit Data Messages (both OPM, OEM, OMM, OCM types are supported, in both KVN and XML formats, standalone or in combined NDM)
            • -
            • loading of SEM and YUMA files for GPS constellation
            • -
            • exporting of ephemeris in CCSDS OEM and OCM file formats
            • -
            • loading of ILRS CPF orbit files
            • -
            • exporting of ephemeris in STK format
            • -
            -
          • -
          • Earth models -
              -
            • atmospheric models (DTM2000, Jacchia-Bowman 2008, NRL MSISE 2000, Harris-Priester and - simple exponential models), and Marshall solar Activity Future Estimation, optionally with lift component
            • -
            • support for CSSI space weather data
            • -
            • support for SOLFSMY and DTC data for JB2008 atmospheric model
            • -
            • tropospheric delay (modified Saastamoinen, estimated, fixed)
            • -
            • tropospheric mapping functions (Vienna 1, Vienna 3, Global, Niell)
            • -
            • tropospheric refraction correction angle (Recommendation ITU-R P.834-7 and Saemundssen's formula quoted by Meeus)
            • -
            • tropospheric model for laser ranging (Marini-Murray, Mendes-Pavlis)
            • -
            • Klobuchar ionospheric model (including parsing α and β coefficients from University of Bern Astronomical Institute files)
            • -
            • Global Ionospheric Map (GIM) model
            • -
            • NeQuick ionospheric model
            • -
            • VTEC estimated ionospheric model with Single Layer Model (SLM) ionospheric mapping function
            • -
            • Global Pressure and Temperature models (GPT and GPT2)
            • -
            • geomagnetic field (WMM, IGRF)
            • -
            • geoid model from any gravity field
            • -
            • displacement of ground points due to tides
            • -
            • tessellation of zones of interest as tiles
            • -
            • sampling of zones of interest as grids of points
            • -
            • construction of trajectories using loxodromes (commonly, a rhumb line)
            • -
            -
          • -
          • Collisions -
              -
            • loading and writing of CCSDS Conjunction Data Messages (CDM in both KVN and XML formats)
            • -
            • 2D probability of collision computing methods assuming short term encounter and spherical bodies : -
                -
              • Chan 1997
              • -
              • Alfriend 1999
              • -
              • Alfriend 1999 (maximum version)
              • -
              • Alfano 2005
              • -
              • Patera 2005 (custom Orekit implementation) (recommended)
              • -
              • Laas 2015 (recommended)
              • -
            • -
            -
          • -
          • Customizable data loading -
              -
            • loading by exploring folders hierarchy on local disk
            • -
            • loading from explicit lists of files on local disk
            • -
            • loading from classpath
            • -
            • loading from network (even through internet proxies)
            • -
            • support for zip archives
            • -
            • automatic decompression of gzip compressed (.gz) files upon loading
            • -
            • automatic decompression of Unix compressed (.Z) files upon loading
            • -
            • automatic decompression of Hatanaka compressed files upon loading
            • -
            • plugin mechanism to add filtering like custom decompression algorithms, deciphering or monitoring
            • -
            • plugin mechanism to delegate loading to user defined database or data access library
            -
          • -

          3. Dependency

          diff --git a/src/main/java/org/orekit/propagation/AbstractPropagator.java b/src/main/java/org/orekit/propagation/AbstractPropagator.java index 9504e82376..32b07cb621 100644 --- a/src/main/java/org/orekit/propagation/AbstractPropagator.java +++ b/src/main/java/org/orekit/propagation/AbstractPropagator.java @@ -33,7 +33,6 @@ import org.orekit.time.AbsoluteDate; import org.orekit.utils.DoubleArrayDictionary; import org.orekit.utils.TimeSpanMap; -import org.orekit.utils.TimeStampedPVCoordinates; /** Common handling of {@link Propagator} methods for analytical propagators. *

          @@ -46,7 +45,7 @@ public abstract class AbstractPropagator implements Propagator { /** Multiplexer for step handlers. */ - private StepHandlerMultiplexer multiplexer; + private final StepHandlerMultiplexer multiplexer; /** Start date. */ private AbsoluteDate startDate; @@ -229,7 +228,7 @@ protected SpacecraftState updateAdditionalStates(final SpacecraftState original) } } else { // we can use this provider right now - updated = updated.addAdditionalState(provider.getName(), provider.getAdditionalState(updated)); + updated = provider.update(updated); yieldCount = 0; } } @@ -276,11 +275,6 @@ public SpacecraftState propagate(final AbsoluteDate target) { return propagate(startDate, target); } - /** {@inheritDoc} */ - public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame frame) { - return propagate(date).getPVCoordinates(frame); - } - /** Initialize propagation. * @since 10.1 */ diff --git a/src/main/java/org/orekit/propagation/AbstractStateModifier.java b/src/main/java/org/orekit/propagation/AbstractStateModifier.java new file mode 100644 index 0000000000..898ac65955 --- /dev/null +++ b/src/main/java/org/orekit/propagation/AbstractStateModifier.java @@ -0,0 +1,58 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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; + +/** Abstract base class for modifying state during propagation. + *

          + * This class is a specialized implementation of {@link AdditionalStateProvider} + * with a name set to the empty string and returning a null additional state. + *

          + *

          + * Beware that changing the state undercover from the propagator may have + * many side effects. Using this class should therefore be done cautiously. + *

          + * @see Propagator + * @see AdditionalStateProvider + * @author Luc Maisonobe + * @since 12.1 + */ +public abstract class AbstractStateModifier implements AdditionalStateProvider { + + /** {@inheritDoc} */ + @Override + public String getName() { + return ""; + } + + /** {@inheritDoc} */ + @Override + public double[] getAdditionalState(final SpacecraftState state) { + return null; + } + + /** {@inheritDoc} */ + @Override + public SpacecraftState update(final SpacecraftState state) { + return change(state); + } + /** Change main state. + * @param state spacecraft state to change + * @return changed state + */ + public abstract SpacecraftState change(SpacecraftState state); + +} diff --git a/src/main/java/org/orekit/propagation/AdditionalStateProvider.java b/src/main/java/org/orekit/propagation/AdditionalStateProvider.java index 9ea0ad16be..40e71370bc 100644 --- a/src/main/java/org/orekit/propagation/AdditionalStateProvider.java +++ b/src/main/java/org/orekit/propagation/AdditionalStateProvider.java @@ -18,14 +18,14 @@ import org.orekit.time.AbsoluteDate; -/** This interface represents providers for additional state data beyond {@link SpacecraftState}. +/** This interface allows to modify {@link SpacecraftState} and set up additional state data. *

          * {@link Propagator Propagators} generate {@link SpacecraftState states} that contain at * least orbit, attitude, and mass. These states may however also contain {@link * SpacecraftState#addAdditionalState(String, double...) additional states}. Instances of classes - * implementing this interface are intended to be registered to propagators so they can add these - * additional states incrementally after having computed the basic components - * (orbit, attitude and mass). + * implementing this interface are intended to be registered to propagators so they can either + * modify the basic components (orbit, attitude and mass) or add additional states incrementally + * after having computed the basic components. *

          *

          * Some additional states may depend on previous additional states to @@ -33,7 +33,7 @@ * of these additional states at some time if they depend on conditions that are fulfilled only * after propagation as started or some event has occurred. As the propagator builds the complete * state incrementally, looping over the registered providers, it must call their {@link - * #getAdditionalState(SpacecraftState) getAdditionalState} methods in an order that fulfill these dependencies that + * #update(SpacecraftState) update} methods in an order that fulfill these dependencies that * may be time-dependent and are not related to the order in which the providers are registered to * the propagator. This reordering is performed each time the complete state is built, using a yield * mechanism. The propagator first pushes all providers in a stack and then empty the stack, one provider @@ -51,8 +51,8 @@ *

          *

          * It is possible that at some stages in the propagation, a subset of the providers registered to a - * propagator all yield and cannot {@link #getAdditionalState(SpacecraftState) retrieve} their additional - * state. This happens for example during the initialization phase of a propagator that + * propagator all yield and cannot {@link #update(SpacecraftState) update} the state. This happens + * for example during the initialization phase of a propagator that * computes State Transition Matrices or Jacobian matrices. These features are managed as secondary equations * in the ODE integrator, and initialized after the primary equations (which correspond to orbit) have * been initialized. So when the primary equation are initialized, the providers that depend on the secondary @@ -69,11 +69,17 @@ *

          * @see org.orekit.propagation.Propagator * @see org.orekit.propagation.integration.AdditionalDerivativesProvider + * @see AbstractStateModifier * @author Luc Maisonobe */ public interface AdditionalStateProvider { /** Get the name of the additional state. + *

          + * If a provider just modifies one of the basic elements (orbit, attitude + * or mass) without adding any new state, it should return the empty string + * as its name. + *

          * @return name of the additional state (names containing "orekit" * with any case are reserved for the library internal use) */ @@ -120,4 +126,13 @@ default boolean yields(SpacecraftState state) { */ double[] getAdditionalState(SpacecraftState state); + /** Update a state. + * @param state spacecraft state to update + * @return updated state + * @since 12.1 + */ + default SpacecraftState update(final SpacecraftState state) { + return state.addAdditionalState(getName(), getAdditionalState(state)); + } + } diff --git a/src/main/java/org/orekit/propagation/FieldAbstractPropagator.java b/src/main/java/org/orekit/propagation/FieldAbstractPropagator.java index b8ab8de3dc..64dd7ae4f3 100644 --- a/src/main/java/org/orekit/propagation/FieldAbstractPropagator.java +++ b/src/main/java/org/orekit/propagation/FieldAbstractPropagator.java @@ -34,7 +34,6 @@ import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.FieldArrayDictionary; import org.orekit.utils.FieldTimeSpanMap; -import org.orekit.utils.TimeStampedFieldPVCoordinates; /** Common handling of {@link Propagator} methods for analytical propagators. *

          @@ -48,7 +47,7 @@ public abstract class FieldAbstractPropagator> implements FieldPropagator { /** Multiplexer for step handlers. */ - private FieldStepHandlerMultiplexer multiplexer; + private final FieldStepHandlerMultiplexer multiplexer; /** Start date. */ private FieldAbsoluteDate startDate; @@ -199,7 +198,7 @@ protected FieldSpacecraftState updateAdditionalStates(final FieldSpacecraftSt } } else { // we can use this provider right now - updated = updated.addAdditionalState(provider.getName(), provider.getAdditionalState(updated)); + updated = provider.update(updated); yieldCount = 0; } } @@ -246,11 +245,6 @@ public FieldSpacecraftState propagate(final FieldAbsoluteDate target) { return propagate(startDate, target); } - /** {@inheritDoc} */ - public TimeStampedFieldPVCoordinates getPVCoordinates(final FieldAbsoluteDate date, final Frame frame) { - return propagate(date).getPVCoordinates(frame); - } - /** Initialize propagation. * @since 10.1 */ diff --git a/src/main/java/org/orekit/propagation/FieldAbstractStateModifier.java b/src/main/java/org/orekit/propagation/FieldAbstractStateModifier.java new file mode 100644 index 0000000000..e9dceac9f9 --- /dev/null +++ b/src/main/java/org/orekit/propagation/FieldAbstractStateModifier.java @@ -0,0 +1,62 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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; + +import org.hipparchus.CalculusFieldElement; + +/** Abstract base class for modifying state during propagation. + *

          + * This class is a specialized implementation of {@link AdditionalStateProvider} + * with a name set to the empty string and returning a null additional state. + *

          + *

          + * Beware that changing the state undercover from the propagator may have + * many side effects. Using this class should therefore be done cautiously. + *

          + * @see Propagator + * @see AdditionalStateProvider + * @author Luc Maisonobe + * @param type of the field elements + * @since 12.1 + */ +public abstract class FieldAbstractStateModifier> + implements FieldAdditionalStateProvider { + + /** {@inheritDoc} */ + @Override + public String getName() { + return ""; + } + + /** {@inheritDoc} */ + @Override + public T[] getAdditionalState(final FieldSpacecraftState state) { + return null; + } + + /** {@inheritDoc} */ + @Override + public FieldSpacecraftState update(final FieldSpacecraftState state) { + return change(state); + } + /** Change main state. + * @param state spacecraft state to change + * @return changed state + */ + public abstract FieldSpacecraftState change(FieldSpacecraftState state); + +} diff --git a/src/main/java/org/orekit/propagation/FieldAdditionalStateProvider.java b/src/main/java/org/orekit/propagation/FieldAdditionalStateProvider.java index 1e0f482271..da21699e13 100644 --- a/src/main/java/org/orekit/propagation/FieldAdditionalStateProvider.java +++ b/src/main/java/org/orekit/propagation/FieldAdditionalStateProvider.java @@ -19,14 +19,14 @@ import org.hipparchus.CalculusFieldElement; import org.orekit.time.FieldAbsoluteDate; -/** This interface represents providers for additional state data beyond {@link SpacecraftState}. +/** This interface allows to modify {@link FieldSpacecraftState} and set up additional state data. *

          * {@link FieldPropagator Propagators} generate {@link FieldSpacecraftState states} that contain at * least orbit, attitude, and mass. These states may however also contain {@link - * FieldSpacecraftState#addAdditionalState(String, CalculusFieldElement...) additional states}. Instances of classes - * implementing this interface are intended to be registered to propagators so they can add these - * additional states incrementally after having computed the basic components - * (orbit, attitude and mass). + * FieldSpacecraftState#addAdditionalState(String, CalculusFieldElement...) additional states}. + * Instances of classes implementing this interface are intended to be registered to propagators + * so they can either modify the basic components (orbit, attitude and mass) or add additional + * states incrementally after having computed the basic components. *

          *

          * Some additional states may depend on previous additional states to @@ -34,7 +34,7 @@ * of these additional states at some time if they depend on conditions that are fulfilled only * after propagation as started or some event has occurred. As the propagator builds the complete * state incrementally, looping over the registered providers, it must call their {@link - * #getAdditionalState(FieldSpacecraftState) getAdditionalState} methods in an order that fulfill these dependencies that + * #update(FieldSpacecraftState) update} methods in an order that fulfill these dependencies that * may be time-dependent and are not related to the order in which the providers are registered to * the propagator. This reordering is performed each time the complete state is built, using a yield * mechanism. The propagator first pushes all providers in a stack and then empty the stack, one provider @@ -52,8 +52,8 @@ *

          *

          * It is possible that at some stages in the propagation, a subset of the providers registered to a - * propagator all yield and cannot {@link #getAdditionalState(FieldSpacecraftState) retrieve} their additional - * state. This happens for example during the initialization phase of a propagator that + * propagator all yield and cannot {@link #update(FieldSpacecraftState) update} the state. + * This happens for example during the initialization phase of a propagator that * computes State Transition Matrices or Jacobian matrices. These features are managed as secondary equations * in the ODE integrator, and initialized after the primary equations (which correspond to orbit) have * been initialized. So when the primary equation are initialized, the providers that depend on the secondary @@ -70,13 +70,20 @@ *

          * @see org.orekit.propagation.FieldPropagator * @see org.orekit.propagation.integration.FieldAdditionalDerivativesProvider + * @see FieldAbstractStateModifier * @author Luc Maisonobe * @param type of the field elements */ public interface FieldAdditionalStateProvider> { /** Get the name of the additional state. - * @return name of the additional state + *

          + * If a provider just modifies one of the basic elements (orbit, attitude + * or mass) without adding any new state, it should return the empty string + * as its name. + *

          + * @return name of the additional state (names containing "orekit" + * with any case are reserved for the library internal use) */ String getName(); @@ -121,4 +128,13 @@ default boolean yields(FieldSpacecraftState state) { */ T[] getAdditionalState(FieldSpacecraftState state); + /** Update a state. + * @param state spacecraft state to update + * @return updated state + * @since 12.1 + */ + default FieldSpacecraftState update(final FieldSpacecraftState state) { + return state.addAdditionalState(getName(), getAdditionalState(state)); + } + } diff --git a/src/main/java/org/orekit/propagation/FieldPropagator.java b/src/main/java/org/orekit/propagation/FieldPropagator.java index 4b34c9431a..9489153bbc 100644 --- a/src/main/java/org/orekit/propagation/FieldPropagator.java +++ b/src/main/java/org/orekit/propagation/FieldPropagator.java @@ -20,6 +20,7 @@ import java.util.List; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.orekit.attitudes.AttitudeProvider; import org.orekit.frames.Frame; import org.orekit.propagation.events.FieldEventDetector; @@ -28,6 +29,7 @@ import org.orekit.propagation.sampling.FieldStepHandlerMultiplexer; import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.FieldPVCoordinatesProvider; +import org.orekit.utils.TimeStampedFieldPVCoordinates; /** This interface provides a way to propagate an orbit at any time. * @@ -249,4 +251,16 @@ default void setStepHandler(final FieldOrekitStepHandler handler) { */ FieldSpacecraftState propagate(FieldAbsoluteDate start, FieldAbsoluteDate target); + /** {@inheritDoc} */ + @Override + default TimeStampedFieldPVCoordinates getPVCoordinates(FieldAbsoluteDate date, Frame frame) { + return propagate(date).getPVCoordinates(frame); + } + + /** {@inheritDoc} */ + @Override + default FieldVector3D getPosition(final FieldAbsoluteDate date, final Frame frame) { + return propagate(date).getPosition(frame); + } + } diff --git a/src/main/java/org/orekit/propagation/FieldSpacecraftState.java b/src/main/java/org/orekit/propagation/FieldSpacecraftState.java index 80f1bbe702..820b526f07 100644 --- a/src/main/java/org/orekit/propagation/FieldSpacecraftState.java +++ b/src/main/java/org/orekit/propagation/FieldSpacecraftState.java @@ -24,7 +24,6 @@ import org.hipparchus.exception.MathIllegalStateException; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.util.FastMath; -import org.hipparchus.util.MathArrays; import org.orekit.attitudes.FieldAttitude; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitIllegalArgumentException; @@ -34,7 +33,7 @@ import org.orekit.frames.FieldTransform; import org.orekit.frames.Frame; import org.orekit.orbits.FieldOrbit; -import org.orekit.orbits.PositionAngleType; +import org.orekit.orbits.Orbit; import org.orekit.time.FieldAbsoluteDate; import org.orekit.time.FieldTimeShiftable; import org.orekit.time.FieldTimeStamped; @@ -113,7 +112,7 @@ public class FieldSpacecraftState > public FieldSpacecraftState(final FieldOrbit orbit) { this(orbit, SpacecraftState.getDefaultAttitudeProvider(orbit.getFrame()) .getAttitude(orbit, orbit.getDate(), orbit.getFrame()), - orbit.getA().getField().getZero().add(DEFAULT_MASS), (FieldArrayDictionary) null); + orbit.getA().getField().getZero().newInstance(DEFAULT_MASS), (FieldArrayDictionary) null); } /** Build a spacecraft state from orbit and attitude. @@ -125,7 +124,7 @@ public FieldSpacecraftState(final FieldOrbit orbit) { */ public FieldSpacecraftState(final FieldOrbit orbit, final FieldAttitude attitude) throws IllegalArgumentException { - this(orbit, attitude, orbit.getA().getField().getZero().add(DEFAULT_MASS), (FieldArrayDictionary) null); + this(orbit, attitude, orbit.getA().getField().getZero().newInstance(DEFAULT_MASS), (FieldArrayDictionary) null); } /** Create a new instance from orbit and mass. @@ -160,7 +159,7 @@ public FieldSpacecraftState(final FieldOrbit orbit, final FieldAttitude at public FieldSpacecraftState(final FieldOrbit orbit, final FieldArrayDictionary additional) { this(orbit, SpacecraftState.getDefaultAttitudeProvider(orbit.getFrame()) .getAttitude(orbit, orbit.getDate(), orbit.getFrame()), - orbit.getA().getField().getZero().add(DEFAULT_MASS), additional); + orbit.getA().getField().getZero().newInstance(DEFAULT_MASS), additional); } /** Build a spacecraft state from orbit attitude and additional states. @@ -174,7 +173,7 @@ public FieldSpacecraftState(final FieldOrbit orbit, final FieldArrayDictionar */ public FieldSpacecraftState(final FieldOrbit orbit, final FieldAttitude attitude, final FieldArrayDictionary additional) throws IllegalArgumentException { - this(orbit, attitude, orbit.getA().getField().getZero().add(DEFAULT_MASS), additional); + this(orbit, attitude, orbit.getA().getField().getZero().newInstance(DEFAULT_MASS), additional); } /** Create a new instance from orbit, mass and additional states. @@ -244,28 +243,9 @@ public FieldSpacecraftState(final FieldOrbit orbit, final FieldAttitude at public FieldSpacecraftState(final Field field, final SpacecraftState state) { if (state.isOrbitDefined()) { - final double[] stateD = new double[6]; - final double[] stateDotD = state.getOrbit().hasDerivatives() ? new double[6] : null; - final PositionAngleType positionAngleType = PositionAngleType.TRUE; - state.getOrbit().getType().mapOrbitToArray(state.getOrbit(), positionAngleType, stateD, stateDotD); - final T[] stateF = MathArrays.buildArray(field, 6); - for (int i = 0; i < stateD.length; ++i) { - stateF[i] = field.getZero().add(stateD[i]); - } - final T[] stateDotF; - if (stateDotD == null) { - stateDotF = null; - } else { - stateDotF = MathArrays.buildArray(field, 6); - for (int i = 0; i < stateDotD.length; ++i) { - stateDotF[i] = field.getZero().add(stateDotD[i]); - } - } - - final FieldAbsoluteDate dateF = new FieldAbsoluteDate<>(field, state.getDate()); - this.orbit = state.getOrbit().getType().mapArrayToOrbit(stateF, stateDotF, positionAngleType, dateF, - field.getZero().add(state.getMu()), state.getFrame()); + final Orbit nonFieldOrbit = state.getOrbit(); + this.orbit = nonFieldOrbit.getType().convertToFieldOrbit(field, nonFieldOrbit); this.absPva = null; } else { @@ -280,7 +260,7 @@ public FieldSpacecraftState(final Field field, final SpacecraftState state) { } this.attitude = new FieldAttitude<>(field, state.getAttitude()); - this.mass = field.getZero().add(state.getMass()); + this.mass = field.getZero().newInstance(state.getMass()); final DoubleArrayDictionary additionalD = state.getAdditionalStatesValues(); if (additionalD.size() == 0) { @@ -311,7 +291,7 @@ public FieldSpacecraftState(final FieldAbsolutePVCoordinates absPva) { this(absPva, SpacecraftState.getDefaultAttitudeProvider(absPva.getFrame()). getAttitude(absPva, absPva.getDate(), absPva.getFrame()), - absPva.getDate().getField().getZero().add(DEFAULT_MASS), (FieldArrayDictionary) null); + absPva.getDate().getField().getZero().newInstance(DEFAULT_MASS), (FieldArrayDictionary) null); } /** Build a spacecraft state from orbit and attitude. @@ -323,7 +303,7 @@ public FieldSpacecraftState(final FieldAbsolutePVCoordinates absPva) { */ public FieldSpacecraftState(final FieldAbsolutePVCoordinates absPva, final FieldAttitude attitude) throws IllegalArgumentException { - this(absPva, attitude, absPva.getDate().getField().getZero().add(DEFAULT_MASS), (FieldArrayDictionary) null); + this(absPva, attitude, absPva.getDate().getField().getZero().newInstance(DEFAULT_MASS), (FieldArrayDictionary) null); } /** Create a new instance from orbit and mass. @@ -358,7 +338,7 @@ public FieldSpacecraftState(final FieldAbsolutePVCoordinates absPva, final Fi public FieldSpacecraftState(final FieldAbsolutePVCoordinates absPva, final FieldArrayDictionary additional) { this(absPva, SpacecraftState.getDefaultAttitudeProvider(absPva.getFrame()) .getAttitude(absPva, absPva.getDate(), absPva.getFrame()), - absPva.getDate().getField().getZero().add(DEFAULT_MASS), additional); + absPva.getDate().getField().getZero().newInstance(DEFAULT_MASS), additional); } /** Build a spacecraft state from orbit and attitude. @@ -373,7 +353,7 @@ public FieldSpacecraftState(final FieldAbsolutePVCoordinates absPva, final Fi public FieldSpacecraftState(final FieldAbsolutePVCoordinates absPva, final FieldAttitude attitude, final FieldArrayDictionary additional) throws IllegalArgumentException { - this(absPva, attitude, absPva.getDate().getField().getZero().add(DEFAULT_MASS), additional); + this(absPva, attitude, absPva.getDate().getField().getZero().newInstance(DEFAULT_MASS), additional); } /** Create a new instance from orbit and mass. diff --git a/src/main/java/org/orekit/propagation/FieldSpacecraftStateInterpolator.java b/src/main/java/org/orekit/propagation/FieldSpacecraftStateInterpolator.java index 495f0b7b86..0a9221429c 100644 --- a/src/main/java/org/orekit/propagation/FieldSpacecraftStateInterpolator.java +++ b/src/main/java/org/orekit/propagation/FieldSpacecraftStateInterpolator.java @@ -141,6 +141,32 @@ public FieldSpacecraftStateInterpolator(final int interpolationPoints, final Fra this(interpolationPoints, outputFrame, outputFrame); } + /** + * Constructor to create a customizable Hermite interpolator for every spacecraft state field. + *

          + * The interpolators will have the following configuration : + *

            + *
          • Same frame for coordinates and attitude
          • + *
          • Use of position and two time derivatives for absolute position-velocity-acceleration coordinates interpolation
          • + *
          • Use of angular and first time derivative for attitude interpolation
          • + *
          + *

          + * 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). + *

          + * BEWARE: output frame must be inertial if this instance is going to interpolate between + * tabulated spacecraft states defined by orbit, will throw an error otherwise. + * + * @param interpolationPoints number of interpolation points + * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail + * @param outputFrame output frame + * @since 12.1 + */ + public FieldSpacecraftStateInterpolator(final int interpolationPoints, final double extrapolationThreshold, final Frame outputFrame) { + this(interpolationPoints, extrapolationThreshold, outputFrame, outputFrame); + } + /** * Constructor to create a customizable Hermite interpolator for every spacecraft state field. *

          @@ -434,7 +460,7 @@ protected FieldSpacecraftState interpolate(final InterpolationData interpola interpolatedMass = massInterpolator.interpolate(interpolationDate, masses).getValue(); } else { - interpolatedMass = one.multiply(SpacecraftState.DEFAULT_MASS); + interpolatedMass = one.newInstance(SpacecraftState.DEFAULT_MASS); } // Interpolate additional states and derivatives diff --git a/src/main/java/org/orekit/propagation/MatricesHarvester.java b/src/main/java/org/orekit/propagation/MatricesHarvester.java index 32dfe2766c..0b195a6c02 100644 --- a/src/main/java/org/orekit/propagation/MatricesHarvester.java +++ b/src/main/java/org/orekit/propagation/MatricesHarvester.java @@ -25,9 +25,9 @@ /** Interface for extracting State Transition Matrices and Jacobians matrices from {@link SpacecraftState spacecraft state}. *

          * The State Transition Matrix and Jacobians matrices with respect to propagation parameters are stored in the state - * as {@link SpacecraftState#getAdditionalState(String) additional states}. Each propagator and support classes has - * its own way to handle it. The interface leverages these differences which are implementation details and provides - * a higher level access to these matrices, regardless of haw they were computed and stored. + * as {@link SpacecraftState#getAdditionalState(String) additional states}. Each propagator and support classes have + * their own way to handle them. The interface leverages these differences which are implementation details and provides + * a higher level access to these matrices, regardless of how they were computed and stored. *

          * @author Luc Maisonobe * @since 11.1 diff --git a/src/main/java/org/orekit/propagation/Propagator.java b/src/main/java/org/orekit/propagation/Propagator.java index 90b3085fb5..fabb9acc29 100644 --- a/src/main/java/org/orekit/propagation/Propagator.java +++ b/src/main/java/org/orekit/propagation/Propagator.java @@ -20,6 +20,7 @@ import java.util.List; import org.hipparchus.geometry.euclidean.threed.Rotation; +import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.linear.RealMatrix; import org.orekit.attitudes.AttitudeProvider; import org.orekit.attitudes.FrameAlignedProvider; @@ -33,6 +34,7 @@ import org.orekit.time.AbsoluteDate; import org.orekit.utils.DoubleArrayDictionary; import org.orekit.utils.PVCoordinatesProvider; +import org.orekit.utils.TimeStampedPVCoordinates; /** This interface provides a way to propagate an orbit at any time. * @@ -292,4 +294,16 @@ default MatricesHarvester setupMatricesComputation(final String stmName, final R */ SpacecraftState propagate(AbsoluteDate start, AbsoluteDate target); + /** {@inheritDoc} */ + @Override + default TimeStampedPVCoordinates getPVCoordinates(AbsoluteDate date, Frame frame) { + return propagate(date).getPVCoordinates(frame); + } + + /** {@inheritDoc} */ + @Override + default Vector3D getPosition(AbsoluteDate date, Frame frame) { + return propagate(date).getPosition(frame); + } + } diff --git a/src/main/java/org/orekit/propagation/SpacecraftState.java b/src/main/java/org/orekit/propagation/SpacecraftState.java index 4ad855e89d..c9adada2cf 100644 --- a/src/main/java/org/orekit/propagation/SpacecraftState.java +++ b/src/main/java/org/orekit/propagation/SpacecraftState.java @@ -750,9 +750,7 @@ public DoubleArrayDictionary getAdditionalStatesDerivatives() { */ public Transform toTransform() { final TimeStampedPVCoordinates pv = getPVCoordinates(); - return new Transform(pv.getDate(), - new Transform(pv.getDate(), pv.negate()), - new Transform(pv.getDate(), attitude.getOrientation())); + return new Transform(pv.getDate(), pv.negate(), attitude.getOrientation()); } /** Compute the static transform from state defining frame to spacecraft frame. diff --git a/src/main/java/org/orekit/propagation/SpacecraftStateInterpolator.java b/src/main/java/org/orekit/propagation/SpacecraftStateInterpolator.java index 2c8cccf0b3..6d242ea353 100644 --- a/src/main/java/org/orekit/propagation/SpacecraftStateInterpolator.java +++ b/src/main/java/org/orekit/propagation/SpacecraftStateInterpolator.java @@ -136,6 +136,33 @@ public SpacecraftStateInterpolator(final int interpolationPoints, final Frame ou this(interpolationPoints, DEFAULT_EXTRAPOLATION_THRESHOLD_SEC, outputFrame, outputFrame); } + /** + * Constructor to create a customizable Hermite interpolator for every spacecraft state field. + *

          + * The interpolators will have the following configuration : + *

            + *
          • Same frame for coordinates and attitude
          • + *
          • Use of position and two time derivatives for absolute position-velocity-acceleration coordinates interpolation
          • + *
          • Use of angular and first time derivative for attitude interpolation
          • + *
          + *

          + * 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). + *

          + * BEWARE: output frame must be inertial if this instance is going to interpolate between + * tabulated spacecraft states defined by orbit, will throw an error otherwise. + * + * @param interpolationPoints number of interpolation points + * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail + * @param outputFrame output frame + * @since 12.1 + * @see AbstractTimeInterpolator + */ + public SpacecraftStateInterpolator(final int interpolationPoints, final double extrapolationThreshold, final Frame outputFrame) { + this(interpolationPoints, extrapolationThreshold, outputFrame, outputFrame); + } + /** * Constructor to create a customizable Hermite interpolator for every spacecraft state field. *

          diff --git a/src/main/java/org/orekit/propagation/analytical/AbstractAnalyticalPropagator.java b/src/main/java/org/orekit/propagation/analytical/AbstractAnalyticalPropagator.java index f68a4374eb..533741e0f0 100644 --- a/src/main/java/org/orekit/propagation/analytical/AbstractAnalyticalPropagator.java +++ b/src/main/java/org/orekit/propagation/analytical/AbstractAnalyticalPropagator.java @@ -470,11 +470,6 @@ public double getMass(final AbsoluteDate date) { return AbstractAnalyticalPropagator.this.getMass(date); } - /** {@inheritDoc} */ - public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame frame) { - return propagate(date).getPVCoordinates(frame); - } - /** {@inheritDoc} */ public void resetInitialState(final SpacecraftState state) { super.resetInitialState(state); diff --git a/src/main/java/org/orekit/propagation/analytical/AggregateBoundedPropagator.java b/src/main/java/org/orekit/propagation/analytical/AggregateBoundedPropagator.java index f351aa81f2..cfcbd256e4 100644 --- a/src/main/java/org/orekit/propagation/analytical/AggregateBoundedPropagator.java +++ b/src/main/java/org/orekit/propagation/analytical/AggregateBoundedPropagator.java @@ -17,22 +17,27 @@ package org.orekit.propagation.analytical; import java.util.Collection; -import java.util.Collections; -import java.util.Map.Entry; import java.util.NavigableMap; import java.util.TreeMap; +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.geometry.euclidean.threed.FieldRotation; +import org.hipparchus.geometry.euclidean.threed.Rotation; +import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.orekit.attitudes.Attitude; import org.orekit.attitudes.AttitudeProvider; -import org.orekit.attitudes.FrameAlignedProvider; +import org.orekit.attitudes.FieldAttitude; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; import org.orekit.frames.Frame; import org.orekit.orbits.Orbit; import org.orekit.propagation.BoundedPropagator; -import org.orekit.propagation.Propagator; import org.orekit.propagation.SpacecraftState; import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldPVCoordinatesProvider; +import org.orekit.utils.PVCoordinatesProvider; +import org.orekit.utils.TimeSpanMap; import org.orekit.utils.TimeStampedPVCoordinates; /** @@ -47,7 +52,8 @@ public class AggregateBoundedPropagator extends AbstractAnalyticalPropagator implements BoundedPropagator { /** Constituent propagators. */ - private final NavigableMap propagators; + private final TimeSpanMap map; + /** Minimum date for {@link #getMinDate()}. */ private final AbsoluteDate min; /** Maximum date for {@link #getMaxDate()}. */ @@ -67,19 +73,14 @@ public class AggregateBoundedPropagator extends AbstractAnalyticalPropagator * propagator with the latest {@link BoundedPropagator#getMinDate()} * is used. */ - public AggregateBoundedPropagator( - final Collection propagators) { - super(defaultAttitude(propagators)); - final NavigableMap map = - new TreeMap<>(); - for (final BoundedPropagator propagator : propagators) { - map.put(propagator.getMinDate(), propagator); - } - this.propagators = map; - this.min = map.firstEntry().getValue().getMinDate(); - this.max = map.lastEntry().getValue().getMaxDate(); - super.resetInitialState( - this.propagators.firstEntry().getValue().getInitialState()); + public AggregateBoundedPropagator(final Collection propagators) { + super(null); + map = new TimeSpanMap<>(null); + propagators.forEach(p -> map.addValidAfter(p, p.getMinDate(), false)); + setAttitudeProvider(new AggregateAttitudeProvider()); + this.min = map.getFirstNonNullSpan().getData().getMinDate(); + this.max = map.getLastNonNullSpan().getData().getMaxDate(); + super.resetInitialState(getInitialState()); } /** @@ -93,31 +94,23 @@ public AggregateBoundedPropagator( * @param min the value for {@link #getMinDate()}. * @param max the value for {@link #getMaxDate()}. */ - public AggregateBoundedPropagator( - final NavigableMap propagators, - final AbsoluteDate min, - final AbsoluteDate max) { - super(defaultAttitude(propagators.values())); - this.propagators = propagators; + public AggregateBoundedPropagator(final NavigableMap propagators, + final AbsoluteDate min, final AbsoluteDate max) { + super(null); + map = new TimeSpanMap<>(null); + propagators.forEach((d, p) -> map.addValidAfter(p, p.getMinDate(), false)); + setAttitudeProvider(new AggregateAttitudeProvider()); this.min = min; this.max = max; - super.resetInitialState( - this.propagators.firstEntry().getValue().getInitialState()); + super.resetInitialState(getInitialState()); } - /** - * Helper function for the constructor. - * @param propagators to consider. - * @return attitude provider. + /** Get the propagators map. + * @return propagators map + * @since 12.1 */ - private static AttitudeProvider defaultAttitude( - final Collection propagators) { - // this check is needed here because it can't be before the super() call in the - // constructor. - if (propagators.isEmpty()) { - throw new OrekitException(OrekitMessages.NOT_ENOUGH_PROPAGATORS); - } - return new FrameAlignedProvider(propagators.iterator().next().getFrame()); + public TimeSpanMap getPropagatorsMap() { + return map; } /** Get an unmodifiable view of the propagators map. @@ -127,9 +120,15 @@ private static AttitudeProvider defaultAttitude( *

          * @return unmodifiable view of the propagators map * @since 12.0 + * @deprecated as of 12.1, replaced by {@link #getPropagatorsMap()} */ - public NavigableMap getPropagators() { - return Collections.unmodifiableNavigableMap(propagators); + @Deprecated + public NavigableMap getPropagators() { + final NavigableMap nm = new TreeMap<>(); + for (TimeSpanMap.Span span = map.getFirstNonNullSpan(); span != null; span = span.next()) { + nm.put(span.getData().getMinDate(), span.getData()); + } + return nm; } @Override @@ -162,6 +161,11 @@ public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, return getPropagator(date).getPVCoordinates(date, frame); } + @Override + public Vector3D getPosition(final AbsoluteDate date, final Frame frame) { + return getPropagator(date).propagate(date).getPosition(frame); + } + @Override protected Orbit propagateOrbit(final AbsoluteDate date) { return getPropagator(date).propagate(date).getOrbit(); @@ -184,7 +188,7 @@ protected double getMass(final AbsoluteDate date) { @Override public SpacecraftState getInitialState() { - return propagators.firstEntry().getValue().getInitialState(); + return map.getFirstNonNullSpan().getData().getInitialState(); } @Override @@ -205,13 +209,46 @@ public void resetInitialState(final SpacecraftState state) { * @return propagator to use on date. */ private BoundedPropagator getPropagator(final AbsoluteDate date) { - final Entry entry = - propagators.floorEntry(date); - if (entry != null) { - return entry.getValue(); + final BoundedPropagator propagator = map.get(date); + if (propagator != null) { + return propagator; } else { // let the first propagator throw the exception - return propagators.firstEntry().getValue(); + return map.getFirstNonNullSpan().getData(); + } + } + + /** Local attitude provider. */ + private class AggregateAttitudeProvider implements AttitudeProvider { + + /** {@inheritDoc} */ + @Override + public Attitude getAttitude(final PVCoordinatesProvider pvProv, + final AbsoluteDate date, + final Frame frame) { + return getPropagator(date).getAttitudeProvider().getAttitude(pvProv, date, frame); + } + + /** {@inheritDoc} */ + @Override + public > FieldAttitude getAttitude(final FieldPVCoordinatesProvider pvProv, + final FieldAbsoluteDate date, + final Frame frame) { + return getPropagator(date.toAbsoluteDate()).getAttitudeProvider().getAttitude(pvProv, date, frame); + } + + /** {@inheritDoc} */ + @Override + public Rotation getAttitudeRotation(final PVCoordinatesProvider pvProv, final AbsoluteDate date, final Frame frame) { + return getPropagator(date).getAttitudeProvider().getAttitudeRotation(pvProv, date, frame); + } + + /** {@inheritDoc} */ + @Override + public > FieldRotation getAttitudeRotation(final FieldPVCoordinatesProvider pvProv, + final FieldAbsoluteDate date, + final Frame frame) { + return getPropagator(date.toAbsoluteDate()).getAttitudeProvider().getAttitudeRotation(pvProv, date, frame); } } diff --git a/src/main/java/org/orekit/propagation/analytical/BrouwerLyddaneGradientConverter.java b/src/main/java/org/orekit/propagation/analytical/BrouwerLyddaneGradientConverter.java index be05b3b8fd..0112ded2ef 100644 --- a/src/main/java/org/orekit/propagation/analytical/BrouwerLyddaneGradientConverter.java +++ b/src/main/java/org/orekit/propagation/analytical/BrouwerLyddaneGradientConverter.java @@ -60,7 +60,7 @@ public FieldBrouwerLyddanePropagator getPropagator(final FieldSpacecra final AttitudeProvider provider = propagator.getAttitudeProvider(); // Central attraction coefficient - final Gradient mu = zero.add(propagator.getMu()); + final Gradient mu = zero.newInstance(propagator.getMu()); // Return the "Field" propagator return new FieldBrouwerLyddanePropagator<>(state.getOrbit(), provider, radius, mu, diff --git a/src/main/java/org/orekit/propagation/analytical/BrouwerLyddanePropagator.java b/src/main/java/org/orekit/propagation/analytical/BrouwerLyddanePropagator.java index 92333f1181..f2b8e8046f 100644 --- a/src/main/java/org/orekit/propagation/analytical/BrouwerLyddanePropagator.java +++ b/src/main/java/org/orekit/propagation/analytical/BrouwerLyddanePropagator.java @@ -20,12 +20,12 @@ import java.util.Collections; import java.util.List; -import org.hipparchus.analysis.differentiation.UnivariateDerivative2; +import org.hipparchus.analysis.differentiation.UnivariateDerivative1; import org.hipparchus.linear.RealMatrix; import org.hipparchus.util.CombinatoricsUtils; import org.hipparchus.util.FastMath; +import org.hipparchus.util.FieldSinCos; import org.hipparchus.util.MathUtils; -import org.hipparchus.util.Precision; import org.hipparchus.util.SinCos; import org.orekit.attitudes.AttitudeProvider; import org.orekit.attitudes.FrameAlignedProvider; @@ -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.FieldKeplerianAnomalyUtility; import org.orekit.orbits.KeplerianOrbit; import org.orekit.orbits.Orbit; import org.orekit.orbits.OrbitType; @@ -654,6 +655,7 @@ public static KeplerianOrbit computeMeanOrbit(final Orbit osculating, * must be defined with an osculating orbit.

          * @see #resetInitialState(SpacecraftState, PropagationType) */ + @Override public void resetInitialState(final SpacecraftState state) { resetInitialState(state, PropagationType.OSCULATING); } @@ -680,7 +682,7 @@ public void resetInitialState(final SpacecraftState state, final PropagationType this.initialModel = (stateType == PropagationType.MEAN) ? new BLModel(keplerian, state.getMass(), referenceRadius, mu, ck0) : computeMeanParameters(keplerian, state.getMass(), epsilon, maxIterations); - this.models = new TimeSpanMap(initialModel); + this.models = new TimeSpanMap<>(initialModel); } /** {@inheritDoc} */ @@ -761,7 +763,7 @@ private BLModel computeMeanParameters(final KeplerianOrbit osculating, final dou current.mean.getPerigeeArgument() + deltaOmega, current.mean.getRightAscensionOfAscendingNode() + deltaRAAN, current.mean.getMeanAnomaly() + deltaAnom, - PositionAngleType.MEAN, + PositionAngleType.MEAN, PositionAngleType.MEAN, current.mean.getFrame(), current.mean.getDate(), mu), mass, referenceRadius, mu, ck0); @@ -781,7 +783,7 @@ private BLModel computeMeanParameters(final KeplerianOrbit osculating, final dou /** {@inheritDoc} */ public KeplerianOrbit propagateOrbit(final AbsoluteDate date) { - // compute keplerian parameters, taking derivatives into account + // compute Keplerian parameters, taking derivatives into account final BLModel current = models.get(date); return current.propagateParameters(date); } @@ -846,6 +848,7 @@ protected AbstractMatricesHarvester createHarvester(final String stmName, final * Get the names of the parameters in the matrix returned by {@link MatricesHarvester#getParametersJacobian}. * @return names of the parameters (i.e. columns) of the Jacobian matrix */ + @Override protected List getJacobiansColumnsNames() { final List columnsNames = new ArrayList<>(); for (final ParameterDriver driver : getParametersDrivers()) { @@ -873,7 +876,7 @@ private class BLModel { // preprocessed values // Constant for secular terms l'', g'', h'' - // l standing for true anomaly, g for perigee argument and h for raan + // 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; @@ -1140,94 +1143,18 @@ private class BLModel { } - /** - * Accurate computation of E - e sin(E). - * - * @param E eccentric anomaly - * @return E - e sin(E) - */ - private UnivariateDerivative2 eMeSinE(final UnivariateDerivative2 E) { - UnivariateDerivative2 x = E.sin().multiply(1 - mean.getE()); - final UnivariateDerivative2 mE2 = E.negate().multiply(E); - UnivariateDerivative2 term = E; - UnivariateDerivative2 d = E.getField().getZero(); - // the inequality test below IS intentional and should NOT be replaced by a check with a small tolerance - for (UnivariateDerivative2 x0 = d.add(Double.NaN); !Double.valueOf(x.getValue()).equals(Double.valueOf(x0.getValue()));) { - d = d.add(2); - term = term.multiply(mE2.divide(d.multiply(d.add(1)))); - x0 = x; - x = x.subtract(term); - } - return x; - } - /** * Gets eccentric anomaly from mean anomaly. - *

          The algorithm used to solve the Kepler equation has been published in: - * "Procedures for solving Kepler's Equation", A. W. Odell and R. H. Gooding, - * Celestial Mechanics 38 (1986) 307-334

          - *

          It has been copied from the OREKIT library (KeplerianOrbit class).

          - * * @param mk the mean anomaly (rad) * @return the eccentric anomaly (rad) */ - private UnivariateDerivative2 getEccentricAnomaly(final UnivariateDerivative2 mk) { - final double k1 = 3 * FastMath.PI + 2; - final double k2 = FastMath.PI - 1; - final double k3 = 6 * FastMath.PI - 1; - final double A = 3.0 * k2 * k2 / k1; - final double B = k3 * k3 / (6.0 * k1); + private UnivariateDerivative1 getEccentricAnomaly(final UnivariateDerivative1 mk) { // reduce M to [-PI PI] interval - final UnivariateDerivative2 reducedM = new UnivariateDerivative2(MathUtils.normalizeAngle(mk.getValue(), 0.0), - mk.getFirstDerivative(), - mk.getSecondDerivative()); - - // compute start value according to A. W. Odell and R. H. Gooding S12 starter - UnivariateDerivative2 ek; - if (FastMath.abs(reducedM.getValue()) < 1.0 / 6.0) { - if (FastMath.abs(reducedM.getValue()) < Precision.SAFE_MIN) { - // this is an Orekit change to the S12 starter. - // If reducedM is 0.0, the derivative of cbrt is infinite which induces NaN appearing later in - // the computation. As in this case E and M are almost equal, we initialize ek with reducedM - ek = reducedM; - } else { - // this is the standard S12 starter - ek = reducedM.add(reducedM.multiply(6).cbrt().subtract(reducedM).multiply(mean.getE())); - } - } else { - if (reducedM.getValue() < 0) { - final UnivariateDerivative2 w = reducedM.add(FastMath.PI); - ek = reducedM.add(w.multiply(-A).divide(w.subtract(B)).subtract(FastMath.PI).subtract(reducedM).multiply(mean.getE())); - } else { - final UnivariateDerivative2 minusW = reducedM.subtract(FastMath.PI); - ek = reducedM.add(minusW.multiply(A).divide(minusW.add(B)).add(FastMath.PI).subtract(reducedM).multiply(mean.getE())); - } - } + final UnivariateDerivative1 reducedM = new UnivariateDerivative1(MathUtils.normalizeAngle(mk.getValue(), 0.0), + mk.getFirstDerivative()); - final double e1 = 1 - mean.getE(); - final boolean noCancellationRisk = (e1 + ek.getValue() * ek.getValue() / 6) >= 0.1; - - // perform two iterations, each consisting of one Halley step and one Newton-Raphson step - for (int j = 0; j < 2; ++j) { - final UnivariateDerivative2 f; - UnivariateDerivative2 fd; - final UnivariateDerivative2 fdd = ek.sin().multiply(mean.getE()); - final UnivariateDerivative2 fddd = ek.cos().multiply(mean.getE()); - if (noCancellationRisk) { - f = ek.subtract(fdd).subtract(reducedM); - fd = fddd.subtract(1).negate(); - } else { - f = eMeSinE(ek).subtract(reducedM); - final UnivariateDerivative2 s = ek.multiply(0.5).sin(); - fd = s.multiply(s).multiply(2 * mean.getE()).add(e1); - } - final UnivariateDerivative2 dee = f.multiply(fd).divide(f.multiply(0.5).multiply(fdd).subtract(fd.multiply(fd))); - - // update eccentric anomaly, using expressions that limit underflow problems - final UnivariateDerivative2 w = fd.add(dee.multiply(0.5).multiply(fdd.add(dee.multiply(fdd).divide(3)))); - fd = fd.add(dee.multiply(fdd.add(dee.multiply(0.5).multiply(fdd)))); - ek = ek.subtract(f.subtract(dee.multiply(fd.subtract(w))).divide(fd)); - } + final UnivariateDerivative1 meanE = new UnivariateDerivative1(mean.getE(), 0.); + UnivariateDerivative1 ek = FieldKeplerianAnomalyUtility.ellipticMeanToEccentric(meanE, mk); // expand the result back to original range ek = ek.add(mk.getValue() - reducedM.getValue()); @@ -1281,98 +1208,97 @@ public KeplerianOrbit propagateParameters(final AbsoluteDate date) { final double m2 = M2Driver.getValue(); // Keplerian evolution - final UnivariateDerivative2 dt = new UnivariateDerivative2(date.durationFrom(mean.getDate()), 1.0, 0.0); - final UnivariateDerivative2 xnot = dt.multiply(xnotDot); + final UnivariateDerivative1 dt = new UnivariateDerivative1(date.durationFrom(mean.getDate()), 1.0); + final UnivariateDerivative1 xnot = dt.multiply(xnotDot); //____________________________________ // secular effects // mean mean anomaly (with drag Eq. 2.38 of Phipps' 1992 thesis) - final UnivariateDerivative2 dtM2 = dt.multiply(m2); - final UnivariateDerivative2 dt2M2 = dt.multiply(dtM2); - final UnivariateDerivative2 lpp = new UnivariateDerivative2(MathUtils.normalizeAngle(mean.getMeanAnomaly() + lt * xnot.getValue() + dt2M2.getValue(), 0), - lt * xnotDot + 2.0 * dtM2.getValue(), - 2.0 * m2); + 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 UnivariateDerivative2 gpp = new UnivariateDerivative2(MathUtils.normalizeAngle(mean.getPerigeeArgument() + gt * xnot.getValue(), 0), - gt * xnotDot, - 0.0); + final UnivariateDerivative1 gpp = new UnivariateDerivative1(MathUtils.normalizeAngle(mean.getPerigeeArgument() + gt * xnot.getValue(), 0), + gt * xnotDot); // mean longitude of ascending node - final UnivariateDerivative2 hpp = new UnivariateDerivative2(MathUtils.normalizeAngle(mean.getRightAscensionOfAscendingNode() + ht * xnot.getValue(), 0), - ht * xnotDot, - 0.0); + 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 UnivariateDerivative2 appDrag = dt.multiply(aRate * m2); + final UnivariateDerivative1 appDrag = dt.multiply(aRate * m2); // eccentricity - final UnivariateDerivative2 eppDrag = dt.multiply(eRate * m2); + final UnivariateDerivative1 eppDrag = dt.multiply(eRate * m2); //____________________________________ // Long periodical terms - final UnivariateDerivative2 cg1 = gpp.cos(); - final UnivariateDerivative2 sg1 = gpp.sin(); - final UnivariateDerivative2 c2g = cg1.multiply(cg1).subtract(sg1.multiply(sg1)); - final UnivariateDerivative2 s2g = cg1.multiply(sg1).add(sg1.multiply(cg1)); - final UnivariateDerivative2 c3g = c2g.multiply(cg1).subtract(s2g.multiply(sg1)); - final UnivariateDerivative2 sg2 = sg1.multiply(sg1); - final UnivariateDerivative2 sg3 = sg1.multiply(sg2); + 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 UnivariateDerivative2 d1e = sg3.multiply(dei3sg). + final UnivariateDerivative1 d1e = sg3.multiply(dei3sg). add(sg1.multiply(deisg)). add(sg2.multiply(de2sg)). add(de); // l' + g' - final UnivariateDerivative2 lp_p_gp = s2g.multiply(dlgs2g). + final UnivariateDerivative1 lpPGp = s2g.multiply(dlgs2g). add(c3g.multiply(dlgc3g)). add(cg1.multiply(dlgcg)). add(lpp). add(gpp); // h' - final UnivariateDerivative2 hp = sg2.multiply(cg1).multiply(dh2sgcg). + 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 UnivariateDerivative2 Ep = getEccentricAnomaly(lpp); - final UnivariateDerivative2 cf1 = (Ep.cos().subtract(mean.getE())).divide(Ep.cos().multiply(-mean.getE()).add(1.0)); - final UnivariateDerivative2 sf1 = (Ep.sin().multiply(n)).divide(Ep.cos().multiply(-mean.getE()).add(1.0)); - final UnivariateDerivative2 f = FastMath.atan2(sf1, cf1); - - final UnivariateDerivative2 c2f = cf1.multiply(cf1).subtract(sf1.multiply(sf1)); - final UnivariateDerivative2 s2f = cf1.multiply(sf1).add(sf1.multiply(cf1)); - final UnivariateDerivative2 c3f = c2f.multiply(cf1).subtract(s2f.multiply(sf1)); - final UnivariateDerivative2 s3f = c2f.multiply(sf1).add(s2f.multiply(cf1)); - final UnivariateDerivative2 cf2 = cf1.multiply(cf1); - final UnivariateDerivative2 cf3 = cf1.multiply(cf2); - - final UnivariateDerivative2 c2g1f = cf1.multiply(c2g).subtract(sf1.multiply(s2g)); - final UnivariateDerivative2 c2g2f = c2f.multiply(c2g).subtract(s2f.multiply(s2g)); - final UnivariateDerivative2 c2g3f = c3f.multiply(c2g).subtract(s3f.multiply(s2g)); - final UnivariateDerivative2 s2g1f = cf1.multiply(s2g).add(c2g.multiply(sf1)); - final UnivariateDerivative2 s2g2f = c2f.multiply(s2g).add(c2g.multiply(s2f)); - final UnivariateDerivative2 s2g3f = c3f.multiply(s2g).add(c2g.multiply(s3f)); - - final UnivariateDerivative2 eE = (Ep.cos().multiply(-mean.getE()).add(1.0)).reciprocal(); - final UnivariateDerivative2 eE3 = eE.multiply(eE).multiply(eE); - final UnivariateDerivative2 sigma = eE.multiply(n * n).multiply(eE).add(eE); + 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 UnivariateDerivative2 a = eE3.multiply(aCbis).add(appDrag.add(mean.getA())). + 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 UnivariateDerivative2 e = d1e.add(eppDrag.add(mean.getE())). + final UnivariateDerivative1 e = d1e.add(eppDrag.add(mean.getE())). add(eC). add(cf1.multiply(ecf)). add(cf2.multiply(e2cf)). @@ -1385,14 +1311,14 @@ public KeplerianOrbit propagateParameters(final AbsoluteDate date) { add(c2g3f.multiply(ec2g3f)); // Inclination - final UnivariateDerivative2 i = d1e.multiply(ide). + 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 + True anomaly - final UnivariateDerivative2 g_p_l = lp_p_gp.add(f.multiply(glf)). + // 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)). @@ -1404,14 +1330,14 @@ public KeplerianOrbit propagateParameters(final AbsoluteDate date) { // Longitude of ascending node - final UnivariateDerivative2 h = hp.add(f.multiply(hf)). + 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 UnivariateDerivative2 edl = s2g.multiply(edls2g). + final UnivariateDerivative1 edl = s2g.multiply(edls2g). add(cg1.multiply(edlcg)). add(c3g.multiply(edlc3g)). add(sf1.multiply(edlsf)). @@ -1421,14 +1347,15 @@ public KeplerianOrbit propagateParameters(final AbsoluteDate date) { add(s2g1f.multiply(sigma).multiply(-edls2gf)). add(s2g3f.multiply(sigma).multiply(3.0 * edls2g3f)); - final UnivariateDerivative2 A = e.multiply(lpp.cos()).subtract(edl.multiply(lpp.sin())); - final UnivariateDerivative2 B = e.multiply(lpp.sin()).add(edl.multiply(lpp.cos())); + 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())); - // True anomaly - final UnivariateDerivative2 l = FastMath.atan2(B, A); + // Mean anomaly + final UnivariateDerivative1 l = FastMath.atan2(B, A); // Argument of perigee - final UnivariateDerivative2 g = g_p_l.subtract(l); + final UnivariateDerivative1 g = gPL.subtract(l); // Return a Keplerian orbit return new KeplerianOrbit(a.getValue(), e.getValue(), i.getValue(), diff --git a/src/main/java/org/orekit/propagation/analytical/EcksteinHechlerGradientConverter.java b/src/main/java/org/orekit/propagation/analytical/EcksteinHechlerGradientConverter.java index 26bd26bc8f..19042ce2c2 100644 --- a/src/main/java/org/orekit/propagation/analytical/EcksteinHechlerGradientConverter.java +++ b/src/main/java/org/orekit/propagation/analytical/EcksteinHechlerGradientConverter.java @@ -61,7 +61,7 @@ public FieldEcksteinHechlerPropagator getPropagator(final FieldSpacecr final AttitudeProvider provider = propagator.getAttitudeProvider(); // Central attraction coefficient - final Gradient mu = zero.add(propagator.getMu()); + final Gradient mu = zero.newInstance(propagator.getMu()); // Return the "Field" propagator return new FieldEcksteinHechlerPropagator<>(state.getOrbit(), provider, radius, mu, diff --git a/src/main/java/org/orekit/propagation/analytical/EcksteinHechlerPropagator.java b/src/main/java/org/orekit/propagation/analytical/EcksteinHechlerPropagator.java index b837639156..2acc084f08 100644 --- a/src/main/java/org/orekit/propagation/analytical/EcksteinHechlerPropagator.java +++ b/src/main/java/org/orekit/propagation/analytical/EcksteinHechlerPropagator.java @@ -1114,8 +1114,8 @@ private TimeStampedPVCoordinates toCartesian(final AbsoluteDate date, final Univ final UnivariateDerivative2 alphaE = meanToEccentric(parameters[5], parameters[1], parameters[2]); final UnivariateDerivative2 cosAE = alphaE.cos(); final UnivariateDerivative2 sinAE = alphaE.sin(); - final UnivariateDerivative2 ex2 = parameters[1].multiply(parameters[1]); - final UnivariateDerivative2 ey2 = parameters[2].multiply(parameters[2]); + final UnivariateDerivative2 ex2 = parameters[1].square(); + final UnivariateDerivative2 ey2 = parameters[2].square(); final UnivariateDerivative2 exy = parameters[1].multiply(parameters[2]); final UnivariateDerivative2 q = ex2.add(ey2).subtract(1).negate().sqrt(); final UnivariateDerivative2 beta = q.add(1).reciprocal(); diff --git a/src/main/java/org/orekit/propagation/analytical/FieldAbstractAnalyticalPropagator.java b/src/main/java/org/orekit/propagation/analytical/FieldAbstractAnalyticalPropagator.java index f28cb847fb..b917c4f0cf 100644 --- a/src/main/java/org/orekit/propagation/analytical/FieldAbstractAnalyticalPropagator.java +++ b/src/main/java/org/orekit/propagation/analytical/FieldAbstractAnalyticalPropagator.java @@ -466,12 +466,6 @@ public T getMass(final FieldAbsoluteDate date) { return FieldAbstractAnalyticalPropagator.this.getMass(date); } - /** {@inheritDoc} */ - @Override - public TimeStampedFieldPVCoordinates getPVCoordinates(final FieldAbsoluteDate date, final Frame frame) { - return propagate(date).getPVCoordinates(frame); - } - /** {@inheritDoc} */ @Override public void resetInitialState(final FieldSpacecraftState state) { diff --git a/src/main/java/org/orekit/propagation/analytical/FieldBrouwerLyddanePropagator.java b/src/main/java/org/orekit/propagation/analytical/FieldBrouwerLyddanePropagator.java index c10c180e91..29c8feeedf 100644 --- a/src/main/java/org/orekit/propagation/analytical/FieldBrouwerLyddanePropagator.java +++ b/src/main/java/org/orekit/propagation/analytical/FieldBrouwerLyddanePropagator.java @@ -21,12 +21,11 @@ import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; -import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative2; +import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative1; import org.hipparchus.util.CombinatoricsUtils; import org.hipparchus.util.FastMath; import org.hipparchus.util.FieldSinCos; import org.hipparchus.util.MathUtils; -import org.hipparchus.util.Precision; import org.orekit.attitudes.AttitudeProvider; import org.orekit.attitudes.FrameAlignedProvider; import org.orekit.errors.OrekitException; @@ -35,6 +34,7 @@ import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider.UnnormalizedSphericalHarmonics; 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; @@ -676,7 +676,7 @@ public void resetInitialState(final FieldSpacecraftState state, final Propaga this.initialModel = (stateType == PropagationType.MEAN) ? new FieldBLModel<>(keplerian, state.getMass(), referenceRadius, mu, ck0) : computeMeanParameters(keplerian, state.getMass(), epsilon, maxIterations); - this.models = new FieldTimeSpanMap, T>(initialModel, state.getA().getField()); + this.models = new FieldTimeSpanMap<>(initialModel, state.getA().getField()); } /** {@inheritDoc} */ @@ -726,7 +726,7 @@ private FieldBLModel computeMeanParameters(final FieldKeplerianOrbit oscul final T one = field.getOne(); final T zero = field.getZero(); // rough initialization of the mean parameters - FieldBLModel current = new FieldBLModel(osculating, mass, referenceRadius, mu, ck0); + FieldBLModel current = new FieldBLModel<>(osculating, mass, referenceRadius, mu, ck0); // threshold for each parameter final T thresholdA = current.mean.getA().abs().add(1.0).multiply(epsilon); @@ -749,13 +749,13 @@ private FieldBLModel computeMeanParameters(final FieldKeplerianOrbit oscul // update mean parameters - current = new FieldBLModel(new FieldKeplerianOrbit(current.mean.getA() .add(deltaA), + 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, PositionAngleType.MEAN, current.mean.getFrame(), current.mean.getDate(), mu), mass, referenceRadius, mu, ck0); @@ -814,7 +814,7 @@ private static class FieldBLModel> { // preprocessed values // Constant for secular terms l'', g'', h'' - // l standing for true anomaly, g for perigee argument and h for raan + // 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; @@ -915,15 +915,15 @@ private static class FieldBLModel> { // preliminary processing final T q = app.divide(referenceRadius).reciprocal(); - T ql = q.multiply(q); + T ql = q.square(); final T y2 = ql.multiply(-0.5 * ck0[2]); - n = ((mean.getE().multiply(mean.getE()).negate()).add(1.0)).sqrt(); - final T n2 = n.multiply(n); + n = ((mean.getE().square().negate()).add(1.0)).sqrt(); + final T n2 = n.square(); final T n3 = n2.multiply(n); - final T n4 = n2.multiply(n2); + final T n4 = n2.square(); final T n6 = n4.multiply(n2); - final T n8 = n4.multiply(n4); + final T n8 = n4.square(); final T n10 = n8.multiply(n2); final T yp2 = y2.divide(n4); @@ -937,19 +937,19 @@ private static class FieldBLModel> { final FieldSinCos sc = FastMath.sinCos(mean.getI()); final T sinI1 = sc.sin(); - final T sinI2 = sinI1.multiply(sinI1); + final T sinI2 = sinI1.square(); final T cosI1 = sc.cos(); - final T cosI2 = cosI1.multiply(cosI1); + final T cosI2 = cosI1.square(); final T cosI3 = cosI2.multiply(cosI1); - final T cosI4 = cosI2.multiply(cosI2); + 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.multiply(epp); + final T epp2 = epp.square(); final T epp3 = epp2.multiply(epp); - final T epp4 = epp2.multiply(epp2); + final T epp4 = epp2.square(); if (epp.getReal() >= 1) { // Only for elliptical (e < 1) orbits @@ -992,7 +992,7 @@ private static class FieldBLModel> { 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.multiply(4.0).multiply(epp2. + 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)). @@ -1093,97 +1093,21 @@ private static class FieldBLModel> { } - /** - * Accurate computation of E - e sin(E). - * - * @param E eccentric anomaly - * @return E - e sin(E) - */ - private FieldUnivariateDerivative2 eMeSinE(final FieldUnivariateDerivative2 E) { - FieldUnivariateDerivative2 x = E.sin().multiply(mean.getE().negate().add(1.0)); - final FieldUnivariateDerivative2 mE2 = E.negate().multiply(E); - FieldUnivariateDerivative2 term = E; - FieldUnivariateDerivative2 d = E.getField().getZero(); - // the inequality test below IS intentional and should NOT be replaced by a check with a small tolerance - for (FieldUnivariateDerivative2 x0 = d.add(Double.NaN); !Double.valueOf(x.getValue().getReal()).equals(Double.valueOf(x0.getValue().getReal()));) { - d = d.add(2); - term = term.multiply(mE2.divide(d.multiply(d.add(1)))); - x0 = x; - x = x.subtract(term); - } - return x; - } - /** * Gets eccentric anomaly from mean anomaly. - *

          The algorithm used to solve the Kepler equation has been published in: - * "Procedures for solving Kepler's Equation", A. W. Odell and R. H. Gooding, - * Celestial Mechanics 38 (1986) 307-334

          - *

          It has been copied from the OREKIT library (KeplerianOrbit class).

          - * * @param mk the mean anomaly (rad) * @return the eccentric anomaly (rad) */ - private FieldUnivariateDerivative2 getEccentricAnomaly(final FieldUnivariateDerivative2 mk) { - final double k1 = 3 * FastMath.PI + 2; - final double k2 = FastMath.PI - 1; - final double k3 = 6 * FastMath.PI - 1; - final double A = 3.0 * k2 * k2 / k1; - final double B = k3 * k3 / (6.0 * k1); + private FieldUnivariateDerivative1 getEccentricAnomaly(final FieldUnivariateDerivative1 mk) { final T zero = mean.getE().getField().getZero(); // reduce M to [-PI PI] interval - final FieldUnivariateDerivative2 reducedM = new FieldUnivariateDerivative2(MathUtils.normalizeAngle(mk.getValue(), zero ), - mk.getFirstDerivative(), - mk.getSecondDerivative()); - - // compute start value according to A. W. Odell and R. H. Gooding S12 starter - FieldUnivariateDerivative2 ek; - if (reducedM.getValue().abs().getReal() < 1.0 / 6.0) { - if (reducedM.getValue().abs().getReal() < Precision.SAFE_MIN) { - // this is an Orekit change to the S12 starter. - // If reducedM is 0.0, the derivative of cbrt is infinite which induces NaN appearing later in - // the computation. As in this case E and M are almost equal, we initialize ek with reducedM - ek = reducedM; - } else { - // this is the standard S12 starter - ek = reducedM.add(reducedM.multiply(6).cbrt().subtract(reducedM).multiply(mean.getE())); - } - } else { - if (reducedM.getValue().getReal() < 0) { - final FieldUnivariateDerivative2 w = reducedM.add(FastMath.PI); - ek = reducedM.add(w.multiply(-A).divide(w.subtract(B)).subtract(FastMath.PI).subtract(reducedM).multiply(mean.getE())); - } else { - final FieldUnivariateDerivative2 minusW = reducedM.subtract(FastMath.PI); - ek = reducedM.add(minusW.multiply(A).divide(minusW.add(B)).add(FastMath.PI).subtract(reducedM).multiply(mean.getE())); - } - } + final FieldUnivariateDerivative1 reducedM = new FieldUnivariateDerivative1<>(MathUtils.normalizeAngle(mk.getValue(), zero ), + mk.getFirstDerivative()); - final T e1 = mean.getE().negate().add(1.0); - final boolean noCancellationRisk = (e1.add(ek.getValue().multiply(ek.getValue())).getReal() / 6) >= 0.1; - - // perform two iterations, each consisting of one Halley step and one Newton-Raphson step - for (int j = 0; j < 2; ++j) { - final FieldUnivariateDerivative2 f; - FieldUnivariateDerivative2 fd; - final FieldUnivariateDerivative2 fdd = ek.sin().multiply(mean.getE()); - final FieldUnivariateDerivative2 fddd = ek.cos().multiply(mean.getE()); - if (noCancellationRisk) { - f = ek.subtract(fdd).subtract(reducedM); - fd = fddd.subtract(1).negate(); - } else { - f = eMeSinE(ek).subtract(reducedM); - final FieldUnivariateDerivative2 s = ek.multiply(0.5).sin(); - fd = s.multiply(s).multiply(mean.getE().multiply(2.0)).add(e1); - } - final FieldUnivariateDerivative2 dee = f.multiply(fd).divide(f.multiply(0.5).multiply(fdd).subtract(fd.multiply(fd))); - - // update eccentric anomaly, using expressions that limit underflow problems - final FieldUnivariateDerivative2 w = fd.add(dee.multiply(0.5).multiply(fdd.add(dee.multiply(fdd).divide(3)))); - fd = fd.add(dee.multiply(fdd.add(dee.multiply(0.5).multiply(fdd)))); - ek = ek.subtract(f.subtract(dee.multiply(fd.subtract(w))).divide(fd)); - } + final FieldUnivariateDerivative1 meanE = new FieldUnivariateDerivative1<>(mean.getE(), zero); + FieldUnivariateDerivative1 ek = FieldKeplerianAnomalyUtility.ellipticMeanToEccentric(meanE, mk); // expand the result back to original range ek = ek.add(mk.getValue().subtract(reducedM.getValue())); @@ -1206,8 +1130,8 @@ private FieldUnivariateDerivative2 getEccentricAnomaly(final FieldUnivariateD private T T2(final T cosInc) { // X = (1.0 - 5.0 * cos²(inc)) - final T x = cosInc.multiply(cosInc).multiply(-5.0).add(1.0); - final T x2 = x.multiply(x); + final T x = cosInc.square().multiply(-5.0).add(1.0); + final T x2 = x.square(); // Eq. 2.48 T sum = x.getField().getZero(); @@ -1243,98 +1167,97 @@ public FieldKeplerianOrbit propagateParameters(final FieldAbsoluteDate dat final T m2 = parameters[0]; // Keplerian evolution - final FieldUnivariateDerivative2 dt = new FieldUnivariateDerivative2<>(date.durationFrom(mean.getDate()), one, zero); - final FieldUnivariateDerivative2 xnot = dt.multiply(xnotDot); + final FieldUnivariateDerivative1 dt = new FieldUnivariateDerivative1<>(date.durationFrom(mean.getDate()), one); + final FieldUnivariateDerivative1 xnot = dt.multiply(xnotDot); //____________________________________ // secular effects // mean mean anomaly - final FieldUnivariateDerivative2 dtM2 = dt.multiply(m2); - final FieldUnivariateDerivative2 dt2M2 = dt.multiply(dtM2); - final FieldUnivariateDerivative2 lpp = new FieldUnivariateDerivative2(MathUtils.normalizeAngle(mean.getMeanAnomaly().add(lt.multiply(xnot.getValue())).add(dt2M2.getValue()), zero), - lt.multiply(xnotDot).add(dtM2.multiply(2.0).getValue()), - m2.multiply(2.0)); + 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())); // mean argument of perigee - final FieldUnivariateDerivative2 gpp = new FieldUnivariateDerivative2(MathUtils.normalizeAngle(mean.getPerigeeArgument().add(gt.multiply(xnot.getValue())), zero), - gt.multiply(xnotDot), - zero); + final FieldUnivariateDerivative1 gpp = new FieldUnivariateDerivative1<>(MathUtils.normalizeAngle(mean.getPerigeeArgument().add(gt.multiply(xnot.getValue())), zero), + gt.multiply(xnotDot)); // mean longitude of ascending node - final FieldUnivariateDerivative2 hpp = new FieldUnivariateDerivative2(MathUtils.normalizeAngle(mean.getRightAscensionOfAscendingNode().add(ht.multiply(xnot.getValue())), zero), - ht.multiply(xnotDot), - zero); + 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 FieldUnivariateDerivative2 appDrag = dt.multiply(aRate.multiply(m2)); + final FieldUnivariateDerivative1 appDrag = dt.multiply(aRate.multiply(m2)); // eccentricity - final FieldUnivariateDerivative2 eppDrag = dt.multiply(eRate.multiply(m2)); + final FieldUnivariateDerivative1 eppDrag = dt.multiply(eRate.multiply(m2)); //____________________________________ // Long periodical terms - final FieldUnivariateDerivative2 cg1 = gpp.cos(); - final FieldUnivariateDerivative2 sg1 = gpp.sin(); - final FieldUnivariateDerivative2 c2g = cg1.multiply(cg1).subtract(sg1.multiply(sg1)); - final FieldUnivariateDerivative2 s2g = cg1.multiply(sg1).add(sg1.multiply(cg1)); - final FieldUnivariateDerivative2 c3g = c2g.multiply(cg1).subtract(s2g.multiply(sg1)); - final FieldUnivariateDerivative2 sg2 = sg1.multiply(sg1); - final FieldUnivariateDerivative2 sg3 = sg1.multiply(sg2); + 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 FieldUnivariateDerivative2 d1e = sg3.multiply(dei3sg). + final FieldUnivariateDerivative1 d1e = sg3.multiply(dei3sg). add(sg1.multiply(deisg)). add(sg2.multiply(de2sg)). add(de); // l' + g' - final FieldUnivariateDerivative2 lp_p_gp = s2g.multiply(dlgs2g). + final FieldUnivariateDerivative1 lpPGp = s2g.multiply(dlgs2g). add(c3g.multiply(dlgc3g)). add(cg1.multiply(dlgcg)). add(lpp). add(gpp); // h' - final FieldUnivariateDerivative2 hp = sg2.multiply(cg1).multiply(dh2sgcg). + 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 FieldUnivariateDerivative2 Ep = getEccentricAnomaly(lpp); - final FieldUnivariateDerivative2 cf1 = (Ep.cos().subtract(mean.getE())).divide(Ep.cos().multiply(mean.getE().negate()).add(1.0)); - final FieldUnivariateDerivative2 sf1 = (Ep.sin().multiply(n)).divide(Ep.cos().multiply(mean.getE().negate()).add(1.0)); - final FieldUnivariateDerivative2 f = FastMath.atan2(sf1, cf1); - - final FieldUnivariateDerivative2 c2f = cf1.multiply(cf1).subtract(sf1.multiply(sf1)); - final FieldUnivariateDerivative2 s2f = cf1.multiply(sf1).add(sf1.multiply(cf1)); - final FieldUnivariateDerivative2 c3f = c2f.multiply(cf1).subtract(s2f.multiply(sf1)); - final FieldUnivariateDerivative2 s3f = c2f.multiply(sf1).add(s2f.multiply(cf1)); - final FieldUnivariateDerivative2 cf2 = cf1.multiply(cf1); - final FieldUnivariateDerivative2 cf3 = cf1.multiply(cf2); - - final FieldUnivariateDerivative2 c2g1f = cf1.multiply(c2g).subtract(sf1.multiply(s2g)); - final FieldUnivariateDerivative2 c2g2f = c2f.multiply(c2g).subtract(s2f.multiply(s2g)); - final FieldUnivariateDerivative2 c2g3f = c3f.multiply(c2g).subtract(s3f.multiply(s2g)); - final FieldUnivariateDerivative2 s2g1f = cf1.multiply(s2g).add(c2g.multiply(sf1)); - final FieldUnivariateDerivative2 s2g2f = c2f.multiply(s2g).add(c2g.multiply(s2f)); - final FieldUnivariateDerivative2 s2g3f = c3f.multiply(s2g).add(c2g.multiply(s3f)); - - final FieldUnivariateDerivative2 eE = (Ep.cos().multiply(mean.getE().negate()).add(1.0)).reciprocal(); - final FieldUnivariateDerivative2 eE3 = eE.multiply(eE).multiply(eE); - final FieldUnivariateDerivative2 sigma = eE.multiply(n.multiply(n)).multiply(eE).add(eE); + 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); // Semi-major axis - final FieldUnivariateDerivative2 a = eE3.multiply(aCbis).add(appDrag.add(mean.getA())). + final FieldUnivariateDerivative1 a = eE3.multiply(aCbis).add(appDrag.add(mean.getA())). add(aC). add(eE3.multiply(c2g2f).multiply(ac2g2f)); // Eccentricity - final FieldUnivariateDerivative2 e = d1e.add(eppDrag.add(mean.getE())). + final FieldUnivariateDerivative1 e = d1e.add(eppDrag.add(mean.getE())). add(eC). add(cf1.multiply(ecf)). add(cf2.multiply(e2cf)). @@ -1347,14 +1270,14 @@ public FieldKeplerianOrbit propagateParameters(final FieldAbsoluteDate dat add(c2g3f.multiply(ec2g3f)); // Inclination - final FieldUnivariateDerivative2 i = d1e.multiply(ide). + 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 + True anomaly - final FieldUnivariateDerivative2 g_p_l = lp_p_gp.add(f.multiply(glf)). + // 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)). @@ -1364,16 +1287,15 @@ public FieldKeplerianOrbit propagateParameters(final FieldAbsoluteDate dat add(s2g3f.multiply(gls2g3f)). add(sigma.multiply(s2g3f).multiply(glos2g3f)); - // Longitude of ascending node - final FieldUnivariateDerivative2 h = hp.add(f.multiply(hf)). + 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 FieldUnivariateDerivative2 edl = s2g.multiply(edls2g). + final FieldUnivariateDerivative1 edl = s2g.multiply(edls2g). add(cg1.multiply(edlcg)). add(c3g.multiply(edlc3g)). add(sf1.multiply(edlsf)). @@ -1383,16 +1305,16 @@ public FieldKeplerianOrbit propagateParameters(final FieldAbsoluteDate dat add(s2g1f.multiply(sigma).multiply(edls2gf.negate())). add(s2g3f.multiply(sigma).multiply(edls2g3f.multiply(3.0))); - final FieldUnivariateDerivative2 A = e.multiply(lpp.cos()).subtract(edl.multiply(lpp.sin())); - final FieldUnivariateDerivative2 B = e.multiply(lpp.sin()).add(edl.multiply(lpp.cos())); + final FieldUnivariateDerivative1 A = e.multiply(lpp.cos()).subtract(edl.multiply(lpp.sin())); + final FieldUnivariateDerivative1 B = e.multiply(lpp.sin()).add(edl.multiply(lpp.cos())); - // True anomaly - final FieldUnivariateDerivative2 l = FastMath.atan2(B, A); + // Mean anomaly + final FieldUnivariateDerivative1 l = FastMath.atan2(B, A); // Argument of perigee - final FieldUnivariateDerivative2 g = g_p_l.subtract(l); + final FieldUnivariateDerivative1 g = gPL.subtract(l); - // Return a keplerian orbit + // Return a Keplerian orbit return new FieldKeplerianOrbit<>(a.getValue(), e.getValue(), i.getValue(), g.getValue(), h.getValue(), l.getValue(), a.getFirstDerivative(), e.getFirstDerivative(), i.getFirstDerivative(), diff --git a/src/main/java/org/orekit/propagation/analytical/FieldEcksteinHechlerPropagator.java b/src/main/java/org/orekit/propagation/analytical/FieldEcksteinHechlerPropagator.java index db55ab24bf..55dd8e44e9 100644 --- a/src/main/java/org/orekit/propagation/analytical/FieldEcksteinHechlerPropagator.java +++ b/src/main/java/org/orekit/propagation/analytical/FieldEcksteinHechlerPropagator.java @@ -791,7 +791,7 @@ private static class FieldEHModel> { final T zero = mass.getField().getZero(); final T one = mass.getField().getOne(); // preliminary processing - T q = zero.add(referenceRadius).divide(mean.getA()); + T q = zero.newInstance(referenceRadius).divide(mean.getA()); T ql = q.multiply(q); final T g2 = ql.multiply(ck0[2]); ql = ql.multiply(q); @@ -833,7 +833,7 @@ private static class FieldEHModel> { g6.multiply(13.125).multiply(one.subtract(sinI2.multiply(8.0)).add(sinI4.multiply(129.0 / 8.0)).subtract(sinI6.multiply(297.0 / 32.0)) )); - q = zero.add(3.0).divide(rdpom.multiply(32.0)); + q = zero.newInstance(3.0).divide(rdpom.multiply(32.0)); eps1 = q.multiply(g4).multiply(sinI2).multiply(sinI2.multiply(-35.0).add(30.0)).subtract( q.multiply(175.0).multiply(g6).multiply(sinI2).multiply(sinI2.multiply(-3.0).add(sinI4.multiply(2.0625)).add(1.0))); q = sinI1.multiply(3.0).divide(rdpom.multiply(8.0)); @@ -856,7 +856,7 @@ private static class FieldEHModel> { final T qC = g6.multiply(105.0 / 16.0).multiply(sinI2); final T qD = g3.multiply(-0.75).multiply(sinI1); final T qE = g5.multiply(3.75).multiply(sinI1); - kh = zero.add(0.375).divide(rdpom); + kh = zero.newInstance(0.375).divide(rdpom); kl = kh.divide(sinI1); ax1 = qq.multiply(sinI2.multiply(-3.5).add(2.0)); @@ -1058,8 +1058,8 @@ private TimeStampedFieldPVCoordinates toCartesian(final FieldAbsoluteDate final FieldUnivariateDerivative2 alphaE = meanToEccentric(parameters[5], parameters[1], parameters[2]); final FieldUnivariateDerivative2 cosAE = alphaE.cos(); final FieldUnivariateDerivative2 sinAE = alphaE.sin(); - final FieldUnivariateDerivative2 ex2 = parameters[1].multiply(parameters[1]); - final FieldUnivariateDerivative2 ey2 = parameters[2].multiply(parameters[2]); + final FieldUnivariateDerivative2 ex2 = parameters[1].square(); + final FieldUnivariateDerivative2 ey2 = parameters[2].square(); final FieldUnivariateDerivative2 exy = parameters[1].multiply(parameters[2]); final FieldUnivariateDerivative2 q = ex2.add(ey2).subtract(1).negate().sqrt(); final FieldUnivariateDerivative2 beta = q.add(1).reciprocal(); diff --git a/src/main/java/org/orekit/propagation/analytical/FieldKeplerianPropagator.java b/src/main/java/org/orekit/propagation/analytical/FieldKeplerianPropagator.java index 551c246637..3fba98b6ce 100644 --- a/src/main/java/org/orekit/propagation/analytical/FieldKeplerianPropagator.java +++ b/src/main/java/org/orekit/propagation/analytical/FieldKeplerianPropagator.java @@ -56,7 +56,7 @@ public class FieldKeplerianPropagator> extends */ public FieldKeplerianPropagator(final FieldOrbit initialFieldOrbit) { this(initialFieldOrbit, FrameAlignedProvider.of(initialFieldOrbit.getFrame()), - initialFieldOrbit.getMu(), initialFieldOrbit.getA().getField().getZero().add(DEFAULT_MASS)); + initialFieldOrbit.getMu(), initialFieldOrbit.getA().getField().getZero().newInstance(DEFAULT_MASS)); } /** Build a propagator from orbit and central attraction coefficient μ. @@ -68,7 +68,7 @@ public FieldKeplerianPropagator(final FieldOrbit initialFieldOrbit) { */ public FieldKeplerianPropagator(final FieldOrbit initialFieldOrbit, final T mu) { this(initialFieldOrbit, FrameAlignedProvider.of(initialFieldOrbit.getFrame()), - mu, initialFieldOrbit.getA().getField().getZero().add(DEFAULT_MASS)); + mu, initialFieldOrbit.getA().getField().getZero().newInstance(DEFAULT_MASS)); } /** Build a propagator from orbit and attitude provider. @@ -80,7 +80,8 @@ public FieldKeplerianPropagator(final FieldOrbit initialFieldOrbit, final T m */ public FieldKeplerianPropagator(final FieldOrbit initialFieldOrbit, final AttitudeProvider attitudeProv) { - this(initialFieldOrbit, attitudeProv, initialFieldOrbit.getMu(), initialFieldOrbit.getA().getField().getZero().add(DEFAULT_MASS)); + this(initialFieldOrbit, attitudeProv, initialFieldOrbit.getMu(), + initialFieldOrbit.getA().getField().getZero().newInstance(DEFAULT_MASS)); } /** Build a propagator from orbit, attitude provider and central attraction @@ -93,7 +94,7 @@ public FieldKeplerianPropagator(final FieldOrbit initialFieldOrbit, public FieldKeplerianPropagator(final FieldOrbit initialFieldOrbit, final AttitudeProvider attitudeProv, final T mu) { - this(initialFieldOrbit, attitudeProv, mu, initialFieldOrbit.getA().getField().getZero().add(DEFAULT_MASS)); + this(initialFieldOrbit, attitudeProv, mu, initialFieldOrbit.getA().getField().getZero().newInstance(DEFAULT_MASS)); } /** Build propagator from orbit, attitude provider, central attraction @@ -136,8 +137,9 @@ private FieldSpacecraftState fixState(final FieldOrbit orbit, final FieldA final FieldArrayDictionary additionalStatesderivatives) { final OrbitType type = orbit.getType(); final T[] stateVector = MathArrays.buildArray(mass.getField(), 6); - type.mapOrbitToArray(orbit, PositionAngleType.TRUE, stateVector, null); - final FieldOrbit fixedOrbit = type.mapArrayToOrbit(stateVector, null, PositionAngleType.TRUE, + final PositionAngleType positionAngleType = PositionAngleType.MEAN; + type.mapOrbitToArray(orbit, positionAngleType, stateVector, null); + final FieldOrbit fixedOrbit = type.mapArrayToOrbit(stateVector, null, positionAngleType, orbit.getDate(), mu, orbit.getFrame()); FieldSpacecraftState fixedState = new FieldSpacecraftState<>(fixedOrbit, attitude, mass); if (additionalStates != null) { diff --git a/src/main/java/org/orekit/propagation/analytical/KeplerianGradientConverter.java b/src/main/java/org/orekit/propagation/analytical/KeplerianGradientConverter.java index 51ee53e30d..2ebdd8a46d 100644 --- a/src/main/java/org/orekit/propagation/analytical/KeplerianGradientConverter.java +++ b/src/main/java/org/orekit/propagation/analytical/KeplerianGradientConverter.java @@ -59,7 +59,7 @@ public FieldKeplerianPropagator getPropagator(final FieldSpacecraftSta final AttitudeProvider provider = propagator.getAttitudeProvider(); // Central attraction coefficient - final Gradient mu = zero.add(propagator.getInitialState().getMu()); + final Gradient mu = zero.newInstance(propagator.getInitialState().getMu()); // Return the "Field" propagator return new FieldKeplerianPropagator<>(state.getOrbit(), provider, mu); diff --git a/src/main/java/org/orekit/propagation/analytical/KeplerianPropagator.java b/src/main/java/org/orekit/propagation/analytical/KeplerianPropagator.java index 81c8cf1633..f1f1f4903b 100644 --- a/src/main/java/org/orekit/propagation/analytical/KeplerianPropagator.java +++ b/src/main/java/org/orekit/propagation/analytical/KeplerianPropagator.java @@ -129,8 +129,9 @@ private SpacecraftState fixState(final Orbit orbit, final Attitude attitude, fin final DoubleArrayDictionary additionalStatesDerivatives) { final OrbitType type = orbit.getType(); final double[] stateVector = new double[6]; - type.mapOrbitToArray(orbit, PositionAngleType.TRUE, stateVector, null); - final Orbit fixedOrbit = type.mapArrayToOrbit(stateVector, null, PositionAngleType.TRUE, + final PositionAngleType positionAngleType = PositionAngleType.MEAN; + type.mapOrbitToArray(orbit, positionAngleType, stateVector, null); + final Orbit fixedOrbit = type.mapArrayToOrbit(stateVector, null, positionAngleType, orbit.getDate(), mu, orbit.getFrame()); SpacecraftState fixedState = new SpacecraftState(fixedOrbit, attitude, mass); if (additionalStates != null) { diff --git a/src/main/java/org/orekit/propagation/analytical/gnss/GLONASSAnalyticalPropagator.java b/src/main/java/org/orekit/propagation/analytical/gnss/GLONASSAnalyticalPropagator.java index f7ba5b59ca..f9faa1c8a7 100644 --- a/src/main/java/org/orekit/propagation/analytical/gnss/GLONASSAnalyticalPropagator.java +++ b/src/main/java/org/orekit/propagation/analytical/gnss/GLONASSAnalyticalPropagator.java @@ -169,10 +169,10 @@ public PVCoordinates propagateInEcef(final AbsoluteDate date) { final UnivariateDerivative2 w = FastMath.floor(dTpr.divide(GLONASS_MEAN_DRACONIAN_PERIOD + glonassOrbit.getDeltaT())); // Current inclination - final UnivariateDerivative2 i = zero.add(GLONASS_MEAN_INCLINATION / 180 * GNSSConstants.GLONASS_PI + glonassOrbit.getDeltaI()); + final UnivariateDerivative2 i = zero.newInstance(GLONASS_MEAN_INCLINATION / 180 * GNSSConstants.GLONASS_PI + glonassOrbit.getDeltaI()); // Eccentricity - final UnivariateDerivative2 e = zero.add(glonassOrbit.getE()); + final UnivariateDerivative2 e = zero.newInstance(glonassOrbit.getE()); // Mean draconique period in orbite w+1 and mean motion final UnivariateDerivative2 tDR = w.multiply(2.0).add(1.0).multiply(glonassOrbit.getDeltaTDot()). @@ -203,7 +203,7 @@ public PVCoordinates propagateInEcef(final AbsoluteDate date) { // Current mean longitude final UnivariateDerivative2 correction = dTpr. subtract(w.multiply(GLONASS_MEAN_DRACONIAN_PERIOD + glonassOrbit.getDeltaT())). - subtract(w.multiply(w).multiply(glonassOrbit.getDeltaTDot())); + subtract(w.square().multiply(glonassOrbit.getDeltaTDot())); final UnivariateDerivative2 m = m1.add(n.multiply(correction)); // Take into consideration the periodic perturbations @@ -227,9 +227,9 @@ public PVCoordinates propagateInEcef(final AbsoluteDate date) { paCorr = zero; } else { if (lCorr.getValue() == eCorr.getValue()) { - paCorr = zero.add(0.5 * GNSSConstants.GLONASS_PI); + paCorr = zero.newInstance(0.5 * GNSSConstants.GLONASS_PI); } else if (lCorr.getValue() == -eCorr.getValue()) { - paCorr = zero.add(-0.5 * GNSSConstants.GLONASS_PI); + paCorr = zero.newInstance(-0.5 * GNSSConstants.GLONASS_PI); } else { paCorr = FastMath.atan2(hCorr, lCorr); } @@ -374,7 +374,7 @@ private UnivariateDerivative2 eMeSinE(final UnivariateDerivative2 E, final Univa * @return the true anomaly (rad) */ private UnivariateDerivative2 getTrueAnomaly(final UnivariateDerivative2 ek, final UnivariateDerivative2 ecc) { - final UnivariateDerivative2 svk = ek.sin().multiply(FastMath.sqrt( ecc.multiply(ecc).negate().add(1.0))); + final UnivariateDerivative2 svk = ek.sin().multiply(FastMath.sqrt( ecc.square().negate().add(1.0))); final UnivariateDerivative2 cvk = ek.cos().subtract(ecc); return svk.atan2(cvk); } @@ -428,7 +428,7 @@ private UnivariateDerivative2 computeSma(final UnivariateDerivative2 tDR, final UnivariateDerivative2 sin2I = sinI.multiply(sinI); final UnivariateDerivative2 ome2 = e.multiply(e).negate().add(1.0); final UnivariateDerivative2 ome2Pow3o2 = FastMath.sqrt(ome2).multiply(ome2); - final UnivariateDerivative2 pa = zero.add(glonassOrbit.getPa()); + final UnivariateDerivative2 pa = zero.newInstance(glonassOrbit.getPa()); final UnivariateDerivative2 cosPA = FastMath.cos(pa); final UnivariateDerivative2 opecosPA = e.multiply(cosPA).add(1.0); final UnivariateDerivative2 opecosPAPow2 = opecosPA.multiply(opecosPA); diff --git a/src/main/java/org/orekit/propagation/analytical/intelsat/FieldIntelsatElevenElements.java b/src/main/java/org/orekit/propagation/analytical/intelsat/FieldIntelsatElevenElements.java new file mode 100644 index 0000000000..7cff95c3a9 --- /dev/null +++ b/src/main/java/org/orekit/propagation/analytical/intelsat/FieldIntelsatElevenElements.java @@ -0,0 +1,232 @@ +/* Copyright 2002-2024 Airbus Defence and Space + * 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. + * Airbus Defence and Space 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.intelsat; + +import org.hipparchus.CalculusFieldElement; +import org.orekit.time.FieldAbsoluteDate; + +/** + * This class is a container for a single set of Intelsat's 11 Elements data. + *

          + * Intelsat's 11 elements are defined in ITU-R S.1525 standard. + *

          + * + * @author Bryan Cazabonne + * @since 12.1 + */ +public class FieldIntelsatElevenElements> { + + /** + * Elements epoch. + */ + private final FieldAbsoluteDate epoch; + + /** + * Mean longitude (East of Greenwich). + */ + private final T lm0; + + /** + * Drift rate. + */ + private final T lm1; + + /** + * Drift acceleration. + */ + private final T lm2; + + /** + * Longitude oscillation-amplitude for the cosine term. + */ + private final T lonC; + + /** + * Rate of change of longitude, for the cosine term. + */ + private final T lonC1; + + /** + * Longitude oscillation-amplitude for the sine term. + */ + private final T lonS; + + /** + * Rate of change of longitude, for the sine term. + */ + private final T lonS1; + + /** + * Latitude oscillation-amplitude for the cosine term. + */ + private final T latC; + + /** + * Rate of change of latitude, for the cosine term. + */ + private final T latC1; + + /** + * Latitude oscillation-amplitude for the sine term. + */ + private final T latS; + + /** + * Rate of change of latitude, for the sine term. + */ + private final T latS1; + + /** + * Constructor. + * + * @param epoch elements epoch + * @param lm0 mean longitude (East of Greenwich) in degrees + * @param lm1 drift rate in degrees/day + * @param lm2 drift acceleration in degrees/day/day + * @param lonC longitude oscillation-amplitude for the cosine term in degrees + * @param lonC1 rate of change of longitude, for the cosine term, in degrees/day + * @param lonS longitude oscillation-amplitude for the sine term in degrees + * @param lonS1 rate of change of longitude, for the sine term, in degrees/day + * @param latC latitude oscillation-amplitude for the cosine term in degrees + * @param latC1 rate of change of latitude, for the cosine term, in degrees/day + * @param latS latitude oscillation-amplitude for the sine term in degrees + * @param latS1 rate of change of latitude, for the sine term, in degrees/day + */ + public FieldIntelsatElevenElements(final FieldAbsoluteDate epoch, final T lm0, final T lm1, final T lm2, final T lonC, final T lonC1, final T lonS, final T lonS1, + final T latC, final T latC1, final T latS, final T latS1) { + this.epoch = epoch; + this.lm0 = lm0; + this.lm1 = lm1; + this.lm2 = lm2; + this.lonC = lonC; + this.lonC1 = lonC1; + this.lonS = lonS; + this.lonS1 = lonS1; + this.latC = latC; + this.latC1 = latC1; + this.latS = latS; + this.latS1 = latS1; + } + + /** + * Get the elements epoch. + * + * @return elements epoch + */ + public FieldAbsoluteDate getEpoch() { + return epoch; + } + + /** + * Get the mean longitude (East of Greenwich). + * + * @return the mean longitude (East of Greenwich) in degrees + */ + public T getLm0() { + return lm0; + } + + /** + * Get the drift rate. + * + * @return the drift rate in degrees/day + */ + public T getLm1() { + return lm1; + } + + /** + * Get the drift acceleration. + * + * @return the drift acceleration in degrees/day/day + */ + public T getLm2() { + return lm2; + } + + /** + * Get the longitude oscillation-amplitude for the cosine term. + * + * @return the longitude oscillation-amplitude for the cosine term in degrees + */ + public T getLonC() { + return lonC; + } + + /** + * Get the rate of change of longitude, for the cosine term. + * + * @return the rate of change of longitude, for the cosine term, in degrees/day + */ + public T getLonC1() { + return lonC1; + } + + /** + * Get the longitude oscillation-amplitude for the sine term. + * + * @return the longitude oscillation-amplitude for the sine term in degrees + */ + public T getLonS() { + return lonS; + } + + /** + * Get the rate of change of longitude, for the sine term. + * + * @return the rate of change of longitude, for the sine term, in degrees/day + */ + public T getLonS1() { + return lonS1; + } + + /** + * Get the latitude oscillation-amplitude for the cosine term. + * + * @return the latitude oscillation-amplitude for the cosine term in degrees + */ + public T getLatC() { + return latC; + } + + /** + * Get the rate of change of latitude, for the cosine term. + * + * @return the rate of change of latitude, for the cosine term, in degrees/day + */ + public T getLatC1() { + return latC1; + } + + /** + * Get the latitude oscillation-amplitude for the sine term. + * + * @return the latitude oscillation-amplitude for the sine term in degrees + */ + public T getLatS() { + return latS; + } + + /** + * Get the rate of change of latitude, for the sine term. + * + * @return the rate of change of latitude, for the sine term, in degrees/day + */ + public T getLatS1() { + return latS1; + } +} diff --git a/src/main/java/org/orekit/propagation/analytical/intelsat/FieldIntelsatElevenElementsPropagator.java b/src/main/java/org/orekit/propagation/analytical/intelsat/FieldIntelsatElevenElementsPropagator.java new file mode 100644 index 0000000000..4441865fae --- /dev/null +++ b/src/main/java/org/orekit/propagation/analytical/intelsat/FieldIntelsatElevenElementsPropagator.java @@ -0,0 +1,326 @@ +/* Copyright 2002-2024 Airbus Defence and Space + * 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. + * Airbus Defence and Space 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.intelsat; + +import java.util.Collections; +import java.util.List; +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative2; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.FieldSinCos; +import org.orekit.annotation.DefaultDataContext; +import org.orekit.attitudes.AttitudeProvider; +import org.orekit.attitudes.FieldAttitude; +import org.orekit.attitudes.FrameAlignedProvider; +import org.orekit.data.DataContext; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.frames.Frame; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.FieldCartesianOrbit; +import org.orekit.orbits.FieldOrbit; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.Propagator; +import org.orekit.propagation.analytical.FieldAbstractAnalyticalPropagator; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.Constants; +import org.orekit.utils.FieldPVCoordinates; +import org.orekit.utils.IERSConventions; +import org.orekit.utils.ParameterDriver; +import org.orekit.utils.units.Unit; + +/** + * This class provides elements to propagate Intelsat's 11 elements. + *

          + * Intelsat's 11 elements propagation is defined in ITU-R S.1525 standard. + *

          + * + * @author Bryan Cazabonne + * @since 12.1 + */ +public class FieldIntelsatElevenElementsPropagator> extends FieldAbstractAnalyticalPropagator { + + /** + * Intelsat's 11 elements. + */ + private final FieldIntelsatElevenElements elements; + + /** + * Inertial frame for the output orbit. + */ + private final Frame inertialFrame; + + /** + * ECEF frame related to the Intelsat's 11 elements. + */ + private final Frame ecefFrame; + + /** + * Spacecraft mass in kilograms. + */ + private final T mass; + + /** + * Compute spacecraft's east longitude. + */ + private FieldUnivariateDerivative2 eastLongitudeDegrees; + + /** + * Compute spacecraft's geocentric latitude. + */ + private FieldUnivariateDerivative2 geocentricLatitudeDegrees; + + /** + * Compute spacecraft's orbit radius. + */ + private FieldUnivariateDerivative2 orbitRadius; + + /** + * Default constructor. + *

          + * This constructor uses the {@link DataContext#getDefault() default data context}. + *

          + *

          The attitude provider is set by default to be aligned with the inertial frame.
          + * The mass is set by default to the + * {@link org.orekit.propagation.Propagator#DEFAULT_MASS DEFAULT_MASS}.
          + * The inertial frame is set by default to the + * {@link org.orekit.frames.Predefined#TOD_CONVENTIONS_2010_SIMPLE_EOP TOD frame} in the default data + * context.
          + * The ECEF frame is set by default to the + * {@link org.orekit.frames.Predefined#ITRF_CIO_CONV_2010_SIMPLE_EOP + * CIO/2010-based ITRF simple EOP} in the default data context. + *

          + * + * @param elements Intelsat's 11 elements + */ + @DefaultDataContext + public FieldIntelsatElevenElementsPropagator(final FieldIntelsatElevenElements elements) { + this(elements, FramesFactory.getTOD(IERSConventions.IERS_2010, true), FramesFactory.getITRF(IERSConventions.IERS_2010, true)); + } + + /** + * Constructor. + * + *

          The attitude provider is set by default to be aligned with the inertial frame.
          + * The mass is set by default to the + * {@link org.orekit.propagation.Propagator#DEFAULT_MASS DEFAULT_MASS}.
          + *

          + * + * @param elements Intelsat's 11 elements + * @param inertialFrame inertial frame for the output orbit + * @param ecefFrame ECEF frame related to the Intelsat's 11 elements + */ + public FieldIntelsatElevenElementsPropagator(final FieldIntelsatElevenElements elements, final Frame inertialFrame, final Frame ecefFrame) { + this(elements, inertialFrame, ecefFrame, FrameAlignedProvider.of(inertialFrame), elements.getEpoch().getField().getZero().add(Propagator.DEFAULT_MASS)); + } + + /** + * Constructor. + * + * @param elements Intelsat's 11 elements + * @param inertialFrame inertial frame for the output orbit + * @param ecefFrame ECEF frame related to the Intelsat's 11 elements + * @param attitudeProvider attitude provider + * @param mass spacecraft mass + */ + public FieldIntelsatElevenElementsPropagator(final FieldIntelsatElevenElements elements, final Frame inertialFrame, final Frame ecefFrame, + final AttitudeProvider attitudeProvider, final T mass) { + super(elements.getEpoch().getField(), attitudeProvider); + this.elements = elements; + this.inertialFrame = inertialFrame; + this.ecefFrame = ecefFrame; + this.mass = mass; + setStartDate(elements.getEpoch()); + final FieldOrbit orbitAtElementsDate = propagateOrbit(elements.getEpoch(), getParameters(elements.getEpoch().getField())); + final FieldAttitude attitude = attitudeProvider.getAttitude(orbitAtElementsDate, elements.getEpoch(), inertialFrame); + super.resetInitialState(new FieldSpacecraftState<>(orbitAtElementsDate, attitude, mass)); + } + + /** + * Converts the Intelsat's 11 elements into Position/Velocity coordinates in ECEF. + * + * @param date computation epoch + * @return Position/Velocity coordinates in ECEF + */ + public FieldPVCoordinates propagateInEcef(final FieldAbsoluteDate date) { + final Field field = date.getField(); + final FieldUnivariateDerivative2 tDays = new FieldUnivariateDerivative2<>(date.durationFrom(elements.getEpoch()), field.getOne(), field.getZero()).divide( + Constants.JULIAN_DAY); + final T wDegreesPerDay = elements.getLm1().add(IntelsatElevenElements.DRIFT_RATE_SHIFT_DEG_PER_DAY); + final FieldUnivariateDerivative2 wt = FastMath.toRadians(tDays.multiply(wDegreesPerDay)); + final FieldSinCos> scWt = FastMath.sinCos(wt); + final FieldSinCos> sc2Wt = FastMath.sinCos(wt.multiply(2.0)); + final FieldUnivariateDerivative2 satelliteEastLongitudeDegrees = computeSatelliteEastLongitudeDegrees(tDays, scWt, sc2Wt); + final FieldUnivariateDerivative2 satelliteGeocentricLatitudeDegrees = computeSatelliteGeocentricLatitudeDegrees(tDays, scWt); + final FieldUnivariateDerivative2 satelliteRadius = computeSatelliteRadiusKilometers(wDegreesPerDay, scWt).multiply(Unit.KILOMETRE.getScale()); + this.eastLongitudeDegrees = satelliteEastLongitudeDegrees; + this.geocentricLatitudeDegrees = satelliteGeocentricLatitudeDegrees; + this.orbitRadius = satelliteRadius; + final FieldSinCos> scLongitude = FastMath.sinCos(FastMath.toRadians(satelliteEastLongitudeDegrees)); + final FieldSinCos> scLatitude = FastMath.sinCos(FastMath.toRadians(satelliteGeocentricLatitudeDegrees)); + final FieldVector3D> positionWithDerivatives = new FieldVector3D<>(satelliteRadius.multiply(scLatitude.cos()).multiply(scLongitude.cos()), + satelliteRadius.multiply(scLatitude.cos()).multiply(scLongitude.sin()), + satelliteRadius.multiply(scLatitude.sin())); + return new FieldPVCoordinates<>(new FieldVector3D<>(positionWithDerivatives.getX().getValue(), // + positionWithDerivatives.getY().getValue(), // + positionWithDerivatives.getZ().getValue()), // + new FieldVector3D<>(positionWithDerivatives.getX().getFirstDerivative(), // + positionWithDerivatives.getY().getFirstDerivative(), // + positionWithDerivatives.getZ().getFirstDerivative()), // + new FieldVector3D<>(positionWithDerivatives.getX().getSecondDerivative(), // + positionWithDerivatives.getY().getSecondDerivative(), // + positionWithDerivatives.getZ().getSecondDerivative())); + } + + /** + * {@inheritDoc}. + */ + @Override + public void resetInitialState(final FieldSpacecraftState state) { + throw new OrekitException(OrekitMessages.NON_RESETABLE_STATE); + } + + /** + * {@inheritDoc}. + */ + @Override + protected T getMass(final FieldAbsoluteDate date) { + return mass; + } + + /** + * {@inheritDoc}. + */ + @Override + protected void resetIntermediateState(final FieldSpacecraftState state, final boolean forward) { + throw new OrekitException(OrekitMessages.NON_RESETABLE_STATE); + } + + /** + * {@inheritDoc}. + */ + @Override + protected FieldOrbit propagateOrbit(final FieldAbsoluteDate date, final T[] parameters) { + return new FieldCartesianOrbit<>(ecefFrame.getTransformTo(inertialFrame, date).transformPVCoordinates(propagateInEcef(date)), inertialFrame, date, + date.getField().getZero().add(Constants.WGS84_EARTH_MU)); + } + + /** + * Computes the satellite's east longitude. + * + * @param tDays delta time in days + * @param scW sin/cos of the W angle + * @param sc2W sin/cos of the 2xW angle + * @return the satellite's east longitude in degrees + */ + private FieldUnivariateDerivative2 computeSatelliteEastLongitudeDegrees(final FieldUnivariateDerivative2 tDays, final FieldSinCos> scW, + final FieldSinCos> sc2W) { + final FieldUnivariateDerivative2 longitude = tDays.multiply(tDays).multiply(elements.getLm2()) // + .add(tDays.multiply(elements.getLm1())) // + .add(elements.getLm0()); + final FieldUnivariateDerivative2 cosineLongitudeTerm = scW.cos().multiply(tDays.multiply(elements.getLonC1()).add(elements.getLonC())); + final FieldUnivariateDerivative2 sineLongitudeTerm = scW.sin().multiply(tDays.multiply(elements.getLonS1()).add(elements.getLonS())); + final FieldUnivariateDerivative2 latitudeTerm = sc2W.sin() + .multiply(elements.getLatC() + .multiply(elements.getLatC()) + .subtract(elements.getLatS().multiply(elements.getLatS())) + .multiply(0.5)) // + .subtract(sc2W.cos().multiply(elements.getLatC().multiply(elements.getLatS()))) // + .multiply(IntelsatElevenElements.K); + return longitude.add(cosineLongitudeTerm).add(sineLongitudeTerm).add(latitudeTerm); + } + + /** + * Computes the satellite's geocentric latitude. + * + * @param tDays delta time in days + * @param scW sin/cos of the W angle + * @return he satellite geocentric latitude in degrees + */ + private FieldUnivariateDerivative2 computeSatelliteGeocentricLatitudeDegrees(final FieldUnivariateDerivative2 tDays, + final FieldSinCos> scW) { + final FieldUnivariateDerivative2 cosineTerm = scW.cos().multiply(tDays.multiply(elements.getLatC1()).add(elements.getLatC())); + final FieldUnivariateDerivative2 sineTerm = scW.sin().multiply(tDays.multiply(elements.getLatS1()).add(elements.getLatS())); + return cosineTerm.add(sineTerm); + } + + /** + * Computes the satellite's orbit radius. + * + * @param wDegreesPerDay W angle in degrees/day + * @param scW sin/cos of the W angle + * @return the satellite's orbit radius in kilometers + */ + private FieldUnivariateDerivative2 computeSatelliteRadiusKilometers(final T wDegreesPerDay, final FieldSinCos> scW) { + final T coefficient = elements.getLm1() + .multiply(2.0) + .divide(wDegreesPerDay.subtract(elements.getLm1()).multiply(3.0)) + .negate() + .add(1.0) + .multiply(IntelsatElevenElements.SYNCHRONOUS_RADIUS_KM); + return scW.sin() + .multiply(elements.getLonC().multiply(IntelsatElevenElements.K)) + .add(1.0) + .subtract(scW.cos().multiply(elements.getLonS().multiply(IntelsatElevenElements.K))) + .multiply(coefficient); + } + + /** + * Get the computed satellite's east longitude. + * + * @return the satellite's east longitude in degrees + */ + public FieldUnivariateDerivative2 getEastLongitudeDegrees() { + return eastLongitudeDegrees; + } + + /** + * Get the computed satellite's geocentric latitude. + * + * @return the satellite's geocentric latitude in degrees + */ + public FieldUnivariateDerivative2 getGeocentricLatitudeDegrees() { + return geocentricLatitudeDegrees; + } + + /** + * Get the computed satellite's orbit. + * + * @return satellite's orbit radius in meters + */ + public FieldUnivariateDerivative2 getOrbitRadius() { + return orbitRadius; + } + + /** + * {@inheritDoc}. + */ + @Override + public Frame getFrame() { + return inertialFrame; + } + + /** + * {@inheritDoc}. + */ + @Override + public List getParametersDrivers() { + return Collections.emptyList(); + } +} diff --git a/src/main/java/org/orekit/propagation/analytical/intelsat/IntelsatElevenElements.java b/src/main/java/org/orekit/propagation/analytical/intelsat/IntelsatElevenElements.java new file mode 100644 index 0000000000..53b464add5 --- /dev/null +++ b/src/main/java/org/orekit/propagation/analytical/intelsat/IntelsatElevenElements.java @@ -0,0 +1,246 @@ +/* Copyright 2002-2024 Airbus Defence and Space + * 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. + * Airbus Defence and Space 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.intelsat; + +import org.orekit.time.AbsoluteDate; + +/** + * This class is a container for a single set of Intelsat's 11 Elements data. + *

          + * Intelsat's 11 elements are defined in ITU-R S.1525 standard. + *

          + * + * @author Bryan Cazabonne + * @since 12.1 + */ +public class IntelsatElevenElements { + + /** + * Sun synchronous radius in kilometers. + */ + public static final double SYNCHRONOUS_RADIUS_KM = 42164.57; + + /** + * PI over 360. + */ + public static final double K = 0.0087266462; + + /** + * Longitude drift rate. + */ + public static final double DRIFT_RATE_SHIFT_DEG_PER_DAY = 360.98564; + + /** + * Elements epoch. + */ + private final AbsoluteDate epoch; + + /** + * Mean longitude (East of Greenwich). + */ + private final double lm0; + + /** + * Drift rate. + */ + private final double lm1; + + /** + * Drift acceleration. + */ + private final double lm2; + + /** + * Longitude oscillation-amplitude for the cosine term. + */ + private final double lonC; + + /** + * Rate of change of longitude, for the cosine term. + */ + private final double lonC1; + + /** + * Longitude oscillation-amplitude for the sine term. + */ + private final double lonS; + + /** + * Rate of change of longitude, for the sine term. + */ + private final double lonS1; + + /** + * Latitude oscillation-amplitude for the cosine term. + */ + private final double latC; + + /** + * Rate of change of latitude, for the cosine term. + */ + private final double latC1; + + /** + * Latitude oscillation-amplitude for the sine term. + */ + private final double latS; + + /** + * Rate of change of latitude, for the sine term. + */ + private final double latS1; + + /** + * Constructor. + * + * @param epoch elements epoch + * @param lm0 mean longitude (East of Greenwich) in degrees + * @param lm1 drift rate in degrees/day + * @param lm2 drift acceleration in degrees/day/day + * @param lonC longitude oscillation-amplitude for the cosine term in degrees + * @param lonC1 rate of change of longitude, for the cosine term, in degrees/day + * @param lonS longitude oscillation-amplitude for the sine term in degrees + * @param lonS1 rate of change of longitude, for the sine term, in degrees/day + * @param latC latitude oscillation-amplitude for the cosine term in degrees + * @param latC1 rate of change of latitude, for the cosine term, in degrees/day + * @param latS latitude oscillation-amplitude for the sine term in degrees + * @param latS1 rate of change of latitude, for the sine term, in degrees/day + */ + public IntelsatElevenElements(final AbsoluteDate epoch, final double lm0, final double lm1, final double lm2, final double lonC, final double lonC1, final double lonS, + final double lonS1, final double latC, final double latC1, final double latS, final double latS1) { + this.epoch = epoch; + this.lm0 = lm0; + this.lm1 = lm1; + this.lm2 = lm2; + this.lonC = lonC; + this.lonC1 = lonC1; + this.lonS = lonS; + this.lonS1 = lonS1; + this.latC = latC; + this.latC1 = latC1; + this.latS = latS; + this.latS1 = latS1; + } + + /** + * Get the elements epoch. + * + * @return elements epoch + */ + public AbsoluteDate getEpoch() { + return epoch; + } + + /** + * Get the mean longitude (East of Greenwich). + * + * @return the mean longitude (East of Greenwich) in degrees + */ + public double getLm0() { + return lm0; + } + + /** + * Get the drift rate. + * + * @return the drift rate in degrees/day + */ + public double getLm1() { + return lm1; + } + + /** + * Get the drift acceleration. + * + * @return the drift acceleration in degrees/day/day + */ + public double getLm2() { + return lm2; + } + + /** + * Get the longitude oscillation-amplitude for the cosine term. + * + * @return the longitude oscillation-amplitude for the cosine term in degrees + */ + public double getLonC() { + return lonC; + } + + /** + * Get the rate of change of longitude, for the cosine term. + * + * @return the rate of change of longitude, for the cosine term, in degrees/day + */ + public double getLonC1() { + return lonC1; + } + + /** + * Get the longitude oscillation-amplitude for the sine term. + * + * @return the longitude oscillation-amplitude for the sine term in degrees + */ + public double getLonS() { + return lonS; + } + + /** + * Get the rate of change of longitude, for the sine term. + * + * @return the rate of change of longitude, for the sine term, in degrees/day + */ + public double getLonS1() { + return lonS1; + } + + /** + * Get the latitude oscillation-amplitude for the cosine term. + * + * @return the latitude oscillation-amplitude for the cosine term in degrees + */ + public double getLatC() { + return latC; + } + + /** + * Get the rate of change of latitude, for the cosine term. + * + * @return the rate of change of latitude, for the cosine term, in degrees/day + */ + public double getLatC1() { + return latC1; + } + + /** + * Get the latitude oscillation-amplitude for the sine term. + * + * @return the latitude oscillation-amplitude for the sine term in degrees + */ + public double getLatS() { + return latS; + } + + /** + * Get the rate of change of latitude, for the sine term. + * + * @return the rate of change of latitude, for the sine term, in degrees/day + */ + public double getLatS1() { + return latS1; + } +} diff --git a/src/main/java/org/orekit/propagation/analytical/intelsat/IntelsatElevenElementsPropagator.java b/src/main/java/org/orekit/propagation/analytical/intelsat/IntelsatElevenElementsPropagator.java new file mode 100644 index 0000000000..3e66884f36 --- /dev/null +++ b/src/main/java/org/orekit/propagation/analytical/intelsat/IntelsatElevenElementsPropagator.java @@ -0,0 +1,301 @@ +/* Copyright 2002-2024 Airbus Defence and Space + * 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. + * Airbus Defence and Space 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.intelsat; + +import org.hipparchus.analysis.differentiation.UnivariateDerivative2; +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.orekit.annotation.DefaultDataContext; +import org.orekit.attitudes.Attitude; +import org.orekit.attitudes.AttitudeProvider; +import org.orekit.attitudes.FrameAlignedProvider; +import org.orekit.data.DataContext; +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.propagation.Propagator; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.analytical.AbstractAnalyticalPropagator; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.Constants; +import org.orekit.utils.IERSConventions; +import org.orekit.utils.PVCoordinates; +import org.orekit.utils.units.Unit; + +/** + * This class provides elements to propagate Intelsat's 11 elements. + *

          + * Intelsat's 11 elements propagation is defined in ITU-R S.1525 standard. + *

          + * + * @author Bryan Cazabonne + * @since 12.1 + */ +public class IntelsatElevenElementsPropagator extends AbstractAnalyticalPropagator { + + /** + * Intelsat's 11 elements. + */ + private final IntelsatElevenElements elements; + + /** + * Inertial frame for the output orbit. + */ + private final Frame inertialFrame; + + /** + * ECEF frame related to the Intelsat's 11 elements. + */ + private final Frame ecefFrame; + + /** + * Spacecraft mass in kilograms. + */ + private final double mass; + + /** + * Compute spacecraft's east longitude. + */ + private UnivariateDerivative2 eastLongitudeDegrees; + + /** + * Compute spacecraft's geocentric latitude. + */ + private UnivariateDerivative2 geocentricLatitudeDegrees; + + /** + * Compute spacecraft's orbit radius. + */ + private UnivariateDerivative2 orbitRadius; + + /** + * Default constructor. + *

          + * This constructor uses the {@link DataContext#getDefault() default data context}. + *

          + *

          The attitude provider is set by default to be aligned with the inertial frame.
          + * The mass is set by default to the + * {@link org.orekit.propagation.Propagator#DEFAULT_MASS DEFAULT_MASS}.
          + * The inertial frame is set by default to the + * {@link org.orekit.frames.Predefined#TOD_CONVENTIONS_2010_SIMPLE_EOP TOD frame} in the default data + * context.
          + * The ECEF frame is set by default to the + * {@link org.orekit.frames.Predefined#ITRF_CIO_CONV_2010_SIMPLE_EOP + * CIO/2010-based ITRF simple EOP} in the default data context. + *

          + * + * @param elements Intelsat's 11 elements + */ + @DefaultDataContext + public IntelsatElevenElementsPropagator(final IntelsatElevenElements elements) { + this(elements, FramesFactory.getTOD(IERSConventions.IERS_2010, true), FramesFactory.getITRF(IERSConventions.IERS_2010, true)); + } + + /** + * Constructor. + * + *

          The attitude provider is set by default to be aligned with the inertial frame.
          + * The mass is set by default to the + * {@link org.orekit.propagation.Propagator#DEFAULT_MASS DEFAULT_MASS}.
          + *

          + * + * @param elements Intelsat's 11 elements + * @param inertialFrame inertial frame for the output orbit + * @param ecefFrame ECEF frame related to the Intelsat's 11 elements + */ + public IntelsatElevenElementsPropagator(final IntelsatElevenElements elements, final Frame inertialFrame, final Frame ecefFrame) { + this(elements, inertialFrame, ecefFrame, FrameAlignedProvider.of(inertialFrame), Propagator.DEFAULT_MASS); + } + + /** + * Constructor. + * + * @param elements Intelsat's 11 elements + * @param inertialFrame inertial frame for the output orbit + * @param ecefFrame ECEF frame related to the Intelsat's 11 elements + * @param attitudeProvider attitude provider + * @param mass spacecraft mass + */ + public IntelsatElevenElementsPropagator(final IntelsatElevenElements elements, final Frame inertialFrame, final Frame ecefFrame, final AttitudeProvider attitudeProvider, + final double mass) { + super(attitudeProvider); + this.elements = elements; + this.inertialFrame = inertialFrame; + this.ecefFrame = ecefFrame; + this.mass = mass; + setStartDate(elements.getEpoch()); + final Orbit orbitAtElementsDate = propagateOrbit(elements.getEpoch()); + final Attitude attitude = attitudeProvider.getAttitude(orbitAtElementsDate, elements.getEpoch(), inertialFrame); + super.resetInitialState(new SpacecraftState(orbitAtElementsDate, attitude, mass)); + } + + /** + * Converts the Intelsat's 11 elements into Position/Velocity coordinates in ECEF. + * + * @param date computation epoch + * @return Position/Velocity coordinates in ECEF + */ + public PVCoordinates propagateInEcef(final AbsoluteDate date) { + final UnivariateDerivative2 tDays = new UnivariateDerivative2(date.durationFrom(elements.getEpoch()), 1.0, 0.0).divide(Constants.JULIAN_DAY); + final double wDegreesPerDay = elements.getLm1() + IntelsatElevenElements.DRIFT_RATE_SHIFT_DEG_PER_DAY; + final UnivariateDerivative2 wt = FastMath.toRadians(tDays.multiply(wDegreesPerDay)); + final FieldSinCos scWt = FastMath.sinCos(wt); + final FieldSinCos sc2Wt = FastMath.sinCos(wt.multiply(2.0)); + final UnivariateDerivative2 satelliteEastLongitudeDegrees = computeSatelliteEastLongitudeDegrees(tDays, scWt, sc2Wt); + final UnivariateDerivative2 satelliteGeocentricLatitudeDegrees = computeSatelliteGeocentricLatitudeDegrees(tDays, scWt); + final UnivariateDerivative2 satelliteRadius = computeSatelliteRadiusKilometers(wDegreesPerDay, scWt).multiply(Unit.KILOMETRE.getScale()); + this.eastLongitudeDegrees = satelliteEastLongitudeDegrees; + this.geocentricLatitudeDegrees = satelliteGeocentricLatitudeDegrees; + this.orbitRadius = satelliteRadius; + final FieldSinCos scLongitude = FastMath.sinCos(FastMath.toRadians(satelliteEastLongitudeDegrees)); + final FieldSinCos scLatitude = FastMath.sinCos(FastMath.toRadians(satelliteGeocentricLatitudeDegrees)); + final FieldVector3D positionWithDerivatives = new FieldVector3D<>(satelliteRadius.multiply(scLatitude.cos()).multiply(scLongitude.cos()), + satelliteRadius.multiply(scLatitude.cos()).multiply(scLongitude.sin()), + satelliteRadius.multiply(scLatitude.sin())); + return new PVCoordinates(new Vector3D(positionWithDerivatives.getX().getValue(), // + positionWithDerivatives.getY().getValue(), // + positionWithDerivatives.getZ().getValue()), // + new Vector3D(positionWithDerivatives.getX().getFirstDerivative(), // + positionWithDerivatives.getY().getFirstDerivative(), // + positionWithDerivatives.getZ().getFirstDerivative()), // + new Vector3D(positionWithDerivatives.getX().getSecondDerivative(), // + positionWithDerivatives.getY().getSecondDerivative(), // + positionWithDerivatives.getZ().getSecondDerivative())); + } + + /** + * {@inheritDoc}. + */ + @Override + public void resetInitialState(final SpacecraftState state) { + throw new OrekitException(OrekitMessages.NON_RESETABLE_STATE); + } + + /** + * {@inheritDoc}. + */ + @Override + protected double getMass(final AbsoluteDate date) { + return mass; + } + + /** + * {@inheritDoc}. + */ + @Override + protected void resetIntermediateState(final SpacecraftState state, final boolean forward) { + throw new OrekitException(OrekitMessages.NON_RESETABLE_STATE); + } + + /** + * {@inheritDoc}. + */ + @Override + protected Orbit propagateOrbit(final AbsoluteDate date) { + return new CartesianOrbit(ecefFrame.getTransformTo(inertialFrame, date).transformPVCoordinates(propagateInEcef(date)), inertialFrame, date, Constants.WGS84_EARTH_MU); + } + + /** + * Computes the satellite's east longitude. + * + * @param tDays delta time in days + * @param scW sin/cos of the W angle + * @param sc2W sin/cos of the 2xW angle + * @return the satellite's east longitude in degrees + */ + private UnivariateDerivative2 computeSatelliteEastLongitudeDegrees(final UnivariateDerivative2 tDays, final FieldSinCos scW, + final FieldSinCos sc2W) { + final UnivariateDerivative2 longitude = tDays.multiply(tDays).multiply(elements.getLm2()) // + .add(tDays.multiply(elements.getLm1())) // + .add(elements.getLm0()); + final UnivariateDerivative2 cosineLongitudeTerm = scW.cos().multiply(tDays.multiply(elements.getLonC1()).add(elements.getLonC())); + final UnivariateDerivative2 sineLongitudeTerm = scW.sin().multiply(tDays.multiply(elements.getLonS1()).add(elements.getLonS())); + final UnivariateDerivative2 latitudeTerm = sc2W.sin().multiply(0.5 * (elements.getLatC() * elements.getLatC() - elements.getLatS() * elements.getLatS())) // + .subtract(sc2W.cos().multiply(elements.getLatC() * elements.getLatS())) // + .multiply(IntelsatElevenElements.K); + return longitude.add(cosineLongitudeTerm).add(sineLongitudeTerm).add(latitudeTerm); + } + + /** + * Computes the satellite's geocentric latitude. + * + * @param tDays delta time in days + * @param scW sin/cos of the W angle + * @return he satellite geocentric latitude in degrees + */ + private UnivariateDerivative2 computeSatelliteGeocentricLatitudeDegrees(final UnivariateDerivative2 tDays, final FieldSinCos scW) { + final UnivariateDerivative2 cosineTerm = scW.cos().multiply(tDays.multiply(elements.getLatC1()).add(elements.getLatC())); + final UnivariateDerivative2 sineTerm = scW.sin().multiply(tDays.multiply(elements.getLatS1()).add(elements.getLatS())); + return cosineTerm.add(sineTerm); + } + + /** + * Computes the satellite's orbit radius. + * + * @param wDegreesPerDay W angle in degrees/day + * @param scW sin/cos of the W angle + * @return the satellite's orbit radius in kilometers + */ + private UnivariateDerivative2 computeSatelliteRadiusKilometers(final double wDegreesPerDay, final FieldSinCos scW) { + final double coefficient = IntelsatElevenElements.SYNCHRONOUS_RADIUS_KM * (1.0 - (2.0 * elements.getLm1()) / (3.0 * (wDegreesPerDay - elements.getLm1()))); + return scW.sin() + .multiply(IntelsatElevenElements.K * elements.getLonC()) + .add(1.0) + .subtract(scW.cos().multiply(IntelsatElevenElements.K * elements.getLonS())) + .multiply(coefficient); + } + + /** + * Get the computed satellite's east longitude. + * + * @return the satellite's east longitude in degrees + */ + public UnivariateDerivative2 getEastLongitudeDegrees() { + return eastLongitudeDegrees; + } + + /** + * Get the computed satellite's geocentric latitude. + * + * @return the satellite's geocentric latitude in degrees + */ + public UnivariateDerivative2 getGeocentricLatitudeDegrees() { + return geocentricLatitudeDegrees; + } + + /** + * Get the computed satellite's orbit. + * + * @return satellite's orbit radius in meters + */ + public UnivariateDerivative2 getOrbitRadius() { + return orbitRadius; + } + + /** + * {@inheritDoc}. + */ + @Override + public Frame getFrame() { + return inertialFrame; + } +} diff --git a/src/main/java/org/orekit/propagation/analytical/intelsat/package-info.java b/src/main/java/org/orekit/propagation/analytical/intelsat/package-info.java new file mode 100644 index 0000000000..016785392a --- /dev/null +++ b/src/main/java/org/orekit/propagation/analytical/intelsat/package-info.java @@ -0,0 +1,23 @@ +/* Copyright 2002-2024 Airbus Defence and Space + * 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. + * Airbus Defence and Space 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 to propagate Intelsat's 11 elements. + * + * @author Bryan Cazabonne + * @since 12.1 + */ +package org.orekit.propagation.analytical.intelsat; diff --git a/src/main/java/org/orekit/propagation/analytical/tle/FieldDeepSDP4.java b/src/main/java/org/orekit/propagation/analytical/tle/FieldDeepSDP4.java index cfa1577910..b62eec536a 100644 --- a/src/main/java/org/orekit/propagation/analytical/tle/FieldDeepSDP4.java +++ b/src/main/java/org/orekit/propagation/analytical/tle/FieldDeepSDP4.java @@ -222,12 +222,12 @@ protected void luniSolarTermsComputation() { zmos = MathUtils.normalizeAngle(6.2565837 + 0.017201977 * daysSince1900, pi.getReal()); // Do solar terms - savtsn = zero.add(1e20); + savtsn = zero.newInstance(1e20); - T zcosi = zero.add(0.91744867); - T zsini = zero.add(0.39785416); - T zsing = zero.add(-0.98088458); - T zcosg = zero.add(0.1945905); + T zcosi = zero.newInstance(0.91744867); + T zsini = zero.newInstance(0.39785416); + T zsing = zero.newInstance(-0.98088458); + T zcosg = zero.newInstance(0.1945905); T se = zero; T sgh = zero; @@ -258,9 +258,9 @@ protected void luniSolarTermsComputation() { final T x6 = a6.multiply(sing); final T x7 = a5.multiply(cosg); final T x8 = a6.multiply(cosg); - final T z31 = x1.multiply(x1).multiply(12).subtract(x3.multiply(x3).multiply(3)); + final T z31 = x1.square().multiply(12).subtract(x3.square().multiply(3)); final T z32 = x1.multiply(x2).multiply(24).subtract(x3.multiply(x4).multiply(6)); - final T z33 = x2.multiply(x2).multiply(12).subtract(x4.multiply(x4).multiply(3)); + final T z33 = x2.square().multiply(12).subtract(x4.square().multiply(3)); final T z11 = a1.multiply(-6).multiply(a5).add(e0sq.multiply(x1.multiply(x7).multiply(-24).add(x3.multiply(x5).multiply(-6)))); final T z12 = a1.multiply(a6).add(a3.multiply(a5)).multiply(-6).add( e0sq.multiply(x2.multiply(x7).add(x1.multiply(x8)).multiply(-24).add( @@ -280,9 +280,9 @@ protected void luniSolarTermsComputation() { final T s5 = x1.multiply(x3).add(x2.multiply(x4)); final T s6 = x2.multiply(x3).add(x1.multiply(x4)); final T s7 = x2.multiply(x4).subtract(x1.multiply(x3)); - T z1 = a1.multiply(a1).add(a2.multiply(a2)).multiply(3).add(z31.multiply(e0sq)); + T z1 = a1.square().add(a2.square()).multiply(3).add(z31.multiply(e0sq)); T z2 = a1.multiply(a3).add(a2.multiply(a4)).multiply(6).add(z32.multiply(e0sq)); - T z3 = a3.multiply(a3).add(a4.multiply(a4)).multiply(3).add(z33.multiply(e0sq)); + T z3 = a3.square().add(a4.square()).multiply(3).add(z33.multiply(e0sq)); z1 = z1.add(z1).add(beta02.multiply(z31)); z2 = z2.add(z2).add(beta02.multiply(z32)); @@ -328,10 +328,10 @@ protected void luniSolarTermsComputation() { sh3 = xh3; sl4 = xl4; sgh4 = xgh4; - zcosg = zero.add(zcosgl); - zsing = zero.add(zsingl); - zcosi = zero.add(zcosil); - zsini = zero.add(zsinil); + zcosg = zero.newInstance(zcosgl); + zsing = zero.newInstance(zsingl); + zcosi = zero.newInstance(zcosil); + zsini = zero.newInstance(zsinil); zcosh = cosq.multiply(zcoshl).add(sinq.multiply(zsinhl)); zsinh = sinq.multiply(zcoshl).subtract(cosq.multiply(zsinhl)); zn = TLEConstants.ZNL; @@ -520,7 +520,7 @@ protected void deepSecularEffects(final T t) { final T xldot = xni.add(xfact); - T xlpow = t.getField().getZero().add(1.); + T xlpow = t.getField().getOne(); xli = xli.add(xldot.multiply(delt)); xni = xni.add(derivs[0].multiply(delt)); double delt_factor = delt; diff --git a/src/main/java/org/orekit/propagation/analytical/tle/FieldSDP4.java b/src/main/java/org/orekit/propagation/analytical/tle/FieldSDP4.java index ce568c3501..256297bc2d 100644 --- a/src/main/java/org/orekit/propagation/analytical/tle/FieldSDP4.java +++ b/src/main/java/org/orekit/propagation/analytical/tle/FieldSDP4.java @@ -94,7 +94,7 @@ protected void sxpPropagate(final T tSince, final T[] parameters) { final T bStar = parameters[0]; omgadf = tle.getPerigeeArgument().add(omgdot.multiply(tSince)); final T xnoddf = tle.getRaan().add(xnodot.multiply(tSince)); - final T tSinceSq = tSince.multiply(tSince); + final T tSinceSq = tSince.square(); xnode = xnoddf.add(xnodcf.multiply(tSinceSq)); xn = xn0dp; diff --git a/src/main/java/org/orekit/propagation/analytical/tle/FieldSGP4.java b/src/main/java/org/orekit/propagation/analytical/tle/FieldSGP4.java index d8dd8761cc..dc2054b240 100644 --- a/src/main/java/org/orekit/propagation/analytical/tle/FieldSGP4.java +++ b/src/main/java/org/orekit/propagation/analytical/tle/FieldSGP4.java @@ -104,7 +104,7 @@ protected void sxpInitialize(final T[] parameters) { lessThan220 = perige.getReal() < 220; if (!lessThan220) { final FieldSinCos scM0 = FastMath.sinCos(tle.getMeanAnomaly()); - final T c1sq = c1.multiply(c1); + final T c1sq = c1.square(); delM0 = eta.multiply(scM0.cos()).add(1.0); delM0 = delM0.multiply(delM0).multiply(delM0); d2 = a0dp.multiply(tsi).multiply(c1sq).multiply(4.0); @@ -143,7 +143,7 @@ protected void sxpPropagate(final T tSince, final T[] parameters) { final T xn0ddf = tle.getRaan().add(xnodot.multiply(tSince)); omega = omgadf; T xmp = xmdf; - final T tsq = tSince.multiply(tSince); + final T tsq = tSince.square(); xnode = xn0ddf.add(xnodcf.multiply(tsq)); T tempa = c1.multiply(tSince).negate().add(1.0); T tempe = bStar.multiply(c4).multiply(tSince); @@ -152,7 +152,7 @@ protected void sxpPropagate(final T tSince, final T[] parameters) { if (!lessThan220) { final T delomg = omgcof.multiply(tSince); T delm = eta.multiply(FastMath.cos(xmdf)).add(1.0); - delm = xmcof.multiply(delm.multiply(delm).multiply(delm).subtract(delM0)); + delm = xmcof.multiply(delm.square().multiply(delm).subtract(delM0)); final T temp = delomg.add(delm); xmp = xmdf.add(temp); omega = omgadf.subtract(temp); @@ -168,7 +168,7 @@ protected void sxpPropagate(final T tSince, final T[] parameters) { // A highly arbitrary lower limit on e, of 1e-6: if (e.getReal() < 1e-6) { - e = e.getField().getZero().add(1e-6); + e = e.getField().getZero().newInstance(1e-6); } xl = xmp.add(omega).add(xnode).add(xn0dp.multiply(templ)); diff --git a/src/main/java/org/orekit/propagation/analytical/tle/FieldTLE.java b/src/main/java/org/orekit/propagation/analytical/tle/FieldTLE.java index 7aec1de6f3..c07c8a8a45 100644 --- a/src/main/java/org/orekit/propagation/analytical/tle/FieldTLE.java +++ b/src/main/java/org/orekit/propagation/analytical/tle/FieldTLE.java @@ -239,11 +239,11 @@ public FieldTLE(final Field field, final String line1, final String line2, fi line1.substring(45, 50) + 'e' + line1.substring(50, 52)).replace(' ', '0'))).divide(5.3747712e13); - eccentricity = zero.add(Double.parseDouble("." + line2.substring(26, 33).replace(' ', '0'))); - inclination = zero.add(FastMath.toRadians(ParseUtils.parseDouble(line2, 8, 8))); - pa = zero.add(FastMath.toRadians(ParseUtils.parseDouble(line2, 34, 8))); - raan = zero.add(FastMath.toRadians(Double.parseDouble(line2.substring(17, 25).replace(' ', '0')))); - meanAnomaly = zero.add(FastMath.toRadians(ParseUtils.parseDouble(line2, 43, 8))); + eccentricity = zero.newInstance(Double.parseDouble("." + line2.substring(26, 33).replace(' ', '0'))); + inclination = zero.newInstance(FastMath.toRadians(ParseUtils.parseDouble(line2, 8, 8))); + pa = zero.newInstance(FastMath.toRadians(ParseUtils.parseDouble(line2, 34, 8))); + raan = zero.newInstance(FastMath.toRadians(Double.parseDouble(line2.substring(17, 25).replace(' ', '0')))); + meanAnomaly = zero.newInstance(FastMath.toRadians(ParseUtils.parseDouble(line2, 43, 8))); revolutionNumberAtEpoch = ParseUtils.parseInteger(line2, 63, 5); final double bStarValue = Double.parseDouble((line1.substring(53, 54) + '.' + diff --git a/src/main/java/org/orekit/propagation/analytical/tle/FieldTLEPropagator.java b/src/main/java/org/orekit/propagation/analytical/tle/FieldTLEPropagator.java index 68fe674fb7..0ba4ce2479 100644 --- a/src/main/java/org/orekit/propagation/analytical/tle/FieldTLEPropagator.java +++ b/src/main/java/org/orekit/propagation/analytical/tle/FieldTLEPropagator.java @@ -17,7 +17,9 @@ package org.orekit.propagation.analytical.tle; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.hipparchus.CalculusFieldElement; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; @@ -41,6 +43,7 @@ import org.orekit.utils.FieldPVCoordinates; import org.orekit.utils.PVCoordinates; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TimeSpanMap; /** This class provides elements to propagate TLE's. @@ -175,8 +178,11 @@ public abstract class FieldTLEPropagator> exte /** TLE frame. */ private final Frame teme; - /** Spacecraft mass (kg). */ - private final T mass; + /** Spacecraft masses (kg) mapped to TLEs. */ + private Map, T> masses; + + /** All TLEs. */ + private TimeSpanMap> tles; /** Protected constructor for derived classes. * @@ -189,12 +195,9 @@ public abstract class FieldTLEPropagator> exte * @see #FieldTLEPropagator(FieldTLE, AttitudeProvider, CalculusFieldElement, Frame, CalculusFieldElement[]) */ @DefaultDataContext - protected FieldTLEPropagator(final FieldTLE initialTLE, - final AttitudeProvider attitudeProvider, - final T mass, - final T[] parameters) { - this(initialTLE, attitudeProvider, mass, - DataContext.getDefault().getFrames().getTEME(), parameters); + protected FieldTLEPropagator(final FieldTLE initialTLE, final AttitudeProvider attitudeProvider, final T mass, + final T[] parameters) { + this(initialTLE, attitudeProvider, mass, DataContext.getDefault().getFrames().getTEME(), parameters); } /** Protected constructor for derived classes. @@ -204,17 +207,16 @@ protected FieldTLEPropagator(final FieldTLE initialTLE, * @param teme the TEME frame to use for propagation. * @param parameters SGP4 and SDP4 model parameters */ - protected FieldTLEPropagator(final FieldTLE initialTLE, - final AttitudeProvider attitudeProvider, - final T mass, - final Frame teme, - final T[] parameters) { + protected FieldTLEPropagator(final FieldTLE initialTLE, final AttitudeProvider attitudeProvider, final T mass, + final Frame teme, final T[] parameters) { super(initialTLE.getE().getField(), attitudeProvider); setStartDate(initialTLE.getDate()); - this.tle = initialTLE; - this.teme = teme; - this.mass = mass; - this.utc = initialTLE.getUtc(); + this.utc = initialTLE.getUtc(); + initializeTle(initialTLE); + this.teme = teme; + this.tles = new TimeSpanMap<>(tle); + this.masses = new HashMap<>(); + this.masses.put(tle, mass); initializeCommons(parameters); sxpInitialize(parameters); @@ -253,7 +255,7 @@ public static > FieldTLEPropagator selectEx return selectExtrapolator( tle, FrameAlignedProvider.of(teme), - tle.getE().getField().getZero().add(DEFAULT_MASS), + tle.getE().getField().getZero().newInstance(DEFAULT_MASS), teme, parameters); } @@ -341,13 +343,13 @@ public FieldPVCoordinates getPVCoordinates(final FieldAbsoluteDate date, f */ private void initializeCommons(final T[] parameters) { - final T zero = mass.getField().getZero(); + final T zero = tle.getDate().getField().getZero(); final T bStar = parameters[0]; final T a1 = tle.getMeanMotion().multiply(60.0).reciprocal().multiply(TLEConstants.XKE).pow(TLEConstants.TWO_THIRD); cosi0 = FastMath.cos(tle.getI()); theta2 = cosi0.multiply(cosi0); final T x3thm1 = theta2.multiply(3.0).subtract(1.0); - e0sq = tle.getE().multiply(tle.getE()); + e0sq = tle.getE().square(); beta02 = e0sq.negate().add(1.0); beta0 = FastMath.sqrt(beta02); final T tval = x3thm1.multiply(1.5 * TLEConstants.CK2).divide(beta0.multiply(beta02)); @@ -361,8 +363,8 @@ private void initializeCommons(final T[] parameters) { a0dp = a0.divide(delta0.negate().add(1.0)); // Values of s and qms2t : - s4 = zero.add(TLEConstants.S); // unmodified value for s - T q0ms24 = zero.add(TLEConstants.QOMS2T); // unmodified value for q0ms2T + s4 = zero.newInstance(TLEConstants.S); // unmodified value for s + T q0ms24 = zero.newInstance(TLEConstants.QOMS2T); // unmodified value for q0ms2T perige = a0dp.multiply(tle.getE().negate().add(1.0)).subtract(TLEConstants.NORMALIZED_EQUATORIAL_RADIUS).multiply( TLEConstants.EARTH_RADIUS); // perige @@ -370,26 +372,26 @@ private void initializeCommons(final T[] parameters) { // For perigee below 156 km, the values of s and qoms2t are changed : if (perige.getReal() < 156.0) { if (perige.getReal() <= 98.0) { - s4 = zero.add(20.0); + s4 = zero.newInstance(20.0); } else { s4 = perige.subtract(78.0); } final T temp_val = s4.negate().add(120.0).multiply(TLEConstants.NORMALIZED_EQUATORIAL_RADIUS / TLEConstants.EARTH_RADIUS); final T temp_val_squared = temp_val.multiply(temp_val); - q0ms24 = temp_val_squared.multiply(temp_val_squared); + q0ms24 = temp_val_squared.square(); s4 = s4.divide(TLEConstants.EARTH_RADIUS).add(TLEConstants.NORMALIZED_EQUATORIAL_RADIUS); // new value for q0ms2T and s } final T pinv = a0dp.multiply(beta02).reciprocal(); - final T pinvsq = pinv.multiply(pinv); + final T pinvsq = pinv.square(); tsi = a0dp.subtract(s4).reciprocal(); eta = a0dp.multiply(tle.getE()).multiply(tsi); - etasq = eta.multiply(eta); + etasq = eta.square(); eeta = tle.getE().multiply(eta); final T psisq = etasq.negate().add(1.0).abs(); // abs because pow 3.5 needs positive value final T tsi_squared = tsi.multiply(tsi); - coef = q0ms24.multiply(tsi_squared.multiply(tsi_squared)); + coef = q0ms24.multiply(tsi_squared.square()); coef1 = coef.divide(psisq.pow(3.5)); // C2 and C1 coefficients computation : @@ -441,7 +443,7 @@ private void initializeCommons(final T[] parameters) { */ private FieldPVCoordinates computePVCoordinates() { - final T zero = mass.getField().getZero(); + final T zero = tle.getDate().getField().getZero(); // Long period periodics final T axn = e.multiply(FastMath.cos(omega)); T temp = a.multiply(e.multiply(e).negate().add(1.0)).reciprocal(); @@ -452,7 +454,7 @@ private FieldPVCoordinates computePVCoordinates() { final T aynl = temp.multiply(aycof); final T xlt = xl.add(xll); final T ayn = e.multiply(FastMath.sin(omega)).add(aynl); - final T elsq = axn.multiply(axn).add(ayn.multiply(ayn)); + final T elsq = axn.square().add(ayn.square()); final T capu = MathUtils.normalizeAngle(xlt.subtract(xnode), zero.getPi()); T epw = capu; T ecosE = zero; @@ -461,7 +463,7 @@ private FieldPVCoordinates computePVCoordinates() { T cosEPW = zero; // Dundee changes: items dependent on cosio get recomputed: - final T cosi0Sq = cosi0.multiply(cosi0); + final T cosi0Sq = cosi0.square(); final T x3thm1 = cosi0Sq.multiply(3.0).subtract(1.0); final T x1mth2 = cosi0Sq.negate().add(1.0); final T x7thm1 = cosi0Sq.multiply(7.0).subtract(1.0); @@ -542,9 +544,10 @@ private FieldPVCoordinates computePVCoordinates() { final T cr = rk.multiply(1000 * TLEConstants.EARTH_RADIUS); final FieldVector3D pos = new FieldVector3D<>(cr.multiply(ux), cr.multiply(uy), cr.multiply(uz)); - final T rdot = FastMath.sqrt(a).multiply(esinE.divide(r)).multiply(TLEConstants.XKE); + final T sqrtA = FastMath.sqrt(a); + final T rdot = sqrtA.multiply(esinE.divide(r)).multiply(TLEConstants.XKE); final T rfdot = FastMath.sqrt(pl).divide(r).multiply(TLEConstants.XKE); - final T xn = a.multiply(FastMath.sqrt(a)).reciprocal().multiply(TLEConstants.XKE); + final T xn = a.multiply(sqrtA).reciprocal().multiply(TLEConstants.XKE); final T rdotk = rdot.subtract(xn.multiply(temp1).multiply(x1mth2).multiply(sin2u)); final T rfdotk = rfdot.add(xn.multiply(temp1).multiply(x1mth2.multiply(cos2u).add(x3thm1.multiply(1.5)))); final T vx = xmx.multiply(cosuk).subtract(cosnok.multiply(sinuk)); @@ -555,7 +558,7 @@ private FieldPVCoordinates computePVCoordinates() { final FieldVector3D vel = new FieldVector3D<>(rdotk.multiply(ux).add(rfdotk.multiply(vx)).multiply(cv), rdotk.multiply(uy).add(rfdotk.multiply(vy)).multiply(cv), rdotk.multiply(uz).add(rfdotk.multiply(vz)).multiply(cv)); - return new FieldPVCoordinates(pos, vel); + return new FieldPVCoordinates<>(pos, vel); } @@ -586,30 +589,61 @@ public List getParametersDrivers() { */ public void resetInitialState(final FieldSpacecraftState state) { super.resetInitialState(state); - super.setStartDate(state.getDate()); - final TleGenerationAlgorithm algorithm = TLEPropagator.getDefaultTleGenerationAlgorithm(utc, teme); - final FieldTLE newTLE = algorithm.generate(state, tle); - this.tle = newTLE; - initializeCommons(tle.getParameters(state.getDate().getField())); - sxpInitialize(tle.getParameters(state.getDate().getField())); + resetTle(state); + masses = new HashMap<>(); + masses.put(tle, state.getMass()); + tles = new TimeSpanMap<>(tle); } /** {@inheritDoc} */ protected void resetIntermediateState(final FieldSpacecraftState state, final boolean forward) { - throw new OrekitException(OrekitMessages.NON_RESETABLE_STATE); + resetTle(state); + if (forward) { + tles.addValidAfter(tle, state.getDate().toAbsoluteDate(), false); + } else { + tles.addValidBefore(tle, state.getDate().toAbsoluteDate(), false); + } + stateChanged(state); + masses.put(tle, state.getMass()); + } + + /** Reset internal TLE from a SpacecraftState. + * @param state spacecraft state on which to base new TLE + */ + private void resetTle(final FieldSpacecraftState state) { + final TleGenerationAlgorithm algorithm = TLEPropagator.getDefaultTleGenerationAlgorithm(utc, teme); + final FieldTLE newTle = algorithm.generate(state, tle); + initializeTle(newTle); + } + + /** Initialize internal TLE. + * @param newTle tle to replace current one + */ + private void initializeTle(final FieldTLE newTle) { + tle = newTle; + final T[] parameters = tle.getParameters(tle.getDate().getField()); + initializeCommons(parameters); + sxpInitialize(parameters); } /** {@inheritDoc} */ protected T getMass(final FieldAbsoluteDate date) { - return mass; + return masses.get(tles.get(date.toAbsoluteDate())); } /** {@inheritDoc} */ public FieldOrbit propagateOrbit(final FieldAbsoluteDate date, final T[] parameters) { - return new FieldCartesianOrbit<>(getPVCoordinates(date, parameters), teme, date, date.getField().getZero().add(TLEConstants.MU)); + final FieldTLE closestTle = tles.get(date.toAbsoluteDate()); + if (!tle.equals(closestTle)) { + initializeTle(closestTle); + } + final T mu = date.getField().getZero().newInstance(TLEConstants.MU); + return new FieldCartesianOrbit<>(getPVCoordinates(date, parameters), teme, date, mu); } /** Get the underlying TLE. + * If there has been calls to #resetInitialState or #resetIntermediateState, + * it will not be the same as given to the constructor. * @return underlying TLE */ public FieldTLE getTLE() { 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 0ebc77a661..38918bf67b 100644 --- a/src/main/java/org/orekit/propagation/analytical/tle/TLEPropagator.java +++ b/src/main/java/org/orekit/propagation/analytical/tle/TLEPropagator.java @@ -18,7 +18,9 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.linear.RealMatrix; @@ -46,6 +48,7 @@ import org.orekit.utils.DoubleArrayDictionary; import org.orekit.utils.PVCoordinates; import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TimeSpanMap; import org.orekit.utils.TimeSpanMap.Span; @@ -178,8 +181,11 @@ public abstract class TLEPropagator extends AbstractAnalyticalPropagator { /** TLE frame. */ private final Frame teme; - /** Spacecraft mass (kg). */ - private final double mass; + /** Spacecraft masses (kg) mapped to TLEs. */ + private Map masses; + + /** All TLEs. */ + private TimeSpanMap tles; /** Protected constructor for derived classes. * @@ -210,13 +216,13 @@ protected TLEPropagator(final TLE initialTLE, final Frame teme) { super(attitudeProvider); setStartDate(initialTLE.getDate()); - this.tle = initialTLE; - this.teme = teme; - this.mass = mass; this.utc = initialTLE.getUtc(); + initializeTle(initialTLE); + this.teme = teme; + this.tles = new TimeSpanMap<>(tle); + this.masses = new HashMap<>(); + this.masses.put(tle, mass); - initializeCommons(); - sxpInitialize(); // set the initial state final Orbit orbit = propagateOrbit(initialTLE.getDate()); final Attitude attitude = attitudeProvider.getAttitude(orbit, orbit.getDate(), orbit.getFrame()); @@ -558,30 +564,59 @@ private PVCoordinates computePVCoordinates() { */ public void resetInitialState(final SpacecraftState state) { super.resetInitialState(state); - super.setStartDate(state.getDate()); - final TleGenerationAlgorithm algorithm = getDefaultTleGenerationAlgorithm(utc, teme); - final TLE newTLE = algorithm.generate(state, tle); - this.tle = newTLE; - initializeCommons(); - sxpInitialize(); + resetTle(state); + masses = new HashMap<>(); + masses.put(tle, state.getMass()); + tles = new TimeSpanMap<>(tle); } /** {@inheritDoc} */ protected void resetIntermediateState(final SpacecraftState state, final boolean forward) { - throw new OrekitException(OrekitMessages.NON_RESETABLE_STATE); + resetTle(state); + if (forward) { + tles.addValidAfter(tle, state.getDate(), false); + } else { + tles.addValidBefore(tle, state.getDate(), false); + } + stateChanged(state); + masses.put(tle, state.getMass()); + } + + /** Reset internal TLE from a SpacecraftState. + * @param state spacecraft state on which to base new TLE + */ + private void resetTle(final SpacecraftState state) { + final TleGenerationAlgorithm algorithm = getDefaultTleGenerationAlgorithm(utc, teme); + final TLE newTle = algorithm.generate(state, tle); + initializeTle(newTle); + } + + /** Initialize internal TLE. + * @param newTle tle to replace current one + */ + private void initializeTle(final TLE newTle) { + tle = newTle; + initializeCommons(); + sxpInitialize(); } /** {@inheritDoc} */ protected double getMass(final AbsoluteDate date) { - return mass; + return masses.get(tles.get(date)); } /** {@inheritDoc} */ protected Orbit propagateOrbit(final AbsoluteDate date) { + final TLE closestTle = tles.get(date); + if (!tle.equals(closestTle)) { + initializeTle(closestTle); + } return new CartesianOrbit(getPVCoordinates(date), teme, date, TLEConstants.MU); } /** Get the underlying TLE. + * If there has been calls to #resetInitialState or #resetIntermediateState, + * it will not be the same as given to the constructor. * @return underlying TLE */ public TLE getTLE() { diff --git a/src/main/java/org/orekit/propagation/analytical/tle/generation/FixedPointTleGenerationAlgorithm.java b/src/main/java/org/orekit/propagation/analytical/tle/generation/FixedPointTleGenerationAlgorithm.java index 8f46788e5c..1c3b682377 100644 --- a/src/main/java/org/orekit/propagation/analytical/tle/generation/FixedPointTleGenerationAlgorithm.java +++ b/src/main/java/org/orekit/propagation/analytical/tle/generation/FixedPointTleGenerationAlgorithm.java @@ -40,6 +40,7 @@ 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.time.TimeScale; @@ -137,7 +138,7 @@ public TLE generate(final SpacecraftState state, final TLE templateTLE) { // Generation epoch final AbsoluteDate epoch = state.getDate(); - // gets equinoctial parameters in TEME frame from state + // gets equinoctial parameters in TEME frame and with TLE gravity parameter from state final EquinoctialOrbit equinoctialOrbit = convert(state.getOrbit()); double sma = equinoctialOrbit.getA(); double ex = equinoctialOrbit.getEquinoctialEx(); @@ -221,7 +222,7 @@ public TLE generate(final SpacecraftState state, final TLE templateTLE) { public > FieldTLE generate(final FieldSpacecraftState state, final FieldTLE templateTLE) { - // gets equinoctial parameters in TEME frame from state + // gets equinoctial parameters in TEME frame and with TLE gravity parameter from state final FieldEquinoctialOrbit equinoctialOrbit = convert(state.getOrbit()); T sma = equinoctialOrbit.getA(); T ex = equinoctialOrbit.getEquinoctialEx(); @@ -231,7 +232,7 @@ public > FieldTLE generate(final FieldSpace T lv = equinoctialOrbit.getLv(); // rough initialization of the TLE - final T bStar = state.getA().getField().getZero().add(templateTLE.getBStar()); + final T bStar = state.getA().getField().getZero().newInstance(templateTLE.getBStar()); final FieldKeplerianOrbit keplerianOrbit = (FieldKeplerianOrbit) OrbitType.KEPLERIAN.convertType(equinoctialOrbit); FieldTLE current = TleGenerationUtil.newTLE(keplerianOrbit, templateTLE, bStar, utc); @@ -296,22 +297,22 @@ public > FieldTLE generate(final FieldSpace } /** - * Converts an orbit into an equinoctial orbit expressed in TEME frame. + * Converts an orbit into an equinoctial orbit expressed in TEME frame with the TLE gravity parameter. * @param orbitIn the orbit to convert * @return the converted orbit, i.e. equinoctial in TEME frame */ private EquinoctialOrbit convert(final Orbit orbitIn) { - return new EquinoctialOrbit(orbitIn.getPVCoordinates(teme), teme, orbitIn.getMu()); + return new EquinoctialOrbit(orbitIn.getPVCoordinates(teme), teme, TLEConstants.MU); } /** - * Converts an orbit into an equinoctial orbit expressed in TEME frame. + * Converts an orbit into an equinoctial orbit expressed in TEME frame with the TLE gravity parameter. * @param orbitIn the orbit to convert * @param type of the element * @return the converted orbit, i.e. equinoctial in TEME frame */ private > FieldEquinoctialOrbit convert(final FieldOrbit orbitIn) { - return new FieldEquinoctialOrbit(orbitIn.getPVCoordinates(teme), teme, orbitIn.getMu()); + return new FieldEquinoctialOrbit(orbitIn.getPVCoordinates(teme), teme, orbitIn.getMu().newInstance(TLEConstants.MU)); } } diff --git a/src/main/java/org/orekit/propagation/analytical/tle/generation/LeastSquaresTleGenerationAlgorithm.java b/src/main/java/org/orekit/propagation/analytical/tle/generation/LeastSquaresTleGenerationAlgorithm.java index b6e8be16f2..44fd9e1ceb 100644 --- a/src/main/java/org/orekit/propagation/analytical/tle/generation/LeastSquaresTleGenerationAlgorithm.java +++ b/src/main/java/org/orekit/propagation/analytical/tle/generation/LeastSquaresTleGenerationAlgorithm.java @@ -290,7 +290,7 @@ private FieldVector meanStateToPV(final FieldVector state) { final FieldVector3D position = new FieldVector3D<>(state.getSubVector(0, 3).toArray()); final FieldVector3D velocity = new FieldVector3D<>(state.getSubVector(3, 3).toArray()); final FieldPVCoordinates pvCoordinates = new FieldPVCoordinates<>(position, velocity); - final FieldKeplerianOrbit orbit = new FieldKeplerianOrbit<>(pvCoordinates, teme, epoch, field.getZero().add(TLEPropagator.getMU())); + final FieldKeplerianOrbit orbit = new FieldKeplerianOrbit<>(pvCoordinates, teme, epoch, field.getZero().newInstance(TLEPropagator.getMU())); // Convert to TLE final FieldTLE tle = TleGenerationUtil.newTLE(orbit, templateTLE, bStar[0], utc); diff --git a/src/main/java/org/orekit/propagation/conversion/AbstractFixedStepFieldIntegratorBuilder.java b/src/main/java/org/orekit/propagation/conversion/AbstractFixedStepFieldIntegratorBuilder.java index 7efa223dcc..ffdc2e9888 100644 --- a/src/main/java/org/orekit/propagation/conversion/AbstractFixedStepFieldIntegratorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/AbstractFixedStepFieldIntegratorBuilder.java @@ -83,6 +83,6 @@ protected void checkStep(final double stepToCheck) { * @return "fielded" step size (s) */ protected T getFieldStep(final Field field) { - return fieldStep != null ? fieldStep : field.getOne().multiply(step); + return fieldStep != null ? fieldStep : field.getZero().newInstance(step); } } diff --git a/src/main/java/org/orekit/propagation/conversion/DSSTPropagatorBuilder.java b/src/main/java/org/orekit/propagation/conversion/DSSTPropagatorBuilder.java index 6a785269e0..fa665389ee 100644 --- a/src/main/java/org/orekit/propagation/conversion/DSSTPropagatorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/DSSTPropagatorBuilder.java @@ -16,9 +16,6 @@ */ package org.orekit.propagation.conversion; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; import org.orekit.attitudes.Attitude; import org.orekit.attitudes.AttitudeProvider; import org.orekit.attitudes.FrameAlignedProvider; @@ -39,6 +36,10 @@ import org.orekit.utils.ParameterDriver; import org.orekit.utils.ParameterDriversList; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + /** Builder for DSST propagator. * @author Bryan Cazabonne * @since 10.0 @@ -115,7 +116,7 @@ public DSSTPropagatorBuilder(final Orbit referenceOrbit, final AttitudeProvider attitudeProvider) { super(referenceOrbit, PositionAngleType.MEAN, positionScale, true, attitudeProvider); this.builder = builder; - this.forceModels = new ArrayList(); + this.forceModels = new ArrayList<>(); this.mass = Propagator.DEFAULT_MASS; this.propagationType = propagationType; this.stateType = stateType; diff --git a/src/main/java/org/orekit/propagation/conversion/EphemerisPropagatorBuilder.java b/src/main/java/org/orekit/propagation/conversion/EphemerisPropagatorBuilder.java index 3642442ff4..3d7e64f9db 100644 --- a/src/main/java/org/orekit/propagation/conversion/EphemerisPropagatorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/EphemerisPropagatorBuilder.java @@ -26,6 +26,7 @@ import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.Propagator; import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.SpacecraftStateInterpolator; import org.orekit.propagation.StateCovariance; import org.orekit.propagation.analytical.Ephemeris; import org.orekit.time.TimeInterpolator; @@ -34,7 +35,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.Optional; /** * Builder for Ephemeris propagator. @@ -52,13 +52,13 @@ public class EphemerisPropagatorBuilder extends AbstractPropagatorBuilder { private final List states; /** List of covariances. **/ - private final Optional> covariances; + private final List covariances; /** Spacecraft state interpolator. */ private final TimeInterpolator stateInterpolator; /** State covariance interpolator. */ - private final Optional>> covarianceInterpolator; + private final TimeInterpolator> covarianceInterpolator; /** Attitude provider. */ private final AttitudeProvider provider; @@ -90,6 +90,24 @@ public EphemerisPropagatorBuilder(final List states, this(states, stateInterpolator, new ArrayList<>(), null, attitudeProvider); } + /** + * Constructor. + * + * @param states list of spacecraft states + * @param interpolationPoints number of interpolation points + * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail + * @param attitudeProvider attitude law to use + */ + public EphemerisPropagatorBuilder(final List states, + final int interpolationPoints, + final double extrapolationThreshold, + final AttitudeProvider attitudeProvider) { + this(states, + new SpacecraftStateInterpolator(interpolationPoints, extrapolationThreshold, + states.get(0).getFrame(), states.get(0).getFrame()), + attitudeProvider); + } + /** * Constructor with covariances and default attitude provider. *

          @@ -134,24 +152,22 @@ public EphemerisPropagatorBuilder(final List states, this.states = states; this.stateInterpolator = stateInterpolator; - this.covariances = Optional.ofNullable(covariances); - this.covarianceInterpolator = Optional.ofNullable(covarianceInterpolator); + this.covariances = covariances == null ? new ArrayList<>() : covariances; + this.covarianceInterpolator = covarianceInterpolator; this.provider = attitudeProvider; } /** {@inheritDoc} */ @Override public EphemerisPropagatorBuilder copy() { - return new EphemerisPropagatorBuilder(states, stateInterpolator, - covariances.orElse(null), covarianceInterpolator.orElse(null), - provider); + return new EphemerisPropagatorBuilder(states, stateInterpolator, covariances, covarianceInterpolator, provider); } /** {@inheritDoc}. */ @Override public Propagator buildPropagator(final double[] normalizedParameters) { - if (covariances.isPresent() && covarianceInterpolator.isPresent()) { - return new Ephemeris(states, stateInterpolator, covariances.get(), covarianceInterpolator.get(), provider); + if (!covariances.isEmpty() && covarianceInterpolator != null) { + return new Ephemeris(states, stateInterpolator, covariances, covarianceInterpolator, provider); } return new Ephemeris(states, stateInterpolator, provider); diff --git a/src/main/java/org/orekit/propagation/conversion/PropagatorBuilder.java b/src/main/java/org/orekit/propagation/conversion/PropagatorBuilder.java index 456f2f3c6d..ee51c8d98e 100644 --- a/src/main/java/org/orekit/propagation/conversion/PropagatorBuilder.java +++ b/src/main/java/org/orekit/propagation/conversion/PropagatorBuilder.java @@ -46,6 +46,13 @@ public interface PropagatorBuilder { */ Propagator buildPropagator(double[] normalizedParameters); + /** Build a propagator from current value of selected normalized parameters. + * @return an initialized propagator + */ + default Propagator buildPropagator() { + return buildPropagator(getSelectedNormalizedParameters()); + } + /** Build a new batch least squares model. * @param builders builders to use for propagation * @param measurements measurements diff --git a/src/main/java/org/orekit/propagation/conversion/averaging/AbstractAveragedOrbitalState.java b/src/main/java/org/orekit/propagation/conversion/averaging/AbstractAveragedOrbitalState.java new file mode 100644 index 0000000000..1218cf57a6 --- /dev/null +++ b/src/main/java/org/orekit/propagation/conversion/averaging/AbstractAveragedOrbitalState.java @@ -0,0 +1,57 @@ +/* Copyright 2020-2024 Exotrail + * 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.averaging; + +import org.orekit.frames.Frame; +import org.orekit.time.AbsoluteDate; + +/** + * Abstract class representing averaged orbital state. It is used to define the frame and the date. + * + * @author Romain Serra + * @see AveragedOrbitalState + * @since 12.1 + */ +public abstract class AbstractAveragedOrbitalState implements AveragedOrbitalState { + + /** Reference (inertial) frame. */ + private final Frame frame; + /** Orbit epoch. */ + private final AbsoluteDate date; + + /** + * Protected constructor. + * @param date epoch + * @param frame reference frame + */ + protected AbstractAveragedOrbitalState(final AbsoluteDate date, final Frame frame) { + this.date = date; + this.frame = frame; + } + + /** {@inheritDoc} */ + @Override + public Frame getFrame() { + return frame; + } + + /** {@inheritDoc} */ + @Override + public AbsoluteDate getDate() { + return date; + } +} diff --git a/src/main/java/org/orekit/propagation/conversion/averaging/AbstractHarmonicsBasedOrbitalState.java b/src/main/java/org/orekit/propagation/conversion/averaging/AbstractHarmonicsBasedOrbitalState.java new file mode 100644 index 0000000000..ffdf35dc1f --- /dev/null +++ b/src/main/java/org/orekit/propagation/conversion/averaging/AbstractHarmonicsBasedOrbitalState.java @@ -0,0 +1,60 @@ +/* Copyright 2020-2024 Exotrail + * 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.averaging; + +import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; +import org.orekit.frames.Frame; +import org.orekit.time.AbsoluteDate; + +/** + * Abstract class representing averaged orbital state based on {@link UnnormalizedSphericalHarmonicsProvider}. + * + * @author Romain Serra + * @see AveragedOrbitalState + * @since 12.1 + */ +abstract class AbstractHarmonicsBasedOrbitalState extends AbstractAveragedOrbitalState { + + /** Spherical harmonics provider. */ + private final UnnormalizedSphericalHarmonicsProvider harmonicsProvider; + + /** + * Protected constructor. + * @param date epoch + * @param frame reference frame + * @param harmonicsProvider spherical harmonics provider + */ + protected AbstractHarmonicsBasedOrbitalState(final AbsoluteDate date, final Frame frame, + final UnnormalizedSphericalHarmonicsProvider harmonicsProvider) { + super(date, frame); + this.harmonicsProvider = harmonicsProvider; + } + + /** {@inheritDoc} */ + @Override + public double getMu() { + return harmonicsProvider.getMu(); + } + + /** + * Getter for spherical harmonics provider. + * @return harmonics provider + */ + public UnnormalizedSphericalHarmonicsProvider getHarmonicsProvider() { + return harmonicsProvider; + } +} diff --git a/src/main/java/org/orekit/propagation/conversion/averaging/AveragedOrbitalState.java b/src/main/java/org/orekit/propagation/conversion/averaging/AveragedOrbitalState.java new file mode 100644 index 0000000000..87270fedd3 --- /dev/null +++ b/src/main/java/org/orekit/propagation/conversion/averaging/AveragedOrbitalState.java @@ -0,0 +1,78 @@ +/* Copyright 2020-2024 Exotrail + * 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.averaging; + +import org.orekit.frames.Frame; +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.propagation.conversion.averaging.elements.AveragedOrbitalElements; + +/** + * Interface representing averaged orbital elements at a specific instant. + * Inheritors shall implement a conversion method to transform into an osculating {@link Orbit}. + * + * @author Romain Serra + * @see TimeStamped + * @see AveragedOrbitalElements + * @since 12.1 + */ +public interface AveragedOrbitalState extends TimeStamped { + + /** {@inheritDoc} */ + @Override + AbsoluteDate getDate(); + + /** + * Getter for the averaged orbital elements. + * @return averaged elements + */ + AveragedOrbitalElements getAveragedElements(); + + /** + * Getter for the central body's gravitational constant. + * @return gravitational constant + */ + double getMu(); + + /** + * Getter for the reference frame. + * @return frame + */ + Frame getFrame(); + + /** + * Getter for the averaged orbit type. + * @return orbit type + */ + OrbitType getOrbitType(); + + /** + * Getter for the averaged position angle. + * @return position angle type + */ + PositionAngleType getPositionAngleType(); + + /** + * Convert instance to an osculating orbit. + * @return osculating orbit + */ + Orbit toOsculatingOrbit(); + +} diff --git a/src/main/java/org/orekit/propagation/conversion/averaging/BrouwerLyddaneOrbitalState.java b/src/main/java/org/orekit/propagation/conversion/averaging/BrouwerLyddaneOrbitalState.java new file mode 100644 index 0000000000..081d55cf00 --- /dev/null +++ b/src/main/java/org/orekit/propagation/conversion/averaging/BrouwerLyddaneOrbitalState.java @@ -0,0 +1,109 @@ +/* Copyright 2020-2024 Exotrail + * 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.averaging; + +import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; +import org.orekit.frames.Frame; +import org.orekit.orbits.KeplerianOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; +import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.PropagationType; +import org.orekit.propagation.analytical.BrouwerLyddanePropagator; +import org.orekit.time.AbsoluteDate; +import org.orekit.propagation.conversion.averaging.elements.AveragedKeplerianWithMeanAngle; + +/** + * Class representing an averaged orbital state as in the Brouwer-Lyddane theory. + * + * @author Romain Serra + * @see AveragedOrbitalState + * @see BrouwerLyddanePropagator + * @since 12.1 + */ +public class BrouwerLyddaneOrbitalState extends AbstractHarmonicsBasedOrbitalState { + + /** So-called M2 coefficient, set to zero. */ + private static final double M2 = 0.; + + /** Averaged Keplerian elements. */ + private final AveragedKeplerianWithMeanAngle averagedElements; + + /** + * Constructor. + * @param date epoch + * @param elements averaged orbital elements + * @param frame reference frame + * @param harmonicsProvider spherical harmonics provider + */ + public BrouwerLyddaneOrbitalState(final AbsoluteDate date, + final AveragedKeplerianWithMeanAngle elements, + final Frame frame, + final UnnormalizedSphericalHarmonicsProvider harmonicsProvider) { + super(date, frame, harmonicsProvider); + this.averagedElements = elements; + } + + /** {@inheritDoc} */ + @Override + public OrbitType getOrbitType() { + return OrbitType.KEPLERIAN; + } + + /** {@inheritDoc} */ + @Override + public PositionAngleType getPositionAngleType() { + return PositionAngleType.MEAN; + } + + /** {@inheritDoc} */ + @Override + public AveragedKeplerianWithMeanAngle getAveragedElements() { + return averagedElements; + } + + /** {@inheritDoc} */ + @Override + public Orbit toOsculatingOrbit() { + final BrouwerLyddanePropagator propagator = createPropagator(); + return propagator.propagateOrbit(getDate()); + } + + /** + * Create Brouwer-Lyddane propagator. + * @return propagator using relevant theory + */ + private BrouwerLyddanePropagator createPropagator() { + final KeplerianOrbit orekitOrbit = createOrekitOrbit(); + return new BrouwerLyddanePropagator(orekitOrbit, getHarmonicsProvider(), + PropagationType.MEAN, M2); + } + + /** + * Create Keplerian orbit representation of averaged state. + * @return Keplerian orbit + */ + private KeplerianOrbit createOrekitOrbit() { + return new KeplerianOrbit(averagedElements.getAveragedSemiMajorAxis(), + averagedElements.getAveragedEccentricity(), averagedElements.getAveragedInclination(), + averagedElements.getAveragedPerigeeArgument(), + averagedElements.getAveragedRightAscensionOfTheAscendingNode(), + averagedElements.getAveragedMeanAnomaly(), getPositionAngleType(), getFrame(), + getDate(), getMu()); + } + +} diff --git a/src/main/java/org/orekit/propagation/conversion/averaging/DSST6X0OrbitalState.java b/src/main/java/org/orekit/propagation/conversion/averaging/DSST6X0OrbitalState.java new file mode 100644 index 0000000000..7aee65460f --- /dev/null +++ b/src/main/java/org/orekit/propagation/conversion/averaging/DSST6X0OrbitalState.java @@ -0,0 +1,119 @@ +/* Copyright 2020-2024 Exotrail + * 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.averaging; + +import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; +import org.orekit.frames.Frame; +import org.orekit.orbits.EquinoctialOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; +import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.semianalytical.dsst.DSSTPropagator; +import org.orekit.propagation.semianalytical.dsst.forces.DSSTForceModel; +import org.orekit.propagation.semianalytical.dsst.forces.DSSTZonal; +import org.orekit.time.AbsoluteDate; +import org.orekit.propagation.conversion.averaging.elements.AveragedEquinoctialWithMeanAngle; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * Class representing an averaged orbital state as in the DSST theory using only the first 6 zonal + * harmonics as perturbations. + * + * @author Romain Serra + * @see AveragedOrbitalState + * @see DSSTPropagator + * @see DSSTZonal + * @since 12.1 + */ +public class DSST6X0OrbitalState extends AbstractHarmonicsBasedOrbitalState { + + /** Averaged equinoctial elements. */ + private final AveragedEquinoctialWithMeanAngle averagedElements; + + /** + * Constructor. + * @param date epoch + * @param elements averaged orbital elements + * @param frame reference frame + * @param harmonicsProvider spherical harmonics provider + */ + public DSST6X0OrbitalState(final AbsoluteDate date, + final AveragedEquinoctialWithMeanAngle elements, + final Frame frame, + final UnnormalizedSphericalHarmonicsProvider harmonicsProvider) { + super(date, frame, harmonicsProvider); + this.averagedElements = elements; + } + + /** + * Create collection of fist 6 zonal DSST forces. + * @param provider spherical harmonics provider + * @return six first zonal forces + */ + public static Collection createForces(final UnnormalizedSphericalHarmonicsProvider provider) { + final List forceModels = new ArrayList<>(); + final DSSTZonal zonal = new DSSTZonal(provider); + forceModels.add(zonal); + return forceModels; + } + + /** {@inheritDoc} */ + @Override + public OrbitType getOrbitType() { + return OrbitType.EQUINOCTIAL; + } + + /** {@inheritDoc} */ + @Override + public PositionAngleType getPositionAngleType() { + return PositionAngleType.MEAN; + } + + /** {@inheritDoc} */ + @Override + public AveragedEquinoctialWithMeanAngle getAveragedElements() { + return averagedElements; + } + + /** {@inheritDoc} */ + @Override + public Orbit toOsculatingOrbit() { + final EquinoctialOrbit orekitOrbit = createOrekitOrbit(); + final Collection forceModels = createForces(getHarmonicsProvider()); + final SpacecraftState osculatingState = DSSTPropagator.computeOsculatingState( + new SpacecraftState(orekitOrbit), null, forceModels); + return osculatingState.getOrbit(); + } + + /** + * Create equinoctial orbit representation of averaged state. + * @return equinoctial orbit + */ + private EquinoctialOrbit createOrekitOrbit() { + return new EquinoctialOrbit(averagedElements.getAveragedSemiMajorAxis(), + averagedElements.getAveragedEquinoctialEx(), + averagedElements.getAveragedEquinoctialEy(), + averagedElements.getAveragedHx(), averagedElements.getAveragedHy(), + averagedElements.getAveragedMeanLongitudeArgument(), + getPositionAngleType(), getFrame(), getDate(), getMu()); + } + +} diff --git a/src/main/java/org/orekit/propagation/conversion/averaging/EcksteinHechlerOrbitalState.java b/src/main/java/org/orekit/propagation/conversion/averaging/EcksteinHechlerOrbitalState.java new file mode 100644 index 0000000000..a6a127a63a --- /dev/null +++ b/src/main/java/org/orekit/propagation/conversion/averaging/EcksteinHechlerOrbitalState.java @@ -0,0 +1,106 @@ +/* Copyright 2020-2024 Exotrail + * 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.averaging; + +import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; +import org.orekit.frames.Frame; +import org.orekit.orbits.CircularOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; +import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.PropagationType; +import org.orekit.propagation.analytical.EcksteinHechlerPropagator; +import org.orekit.time.AbsoluteDate; +import org.orekit.propagation.conversion.averaging.elements.AveragedCircularWithMeanAngle; + +/** + * Class representing an averaged orbital state as in the Eckstein-Hechler theory. + * + * @author Romain Serra + * @see AveragedOrbitalState + * @see EcksteinHechlerPropagator + * @since 12.1 + */ +public class EcksteinHechlerOrbitalState extends AbstractHarmonicsBasedOrbitalState { + + /** Averaged circular elements. */ + private final AveragedCircularWithMeanAngle averagedElements; + + /** + * Constructor. + * @param date epoch + * @param elements averaged orbital elements + * @param frame reference frame + * @param harmonicsProvider spherical harmonics provider + */ + public EcksteinHechlerOrbitalState(final AbsoluteDate date, + final AveragedCircularWithMeanAngle elements, + final Frame frame, + final UnnormalizedSphericalHarmonicsProvider harmonicsProvider) { + super(date, frame, harmonicsProvider); + this.averagedElements = elements; + } + + /** {@inheritDoc} */ + @Override + public OrbitType getOrbitType() { + return OrbitType.CIRCULAR; + } + + /** {@inheritDoc} */ + @Override + public PositionAngleType getPositionAngleType() { + return PositionAngleType.MEAN; + } + + /** {@inheritDoc} */ + @Override + public AveragedCircularWithMeanAngle getAveragedElements() { + return averagedElements; + } + + /** {@inheritDoc} */ + @Override + public Orbit toOsculatingOrbit() { + final EcksteinHechlerPropagator propagator = createPropagator(); + return propagator.propagateOrbit(getDate()); + } + + /** + * Create Eckstein-Hechler propagator. + * @return propagator using relevant theory + */ + private EcksteinHechlerPropagator createPropagator() { + final CircularOrbit orekitOrbit = createOrekitOrbit(); + return new EcksteinHechlerPropagator(orekitOrbit, getHarmonicsProvider(), + PropagationType.MEAN); + } + + /** + * Create circular orbit representation of averaged state. + * @return circular orbit + */ + private CircularOrbit createOrekitOrbit() { + return new CircularOrbit(averagedElements.getAveragedSemiMajorAxis(), + averagedElements.getAveragedCircularEx(), averagedElements.getAveragedCircularEy(), + averagedElements.getAveragedInclination(), + averagedElements.getAveragedRightAscensionOfTheAscendingNode(), + averagedElements.getAveragedMeanLatitudeArgument(), getPositionAngleType(), + getFrame(), getDate(), getMu()); + } + +} diff --git a/src/main/java/org/orekit/propagation/conversion/averaging/SGP4OrbitalState.java b/src/main/java/org/orekit/propagation/conversion/averaging/SGP4OrbitalState.java new file mode 100644 index 0000000000..8650bf38e5 --- /dev/null +++ b/src/main/java/org/orekit/propagation/conversion/averaging/SGP4OrbitalState.java @@ -0,0 +1,194 @@ +/* Copyright 2020-2024 Exotrail + * 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.averaging; + +import org.hipparchus.util.FastMath; +import org.orekit.annotation.DefaultDataContext; +import org.orekit.data.DataContext; +import org.orekit.frames.Frame; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; +import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.analytical.tle.TLE; +import org.orekit.propagation.analytical.tle.TLEPropagator; +import org.orekit.propagation.conversion.averaging.elements.AveragedKeplerianWithMeanAngle; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.TimeScale; +import org.orekit.time.UTCScale; + +/** + * Class representing an averaged orbital state as in the TLE-related theory. + * Note it is the averaged mean motion that is written in a Two-Line Element and that, for now, + * conversions back and forth to averaged semi-major axis are approximated with the osculating ones. + * + * @author Romain Serra + * @see AveragedOrbitalState + * @see TLEPropagator + * @since 12.1 + */ +public class SGP4OrbitalState extends AbstractAveragedOrbitalState { + + /** B-star used internally and set to zero. Should not impact calculations. */ + private static final double B_STAR = 0.; + + /** Averaged Keplerian elements. */ + private final AveragedKeplerianWithMeanAngle averagedElements; + /** UTC time scale. */ + private final UTCScale utc; + + /** + * Constructor. + * @param date epoch + * @param elements averaged orbital elements + * @param dataContext data context + */ + public SGP4OrbitalState(final AbsoluteDate date, + final AveragedKeplerianWithMeanAngle elements, + final DataContext dataContext) { + this(date, elements, dataContext.getFrames().getTEME(), + dataContext.getTimeScales().getUTC()); + } + + /** + * Constructor with default data context. + * @param date epoch + * @param elements averaged orbital elements + */ + @DefaultDataContext + public SGP4OrbitalState(final AbsoluteDate date, + final AveragedKeplerianWithMeanAngle elements) { + this(date, elements, DataContext.getDefault()); + } + + /** + * Private constructor. + * @param date epoch + * @param elements averaged orbital elements + * @param teme TEME frame + * @param utc UTC time scale + */ + private SGP4OrbitalState(final AbsoluteDate date, + final AveragedKeplerianWithMeanAngle elements, + final Frame teme, final TimeScale utc) { + super(date, teme); + this.averagedElements = elements; + this.utc = (UTCScale) utc; + } + + /** + * Static constructor. Input frame is implicitly assumed to be TEME (it is not checked). + * @param tle TLE + * @param teme TEME frame (not checked) + * @return TLE-based averaged orbital state + */ + public static SGP4OrbitalState of(final TLE tle, final Frame teme) { + final double semiMajorAxis = computeSemiMajorAxis(tle); + final AveragedKeplerianWithMeanAngle elements = new AveragedKeplerianWithMeanAngle( + semiMajorAxis, tle.getE(), tle.getI(), tle.getPerigeeArgument(), tle.getRaan(), + tle.getMeanAnomaly()); + return new SGP4OrbitalState(tle.getDate(), elements, teme, tle.getUtc()); + } + + /** {@inheritDoc} */ + @Override + public double getMu() { + return getTleMu(); + } + + /** + * Getter for TLE's Earth gravitational constant. + * @return mu. + */ + private static double getTleMu() { + return TLEPropagator.getMU(); + } + + /** {@inheritDoc} */ + @Override + public OrbitType getOrbitType() { + return OrbitType.KEPLERIAN; + } + + /** {@inheritDoc} */ + @Override + public PositionAngleType getPositionAngleType() { + return PositionAngleType.MEAN; + } + + /** {@inheritDoc} */ + @Override + public AveragedKeplerianWithMeanAngle getAveragedElements() { + return averagedElements; + } + + /** {@inheritDoc} */ + @Override + public Orbit toOsculatingOrbit() { + final TLEPropagator propagator = createPropagator(); + return propagator.getInitialState().getOrbit(); + } + + /** + * Create TLE propagator. + * @return propagator using relevant theory + */ + private TLEPropagator createPropagator() { + final TLE tle = createTLE(); + return TLEPropagator.selectExtrapolator(tle, getFrame()); + } + + /** + * Create Orekit TLE. + * @return TLE + */ + private TLE createTLE() { + final double averagedMeanMotion = computeMeanMotion(getAveragedElements() + .getAveragedSemiMajorAxis()); + final double averagedEccentricity = getAveragedElements().getAveragedEccentricity(); + final double averagedInclination = getAveragedElements().getAveragedInclination(); + final double averagedRAAN = getAveragedElements().getAveragedRightAscensionOfTheAscendingNode(); + final double averagedPerigeeArgument = getAveragedElements().getAveragedPerigeeArgument(); + final double averagedMeanAnomaly = getAveragedElements().getAveragedMeanAnomaly(); + return new TLE(0, (char) 0, 2000, 1, "1", 0, 0, + getDate(), averagedMeanMotion, 0., 0., averagedEccentricity, + averagedInclination, averagedPerigeeArgument, averagedRAAN, averagedMeanAnomaly, + 1, B_STAR, utc); + } + + /** + * Convert averaged semi-major axis to averaged mean motion. Uses an approximate transformation + * (same as osculating). + * @param averagedSemiMajorAxis semi-major axis + * @return mean motion + */ + private static double computeMeanMotion(final double averagedSemiMajorAxis) { + final double cubedSemiMajorAxis = averagedSemiMajorAxis * averagedSemiMajorAxis * + averagedSemiMajorAxis; + return FastMath.sqrt(getTleMu() / cubedSemiMajorAxis); + } + + /** + * Compute semi-major axis from Two-Line Elements. Uses an approximate transformation + * (same as osculating). + * @param tle TLE + * @return semi-major axis + */ + private static double computeSemiMajorAxis(final TLE tle) { + final double meanMotion = tle.getMeanMotion(); + return FastMath.cbrt(getTleMu() / (meanMotion * meanMotion)); + } +} diff --git a/src/main/java/org/orekit/propagation/conversion/averaging/converters/FixedPointOsculatingToAveragedConverter.java b/src/main/java/org/orekit/propagation/conversion/averaging/converters/FixedPointOsculatingToAveragedConverter.java new file mode 100644 index 0000000000..d921e43c98 --- /dev/null +++ b/src/main/java/org/orekit/propagation/conversion/averaging/converters/FixedPointOsculatingToAveragedConverter.java @@ -0,0 +1,84 @@ +/* Copyright 2020-2024 Exotrail + * 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.averaging.converters; + +import org.orekit.propagation.conversion.averaging.AveragedOrbitalState; + +/** + * Abstract class for osculating-to-averaged converters based on a fixed-point algorithm. + * + * @author Romain Serra + * @since 12.1 + * @see OsculatingToAveragedConverter + * @param type of averaged orbital state + */ +public abstract class FixedPointOsculatingToAveragedConverter + implements OsculatingToAveragedConverter { + + /** Default convergence threshold. */ + public static final double DEFAULT_EPSILON = 1e-12; + /** Default maximum number of iterations. */ + public static final int DEFAULT_MAX_ITERATIONS = 100; + + /** Convergence threshold. */ + private double epsilon; + /** Maximum number of iterations. */ + private int maxIterations; + + /** + * Protected constructor. + * @param epsilon tolerance for convergence + * @param maxIterations maximum number of iterations + */ + protected FixedPointOsculatingToAveragedConverter(final double epsilon, + final int maxIterations) { + this.epsilon = epsilon; + this.maxIterations = maxIterations; + } + + /** + * Getter for the maximum number of iterations. + * @return maximum number of iterations + */ + public int getMaxIterations() { + return maxIterations; + } + + /** + * Getter for the convergence threshold. + * @return convergence threshold + */ + public double getEpsilon() { + return epsilon; + } + + /** + * Setter for epsilon. + * @param epsilon convergence threshold. + */ + public void setEpsilon(final double epsilon) { + this.epsilon = epsilon; + } + + /** + * Setter for maximum number of iterations. + * @param maxIterations maximum iterations + */ + public void setMaxIterations(final int maxIterations) { + this.maxIterations = maxIterations; + } +} diff --git a/src/main/java/org/orekit/propagation/conversion/averaging/converters/OsculatingToAveragedConverter.java b/src/main/java/org/orekit/propagation/conversion/averaging/converters/OsculatingToAveragedConverter.java new file mode 100644 index 0000000000..7acefb698a --- /dev/null +++ b/src/main/java/org/orekit/propagation/conversion/averaging/converters/OsculatingToAveragedConverter.java @@ -0,0 +1,39 @@ +/* Copyright 2020-2024 Exotrail + * 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.averaging.converters; + +import org.orekit.orbits.Orbit; +import org.orekit.propagation.conversion.averaging.AveragedOrbitalState; + +/** + * Interface for osculating-to-averaged converters. + * + * @author Romain Serra + * @since 12.1 + * @see AveragedOrbitalState + * @param type of averaged orbital state + */ +public interface OsculatingToAveragedConverter { + + /** + * Convert osculating orbit to averaged orbital state according to underlying theory. + * @param osculatingOrbit osculating orbit + * @return averaged orbital state + */ + T convertToAveraged(Orbit osculatingOrbit); + +} diff --git a/src/main/java/org/orekit/propagation/conversion/averaging/converters/OsculatingToBrouwerLyddaneConverter.java b/src/main/java/org/orekit/propagation/conversion/averaging/converters/OsculatingToBrouwerLyddaneConverter.java new file mode 100644 index 0000000000..07eca63fd0 --- /dev/null +++ b/src/main/java/org/orekit/propagation/conversion/averaging/converters/OsculatingToBrouwerLyddaneConverter.java @@ -0,0 +1,103 @@ +/* Copyright 2020-2024 Exotrail + * 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.averaging.converters; + +import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; +import org.orekit.orbits.KeplerianOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.propagation.analytical.BrouwerLyddanePropagator; +import org.orekit.propagation.conversion.averaging.BrouwerLyddaneOrbitalState; +import org.orekit.propagation.conversion.averaging.elements.AveragedKeplerianWithMeanAngle; + +/** + * Class for osculating-to-averaged conversion according to Brouwer-Lyddane theory. + * Value of M2 parameter is set to zero. + * + * @author Romain Serra + * @see BrouwerLyddanePropagator + * @see BrouwerLyddaneOrbitalState + * @since 12.1 + */ +public class OsculatingToBrouwerLyddaneConverter + extends FixedPointOsculatingToAveragedConverter { + + /** So-called M2 coefficient, set to zero. */ + private static final double M2 = 0.; + /** Order for spherical harmonics. */ + private static final int HARMONICS_ORDER = 0; + + /** Spherical harmonics provider. */ + private final UnnormalizedSphericalHarmonicsProvider harmonicsProvider; + + /** + * Constructor with default parameters for fixed-point algorithm. + * @param harmonicsProvider unnormalized provider + */ + public OsculatingToBrouwerLyddaneConverter(final UnnormalizedSphericalHarmonicsProvider harmonicsProvider) { + this(DEFAULT_EPSILON, DEFAULT_MAX_ITERATIONS, harmonicsProvider); + } + + /** + * Constructor. + * @param epsilon convergence threshold + * @param maxIterations maximum number of iterations + * @param harmonicsProvider unnormalized provider + */ + public OsculatingToBrouwerLyddaneConverter(final double epsilon, final int maxIterations, + final UnnormalizedSphericalHarmonicsProvider harmonicsProvider) { + super(epsilon, maxIterations); + this.harmonicsProvider = harmonicsProvider; + } + + /** {@inheritDoc} */ + @Override + public BrouwerLyddaneOrbitalState convertToAveraged(final Orbit osculatingOrbit) { + final KeplerianOrbit averagedOrbit = createAveragedOrbit(osculatingOrbit); + final AveragedKeplerianWithMeanAngle averagedElements = buildElements(averagedOrbit); + return new BrouwerLyddaneOrbitalState(averagedOrbit.getDate(), averagedElements, + averagedOrbit.getFrame(), harmonicsProvider); + } + + /** + * Build averaged orbit. + * @param osculatingOrbit osculating orbit + * @return averaged Keplerian orbit in Brouwer-Lyddane sense. + */ + private KeplerianOrbit createAveragedOrbit(final Orbit osculatingOrbit) { + final UnnormalizedSphericalHarmonicsProvider.UnnormalizedSphericalHarmonics harmonics = + harmonicsProvider.onDate(osculatingOrbit.getDate()); + return BrouwerLyddanePropagator.computeMeanOrbit(osculatingOrbit, + harmonicsProvider.getAe(), harmonicsProvider.getMu(), + harmonics.getUnnormalizedCnm(2, HARMONICS_ORDER), + harmonics.getUnnormalizedCnm(3, HARMONICS_ORDER), + harmonics.getUnnormalizedCnm(4, HARMONICS_ORDER), + harmonics.getUnnormalizedCnm(5, HARMONICS_ORDER), + M2, getEpsilon(), getMaxIterations()); + } + + /** + * Build averaged orbital elements from orbit. + * @param averagedOrbit averaged orbit + * @return orbital elements + */ + private AveragedKeplerianWithMeanAngle buildElements(final KeplerianOrbit averagedOrbit) { + return new AveragedKeplerianWithMeanAngle(averagedOrbit.getA(), averagedOrbit.getE(), + averagedOrbit.getI(), averagedOrbit.getPerigeeArgument(), + averagedOrbit.getRightAscensionOfAscendingNode(), averagedOrbit.getMeanAnomaly()); + } + +} diff --git a/src/main/java/org/orekit/propagation/conversion/averaging/converters/OsculatingToDSST6X0Converter.java b/src/main/java/org/orekit/propagation/conversion/averaging/converters/OsculatingToDSST6X0Converter.java new file mode 100644 index 0000000000..d6d3830c7d --- /dev/null +++ b/src/main/java/org/orekit/propagation/conversion/averaging/converters/OsculatingToDSST6X0Converter.java @@ -0,0 +1,98 @@ +/* Copyright 2020-2024 Exotrail + * 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.averaging.converters; + +import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; +import org.orekit.orbits.Orbit; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.semianalytical.dsst.DSSTPropagator; +import org.orekit.propagation.semianalytical.dsst.forces.DSSTForceModel; +import org.orekit.propagation.conversion.averaging.DSST6X0OrbitalState; +import org.orekit.propagation.conversion.averaging.elements.AveragedEquinoctialWithMeanAngle; + +import java.util.Collection; + +/** + * Class for osculating-to-averaged conversion according to DSST theory, using 6 zonal harmonics as + * the only perturbations. + * + * @author Romain Serra + * @see DSSTPropagator + * @see DSST6X0OrbitalState + * @since 12.1 + */ +public class OsculatingToDSST6X0Converter + extends FixedPointOsculatingToAveragedConverter { + + /** Spherical harmonics provider. */ + private final UnnormalizedSphericalHarmonicsProvider harmonicsProvider; + + /** + * Constructor with default parameters for fixed-point algorithm. + * @param harmonicsProvider unnormalized provider + */ + public OsculatingToDSST6X0Converter(final UnnormalizedSphericalHarmonicsProvider harmonicsProvider) { + this(DEFAULT_EPSILON, DEFAULT_MAX_ITERATIONS, harmonicsProvider); + } + + /** + * Constructor. + * @param epsilon convergence threshold + * @param maxIterations maximum number of iterations + * @param harmonicsProvider unnormalized provider + */ + public OsculatingToDSST6X0Converter(final double epsilon, final int maxIterations, + final UnnormalizedSphericalHarmonicsProvider harmonicsProvider) { + super(epsilon, maxIterations); + this.harmonicsProvider = harmonicsProvider; + } + + /** {@inheritDoc} */ + @Override + public DSST6X0OrbitalState convertToAveraged(final Orbit osculatingOrbit) { + final Orbit averagedOrbit = createAveragedOrbit(osculatingOrbit); + final AveragedEquinoctialWithMeanAngle elements = buildElements(averagedOrbit); + return new DSST6X0OrbitalState(averagedOrbit.getDate(), elements, + averagedOrbit.getFrame(), harmonicsProvider); + } + + /** + * Build averaged orbit. + * @param osculatingOrbit osculating orbit + * @return averaged orbit in DSST sense. + */ + private Orbit createAveragedOrbit(final Orbit osculatingOrbit) { + final Collection forceModels = DSST6X0OrbitalState + .createForces(harmonicsProvider); + final SpacecraftState osculatingState = new SpacecraftState(osculatingOrbit); + final SpacecraftState averagedState = DSSTPropagator.computeMeanState(osculatingState, + null, forceModels, getEpsilon(), getMaxIterations()); + return averagedState.getOrbit(); + } + + /** + * Build averaged orbital elements from orbit. + * @param averagedOrbit averaged orbit + * @return orbital elements + */ + private AveragedEquinoctialWithMeanAngle buildElements(final Orbit averagedOrbit) { + return new AveragedEquinoctialWithMeanAngle(averagedOrbit.getA(), + averagedOrbit.getEquinoctialEx(), averagedOrbit.getEquinoctialEy(), + averagedOrbit.getHx(), averagedOrbit.getHy(), averagedOrbit.getLM()); + } + +} diff --git a/src/main/java/org/orekit/propagation/conversion/averaging/converters/OsculatingToEcksteinHechlerConverter.java b/src/main/java/org/orekit/propagation/conversion/averaging/converters/OsculatingToEcksteinHechlerConverter.java new file mode 100644 index 0000000000..5ff296d5ac --- /dev/null +++ b/src/main/java/org/orekit/propagation/conversion/averaging/converters/OsculatingToEcksteinHechlerConverter.java @@ -0,0 +1,101 @@ +/* Copyright 2020-2024 Exotrail + * 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.averaging.converters; + +import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; +import org.orekit.orbits.CircularOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.propagation.analytical.EcksteinHechlerPropagator; +import org.orekit.propagation.conversion.averaging.EcksteinHechlerOrbitalState; +import org.orekit.propagation.conversion.averaging.elements.AveragedCircularWithMeanAngle; + +/** + * Class for osculating-to-averaged conversion according to Eckstein-Hechler theory. + * + * @author Romain Serra + * @see EcksteinHechlerPropagator + * @see EcksteinHechlerOrbitalState + * @since 12.1 + */ +public class OsculatingToEcksteinHechlerConverter + extends FixedPointOsculatingToAveragedConverter { + + /** Order for spherical harmonics. */ + private static final int HARMONICS_ORDER = 0; + + /** Spherical harmonics provider. */ + private final UnnormalizedSphericalHarmonicsProvider harmonicsProvider; + + /** + * Constructor with default parameters for fixed-point algorithm. + * @param harmonicsProvider unnormalized provider + */ + public OsculatingToEcksteinHechlerConverter(final UnnormalizedSphericalHarmonicsProvider harmonicsProvider) { + this(DEFAULT_EPSILON, DEFAULT_MAX_ITERATIONS, harmonicsProvider); + } + + /** + * Constructor. + * @param epsilon convergence threshold + * @param maxIterations maximum number of iterations + * @param harmonicsProvider unnormalized provider + */ + public OsculatingToEcksteinHechlerConverter(final double epsilon, final int maxIterations, + final UnnormalizedSphericalHarmonicsProvider harmonicsProvider) { + super(epsilon, maxIterations); + this.harmonicsProvider = harmonicsProvider; + } + + /** {@inheritDoc} */ + @Override + public EcksteinHechlerOrbitalState convertToAveraged(final Orbit osculatingOrbit) { + final CircularOrbit averagedOrbit = createAveragedOrbit(osculatingOrbit); + final AveragedCircularWithMeanAngle elements = buildElements(averagedOrbit); + return new EcksteinHechlerOrbitalState(averagedOrbit.getDate(), elements, + averagedOrbit.getFrame(), harmonicsProvider); + } + + /** + * Build averaged orbit. + * @param osculatingOrbit osculating orbit + * @return averaged Circular orbit in Eckstein-Hechler sense. + */ + private CircularOrbit createAveragedOrbit(final Orbit osculatingOrbit) { + final UnnormalizedSphericalHarmonicsProvider.UnnormalizedSphericalHarmonics harmonics = + harmonicsProvider.onDate(osculatingOrbit.getDate()); + return EcksteinHechlerPropagator.computeMeanOrbit(osculatingOrbit, + harmonicsProvider.getAe(), harmonicsProvider.getMu(), + harmonics.getUnnormalizedCnm(2, HARMONICS_ORDER), + harmonics.getUnnormalizedCnm(3, HARMONICS_ORDER), + harmonics.getUnnormalizedCnm(4, HARMONICS_ORDER), + harmonics.getUnnormalizedCnm(5, HARMONICS_ORDER), + harmonics.getUnnormalizedCnm(6, HARMONICS_ORDER), + getEpsilon(), getMaxIterations()); + } + + /** + * Build averaged orbital elements from orbit. + * @param averagedOrbit averaged orbit + * @return orbital elements + */ + private AveragedCircularWithMeanAngle buildElements(final CircularOrbit averagedOrbit) { + return new AveragedCircularWithMeanAngle(averagedOrbit.getA(), + averagedOrbit.getCircularEx(), averagedOrbit.getCircularEy(), averagedOrbit.getI(), + averagedOrbit.getRightAscensionOfAscendingNode(), averagedOrbit.getAlphaM()); + } + +} diff --git a/src/main/java/org/orekit/propagation/conversion/averaging/converters/OsculatingToSGP4Converter.java b/src/main/java/org/orekit/propagation/conversion/averaging/converters/OsculatingToSGP4Converter.java new file mode 100644 index 0000000000..b60a312034 --- /dev/null +++ b/src/main/java/org/orekit/propagation/conversion/averaging/converters/OsculatingToSGP4Converter.java @@ -0,0 +1,95 @@ +/* Copyright 2020-2024 Exotrail + * 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.averaging.converters; + +import org.orekit.annotation.DefaultDataContext; +import org.orekit.data.DataContext; +import org.orekit.frames.Frame; +import org.orekit.orbits.Orbit; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.analytical.tle.TLE; +import org.orekit.propagation.analytical.tle.generation.FixedPointTleGenerationAlgorithm; +import org.orekit.time.UTCScale; +import org.orekit.propagation.conversion.averaging.SGP4OrbitalState; + +/** + * Class for osculating-to-averaged conversion according to "SGP4" theory, meant as the set of + * models associated to Two-Line Elements. + * + * @author Romain Serra + * @see org.orekit.propagation.analytical.tle.TLEPropagator + * @see SGP4OrbitalState + * @since 12.1 + */ +public class OsculatingToSGP4Converter + extends FixedPointOsculatingToAveragedConverter { + + /** First line of arbitrary TLE. Should not impact conversion. */ + private static final String TEMPLATE_LINE_1 = "1 27421U 02021A 02124.48976499 -.00021470 00000-0 -89879-2 0 20"; + /** Second line of arbitrary TLE. Should not impact conversion. */ + private static final String TEMPLATE_LINE_2 = "2 27421 98.7490 199.5121 0001333 133.9522 226.1918 14.26113993 62"; + + /** Scale for fixed-point algorithm. */ + private final double scale; + /** UTC time scale. */ + private final UTCScale utc; + /** TEME frame. */ + private final Frame teme; + + /** + * Constructor with default data context. + */ + @DefaultDataContext + public OsculatingToSGP4Converter() { + this(DataContext.getDefault()); + } + + /** + * Constructor with default parameters for fixed-point algorithm. + * @param dataContext data context + */ + public OsculatingToSGP4Converter(final DataContext dataContext) { + this(DEFAULT_EPSILON, DEFAULT_MAX_ITERATIONS, FixedPointTleGenerationAlgorithm.SCALE_DEFAULT, + dataContext); + } + + /** + * Constructor. + * @param epsilon convergence threshold + * @param maxIterations maximum number of iterations + * @param scale scale + * @param dataContext data context + */ + public OsculatingToSGP4Converter(final double epsilon, final int maxIterations, + final double scale, final DataContext dataContext) { + super(epsilon, maxIterations); + this.scale = scale; + this.utc = dataContext.getTimeScales().getUTC(); + this.teme = dataContext.getFrames().getTEME(); + } + + /** {@inheritDoc} */ + @Override + public SGP4OrbitalState convertToAveraged(final Orbit osculatingOrbit) { + final FixedPointTleGenerationAlgorithm fixedPointAlgorithm = new FixedPointTleGenerationAlgorithm(getEpsilon(), + getMaxIterations(), scale, utc, teme); + final SpacecraftState osculatingState = new SpacecraftState(osculatingOrbit); + final TLE templateTLe = new TLE(TEMPLATE_LINE_1, TEMPLATE_LINE_2, utc); + final TLE tle = fixedPointAlgorithm.generate(osculatingState, templateTLe); + return SGP4OrbitalState.of(tle, teme); + } +} diff --git a/src/main/java/org/orekit/propagation/conversion/averaging/converters/package-info.java b/src/main/java/org/orekit/propagation/conversion/averaging/converters/package-info.java new file mode 100644 index 0000000000..d830ab2d61 --- /dev/null +++ b/src/main/java/org/orekit/propagation/conversion/averaging/converters/package-info.java @@ -0,0 +1,24 @@ +/* Copyright 2020-2024 Exotrail + * 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 wraps conversions from an osculating orbit to an averaged state according to a + * given theory (usually via a fixed-point algorithm using the inverse conversion). + * + * @author Romain Serra + * @since 12.1 + */ +package org.orekit.propagation.conversion.averaging.converters; diff --git a/src/main/java/org/orekit/propagation/conversion/averaging/elements/AveragedCircularWithMeanAngle.java b/src/main/java/org/orekit/propagation/conversion/averaging/elements/AveragedCircularWithMeanAngle.java new file mode 100644 index 0000000000..b65daa6975 --- /dev/null +++ b/src/main/java/org/orekit/propagation/conversion/averaging/elements/AveragedCircularWithMeanAngle.java @@ -0,0 +1,119 @@ +/* Copyright 2020-2024 Exotrail + * 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.averaging.elements; + +/** + * Immutable class containing values of averaged circular elements from any applicable theory + * (with MEAN as {@link org.orekit.orbits.PositionAngleType}). + * + * @author Romain Serra + * @see AveragedOrbitalElements + * @since 12.1 + */ +public class AveragedCircularWithMeanAngle implements AveragedOrbitalElements { + + /** Averaged semi-major axis in arbitrary theory. */ + private final double averagedSemiMajorAxis; + /** Averaged circular ex in arbitrary theory. */ + private final double averagedCircularEx; + /** Averaged circular ey in arbitrary theory. */ + private final double averagedCircularEy; + /** Averaged inclination in arbitrary theory. */ + private final double averagedInclination; + /** Averaged right ascension of the ascending node in arbitrary theory. */ + private final double averagedRightAscensionOfTheAscendingNode; + /** Averaged mean latitude argument in arbitrary theory. */ + private final double averagedMeanLatitudeArgument; + + /** + * Constructor. + * @param averagedSemiMajorAxis averaged semi-major axis + * @param averagedCircularEx averaged circular ex + * @param averagedCircularEy averaged circular ey + * @param averagedInclination averaged inclination + * @param averagedRightAscensionOfTheAscendingNode averaged RAAN + * @param averagedMeanLatitudeArgument averaged mean latitude argument + */ + public AveragedCircularWithMeanAngle(final double averagedSemiMajorAxis, + final double averagedCircularEx, + final double averagedCircularEy, + final double averagedInclination, + final double averagedRightAscensionOfTheAscendingNode, + final double averagedMeanLatitudeArgument) { + this.averagedSemiMajorAxis = averagedSemiMajorAxis; + this.averagedCircularEx = averagedCircularEx; + this.averagedCircularEy = averagedCircularEy; + this.averagedInclination = averagedInclination; + this.averagedRightAscensionOfTheAscendingNode = averagedRightAscensionOfTheAscendingNode; + this.averagedMeanLatitudeArgument = averagedMeanLatitudeArgument; + } + + /** {@inheritDoc} */ + @Override + public double[] toArray() { + return new double[] { averagedSemiMajorAxis, averagedCircularEx, averagedCircularEy, + averagedInclination, averagedRightAscensionOfTheAscendingNode, averagedMeanLatitudeArgument }; + } + + /** + * Getter for averaged semi-major axis. + * @return semi-major axis. + */ + public double getAveragedSemiMajorAxis() { + return averagedSemiMajorAxis; + } + + /** + * Getter for averaged circular ex. + * @return ex + */ + public double getAveragedCircularEx() { + return averagedCircularEx; + } + + /** + * Getter for averaged circular ey. + * @return ey + */ + public double getAveragedCircularEy() { + return averagedCircularEy; + } + + /** + * Getter for averaged inclination. + * @return inclination + */ + public double getAveragedInclination() { + return averagedInclination; + } + + /** + * Getter for averaged RAAN. + * @return RAAN + */ + public double getAveragedRightAscensionOfTheAscendingNode() { + return averagedRightAscensionOfTheAscendingNode; + } + + /** + * Getter for averaged mean latitude argument. + * @return mean latitude argument + */ + public double getAveragedMeanLatitudeArgument() { + return averagedMeanLatitudeArgument; + } +} diff --git a/src/main/java/org/orekit/propagation/conversion/averaging/elements/AveragedEquinoctialWithMeanAngle.java b/src/main/java/org/orekit/propagation/conversion/averaging/elements/AveragedEquinoctialWithMeanAngle.java new file mode 100644 index 0000000000..d8796146a5 --- /dev/null +++ b/src/main/java/org/orekit/propagation/conversion/averaging/elements/AveragedEquinoctialWithMeanAngle.java @@ -0,0 +1,119 @@ +/* Copyright 2020-2024 Exotrail + * 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.averaging.elements; + +/** + * Immutable class containing values of averaged equinoctial elements from any applicable theory + * (with MEAN as {@link org.orekit.orbits.PositionAngleType}). + * + * @author Romain Serra + * @see AveragedOrbitalElements + * @since 12.1 + */ +public class AveragedEquinoctialWithMeanAngle implements AveragedOrbitalElements { + + /** Averaged semi-major axis in arbitrary theory. */ + private final double averagedSemiMajorAxis; + /** Averaged equinoctial ex in arbitrary theory. */ + private final double averagedEquinoctialEx; + /** Averaged equinoctial ey in arbitrary theory. */ + private final double averagedEquinoctialEy; + /** Averaged hx in arbitrary theory. */ + private final double averagedHx; + /** Averaged hy in arbitrary theory. */ + private final double averagedHy; + /** Averaged mean longitude argument in arbitrary theory. */ + private final double averagedMeanLongitudeArgument; + + /** + * Constructor. + * @param averagedSemiMajorAxis semi-major axis + * @param averagedEquinoctialEx equinoctial ex + * @param averagedEquinoctialEy equinoctial ey + * @param averagedHx hx + * @param averagedHy hy + * @param averagedMeanLongitudeArgument mean longitude argument + */ + public AveragedEquinoctialWithMeanAngle(final double averagedSemiMajorAxis, + final double averagedEquinoctialEx, + final double averagedEquinoctialEy, + final double averagedHx, + final double averagedHy, + final double averagedMeanLongitudeArgument) { + this.averagedSemiMajorAxis = averagedSemiMajorAxis; + this.averagedEquinoctialEx = averagedEquinoctialEx; + this.averagedEquinoctialEy = averagedEquinoctialEy; + this.averagedHx = averagedHx; + this.averagedHy = averagedHy; + this.averagedMeanLongitudeArgument = averagedMeanLongitudeArgument; + } + + /** {@inheritDoc} */ + @Override + public double[] toArray() { + return new double[] { averagedSemiMajorAxis, averagedEquinoctialEx, averagedEquinoctialEy, + averagedHx, averagedHy, averagedMeanLongitudeArgument }; + } + + /** + * Getter for the averaged semi-major axis. + * @return semi-major axis. + */ + public double getAveragedSemiMajorAxis() { + return averagedSemiMajorAxis; + } + + /** + * Getter for the averaged equinoctial ex. + * @return ex + */ + public double getAveragedEquinoctialEx() { + return averagedEquinoctialEx; + } + + /** + * Getter for the averaged equinoctial ey. + * @return ey + */ + public double getAveragedEquinoctialEy() { + return averagedEquinoctialEy; + } + + /** + * Getter for the averaged hx. + * @return hx + */ + public double getAveragedHx() { + return averagedHx; + } + + /** + * Getter for the averaged hy. + * @return hy + */ + public double getAveragedHy() { + return averagedHy; + } + + /** + * Getter for the averaged mean longitude argument. + * @return mean longitude argument + */ + public double getAveragedMeanLongitudeArgument() { + return averagedMeanLongitudeArgument; + } +} diff --git a/src/main/java/org/orekit/propagation/conversion/averaging/elements/AveragedKeplerianWithMeanAngle.java b/src/main/java/org/orekit/propagation/conversion/averaging/elements/AveragedKeplerianWithMeanAngle.java new file mode 100644 index 0000000000..a9af5e9cd9 --- /dev/null +++ b/src/main/java/org/orekit/propagation/conversion/averaging/elements/AveragedKeplerianWithMeanAngle.java @@ -0,0 +1,119 @@ +/* Copyright 2020-2024 Exotrail + * 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.averaging.elements; + +/** + * Immutable class containing values of averaged Keplerian elements from any applicable theory + * (with MEAN as {@link org.orekit.orbits.PositionAngleType}). + * + * @author Romain Serra + * @see AveragedOrbitalElements + * @since 12.1 + */ +public class AveragedKeplerianWithMeanAngle implements AveragedOrbitalElements { + + /** Averaged semi-major axis in arbitrary theory. */ + private final double averagedSemiMajorAxis; + /** Averaged eccentricity in arbitrary theory. */ + private final double averagedEccentricity; + /** Averaged inclination in arbitrary theory. */ + private final double averagedInclination; + /** Averaged perigee argument in arbitrary theory. */ + private final double averagedPerigeeArgument; + /** Averaged right ascension of the ascending node in arbitrary theory. */ + private final double averagedRightAscensionOfTheAscendingNode; + /** Averaged mean anomaly in arbitrary theory. */ + private final double averagedMeanAnomaly; + + /** + * Constructor. + * @param averagedSemiMajorAxis averaged semi-major axis + * @param averagedEccentricity averaged eccentricity + * @param averagedInclination averaged inclination + * @param averagedPerigeeArgument averaged perigee argument + * @param averagedRightAscensionOfTheAscendingNode averaged RAAN + * @param averagedMeanAnomaly averaged mean anomaly + */ + public AveragedKeplerianWithMeanAngle(final double averagedSemiMajorAxis, + final double averagedEccentricity, + final double averagedInclination, + final double averagedPerigeeArgument, + final double averagedRightAscensionOfTheAscendingNode, + final double averagedMeanAnomaly) { + this.averagedSemiMajorAxis = averagedSemiMajorAxis; + this.averagedEccentricity = averagedEccentricity; + this.averagedInclination = averagedInclination; + this.averagedPerigeeArgument = averagedPerigeeArgument; + this.averagedRightAscensionOfTheAscendingNode = averagedRightAscensionOfTheAscendingNode; + this.averagedMeanAnomaly = averagedMeanAnomaly; + } + + /** {@inheritDoc} */ + @Override + public double[] toArray() { + return new double[] { averagedSemiMajorAxis, averagedEccentricity, averagedInclination, + averagedPerigeeArgument, averagedRightAscensionOfTheAscendingNode, averagedMeanAnomaly }; + } + + /** + * Getter for the averaged semi-major axis. + * @return semi-major axis + */ + public double getAveragedSemiMajorAxis() { + return averagedSemiMajorAxis; + } + + /** + * Getter for the averaged eccentricity. + * @return eccentricity + */ + public double getAveragedEccentricity() { + return averagedEccentricity; + } + + /** + * Getter for the averaged inclination. + * @return inclination + */ + public double getAveragedInclination() { + return averagedInclination; + } + + /** + * Getter for the averaged perigee argument. + * @return perigee argument. + */ + public double getAveragedPerigeeArgument() { + return averagedPerigeeArgument; + } + + /** + * Getter for the averaged RAAN. + * @return RAAN + */ + public double getAveragedRightAscensionOfTheAscendingNode() { + return averagedRightAscensionOfTheAscendingNode; + } + + /** + * Getter for the averaged mean anomaly. + * @return mean anomaly + */ + public double getAveragedMeanAnomaly() { + return averagedMeanAnomaly; + } +} diff --git a/src/main/java/org/orekit/propagation/conversion/averaging/elements/AveragedOrbitalElements.java b/src/main/java/org/orekit/propagation/conversion/averaging/elements/AveragedOrbitalElements.java new file mode 100644 index 0000000000..ddb913e451 --- /dev/null +++ b/src/main/java/org/orekit/propagation/conversion/averaging/elements/AveragedOrbitalElements.java @@ -0,0 +1,33 @@ +/* Copyright 2020-2024 Exotrail + * 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.averaging.elements; + +/** + * Interface for storing averaged orbital elements. + * + * @author Romain Serra + * @since 12.1 + */ +public interface AveragedOrbitalElements { + + /** + * Write values from instance into an array of doubles. + * @return array with values + */ + double[] toArray(); + +} diff --git a/src/main/java/org/orekit/propagation/conversion/averaging/elements/package-info.java b/src/main/java/org/orekit/propagation/conversion/averaging/elements/package-info.java new file mode 100644 index 0000000000..da6bf92e64 --- /dev/null +++ b/src/main/java/org/orekit/propagation/conversion/averaging/elements/package-info.java @@ -0,0 +1,24 @@ +/* Copyright 2020-2024 Exotrail + * 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 contains data classes representing averaged orbital elements (not time-stamped and + * in any theory compatible with this choice of coordinates). + * + * @author Romain Serra + * @since 12.1 + */ +package org.orekit.propagation.conversion.averaging.elements; diff --git a/src/main/java/org/orekit/propagation/conversion/averaging/package-info.java b/src/main/java/org/orekit/propagation/conversion/averaging/package-info.java new file mode 100644 index 0000000000..81ff31aca1 --- /dev/null +++ b/src/main/java/org/orekit/propagation/conversion/averaging/package-info.java @@ -0,0 +1,24 @@ +/* Copyright 2020-2024 Exotrail + * 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 wraps methods from various (semi)analytical models in Orekit to convert back and + * forth between an averaged orbital state and an osculating one. + * + * @author Romain Serra + * @since 12.1 + */ +package org.orekit.propagation.conversion.averaging; diff --git a/src/main/java/org/orekit/propagation/events/AbstractDetector.java b/src/main/java/org/orekit/propagation/events/AbstractDetector.java index 24638814e1..b01e35f808 100644 --- a/src/main/java/org/orekit/propagation/events/AbstractDetector.java +++ b/src/main/java/org/orekit/propagation/events/AbstractDetector.java @@ -61,7 +61,7 @@ public abstract class AbstractDetector> implements */ protected AbstractDetector(final double maxCheck, final double threshold, final int maxIter, final EventHandler handler) { - this(s -> maxCheck, threshold, maxIter, handler); + this(AdaptableInterval.of(maxCheck), threshold, maxIter, handler); } /** Build a new instance. @@ -133,7 +133,7 @@ public double getThreshold() { * @since 6.1 */ public T withMaxCheck(final double newMaxCheck) { - return withMaxCheck(s -> newMaxCheck); + return withMaxCheck(AdaptableInterval.of(newMaxCheck)); } /** diff --git a/src/main/java/org/orekit/propagation/events/AdaptableInterval.java b/src/main/java/org/orekit/propagation/events/AdaptableInterval.java index 4238182ce4..35ff4fe335 100644 --- a/src/main/java/org/orekit/propagation/events/AdaptableInterval.java +++ b/src/main/java/org/orekit/propagation/events/AdaptableInterval.java @@ -35,4 +35,13 @@ public interface AdaptableInterval { */ double currentInterval(SpacecraftState state); + /** + * Method creating a constant interval provider. + * @param constantInterval value of constant interval + * @return adaptable interval ready to be added to an event detector + * @since 12.1 + */ + static AdaptableInterval of(final double constantInterval) { + return state -> constantInterval; + } } diff --git a/src/main/java/org/orekit/propagation/events/AlignmentDetector.java b/src/main/java/org/orekit/propagation/events/AlignmentDetector.java index 9a9b9c48dc..6b5943e904 100644 --- a/src/main/java/org/orekit/propagation/events/AlignmentDetector.java +++ b/src/main/java/org/orekit/propagation/events/AlignmentDetector.java @@ -73,7 +73,7 @@ public AlignmentDetector(final Orbit orbit, public AlignmentDetector(final double maxCheck, final double threshold, final PVCoordinatesProvider body, final double alignAngle) { - this(s -> maxCheck, threshold, DEFAULT_MAX_ITER, + this(AdaptableInterval.of(maxCheck), threshold, DEFAULT_MAX_ITER, new StopOnIncreasing(), body, alignAngle); } diff --git a/src/main/java/org/orekit/propagation/events/AltitudeDetector.java b/src/main/java/org/orekit/propagation/events/AltitudeDetector.java index f70a31586f..2139489f7c 100644 --- a/src/main/java/org/orekit/propagation/events/AltitudeDetector.java +++ b/src/main/java/org/orekit/propagation/events/AltitudeDetector.java @@ -85,7 +85,7 @@ public AltitudeDetector(final double maxCheck, final double threshold, final double altitude, final BodyShape bodyShape) { - this(s -> maxCheck, threshold, DEFAULT_MAX_ITER, new StopOnDecreasing(), + this(AdaptableInterval.of(maxCheck), threshold, DEFAULT_MAX_ITER, new StopOnDecreasing(), altitude, bodyShape); } diff --git a/src/main/java/org/orekit/propagation/events/AngularSeparationDetector.java b/src/main/java/org/orekit/propagation/events/AngularSeparationDetector.java index c7d4f34f09..229df646ad 100644 --- a/src/main/java/org/orekit/propagation/events/AngularSeparationDetector.java +++ b/src/main/java/org/orekit/propagation/events/AngularSeparationDetector.java @@ -58,7 +58,7 @@ public class AngularSeparationDetector extends AbstractDetector 60.0, 1.0e-3, 100, new StopOnDecreasing(), + this(AdaptableInterval.of(60.), 1.0e-3, 100, new StopOnDecreasing(), beacon, observer, proximityAngle); } diff --git a/src/main/java/org/orekit/propagation/events/AngularSeparationFromSatelliteDetector.java b/src/main/java/org/orekit/propagation/events/AngularSeparationFromSatelliteDetector.java index eca6b9d8dd..fd0469eace 100644 --- a/src/main/java/org/orekit/propagation/events/AngularSeparationFromSatelliteDetector.java +++ b/src/main/java/org/orekit/propagation/events/AngularSeparationFromSatelliteDetector.java @@ -58,7 +58,7 @@ public class AngularSeparationFromSatelliteDetector extends AbstractDetector DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, new StopOnDecreasing(), + this(AdaptableInterval.of(DEFAULT_MAXCHECK), DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, new StopOnDecreasing(), primaryObject, secondaryObject, proximityAngle); } diff --git a/src/main/java/org/orekit/propagation/events/ApsideDetector.java b/src/main/java/org/orekit/propagation/events/ApsideDetector.java index a3d78e306b..03046fb12e 100644 --- a/src/main/java/org/orekit/propagation/events/ApsideDetector.java +++ b/src/main/java/org/orekit/propagation/events/ApsideDetector.java @@ -40,6 +40,16 @@ */ public class ApsideDetector extends AbstractDetector { + /** Build a new instance. + *

          The Keplerian period is used only to set an upper bound for the + * max check interval to period/3 and to set the convergence threshold.

          + * @param keplerianPeriod estimate of the Keplerian period + * @since 12.1 + */ + public ApsideDetector(final double keplerianPeriod) { + super(keplerianPeriod / 3, 1e-13 * keplerianPeriod, DEFAULT_MAX_ITER, new StopOnIncreasing()); + } + /** Build a new instance. *

          The orbit is used only to set an upper bound for the * max check interval to period/3 and to set the convergence @@ -47,7 +57,7 @@ public class ApsideDetector extends AbstractDetector { * @param orbit initial orbit */ public ApsideDetector(final Orbit orbit) { - this(1.0e-13 * orbit.getKeplerianPeriod(), orbit); + this(orbit.getKeplerianPeriod()); } /** Build a new instance. @@ -57,15 +67,12 @@ public ApsideDetector(final Orbit orbit) { * @param orbit initial orbit */ public ApsideDetector(final double threshold, final Orbit orbit) { - super(orbit.getKeplerianPeriod() / 3, threshold, - DEFAULT_MAX_ITER, new StopOnIncreasing()); + super(orbit.getKeplerianPeriod() / 3, threshold, DEFAULT_MAX_ITER, new StopOnIncreasing()); } - /** Protected constructor with full parameters. + /** Public 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. + * This constructor is public because otherwise all accessible ones would require an orbit. *

          * @param maxCheck maximum checking interval * @param threshold convergence threshold (s) @@ -73,8 +80,8 @@ public ApsideDetector(final double threshold, final Orbit orbit) { * @param handler event handler to call at event occurrences * @since 6.1 */ - protected ApsideDetector(final AdaptableInterval maxCheck, final double threshold, - final int maxIter, final EventHandler handler) { + public ApsideDetector(final AdaptableInterval maxCheck, final double threshold, + final int maxIter, final EventHandler handler) { super(maxCheck, threshold, maxIter, handler); } diff --git a/src/main/java/org/orekit/propagation/events/BetaAngleDetector.java b/src/main/java/org/orekit/propagation/events/BetaAngleDetector.java new file mode 100644 index 0000000000..a2b30793c2 --- /dev/null +++ b/src/main/java/org/orekit/propagation/events/BetaAngleDetector.java @@ -0,0 +1,186 @@ +/* Copyright 2002-2024 Joseph Reed + * 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. + * Joseph Reed 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.geometry.euclidean.threed.Vector3D; +import org.hipparchus.ode.events.Action; +import org.hipparchus.util.MathUtils; +import org.orekit.annotation.DefaultDataContext; +import org.orekit.bodies.CelestialBodyFactory; +import org.orekit.frames.Frame; +import org.orekit.frames.FramesFactory; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.events.handlers.EventHandler; +import org.orekit.propagation.events.handlers.StopOnEvent; +import org.orekit.utils.PVCoordinatesProvider; +import org.orekit.utils.TimeStampedPVCoordinates; + +/** Finder for beta angle crossing events. + *

          Locate events when the beta angle (the angle between the orbit plane and the celestial body) + * crosses a threshold. The {@link #g(SpacecraftState)} function is negative when the beta angle + * is above the threshold and positive when the beta angle is below the threshold.

          + *

          The inertial frame provided must have it's origin centered at the satellite's orbit plane. The + * beta angle is computed as the angle between the celestial body's position in this frame with the + * satellite's orbital momentum vector.

          + *

          The default implementation behavior is to {@link Action#STOP stop} + * propagation at the first event date occurrence. This can be changed by calling + * {@link #withHandler(EventHandler)} after construction.

          + * @see org.orekit.propagation.Propagator#addEventDetector(EventDetector) + * @author Joe Reed + * @since 12.1 + */ +public class BetaAngleDetector extends AbstractDetector { + /** Beta angle crossing threshold. */ + private final double betaAngleThreshold; + /** Coordinate provider for the celestial body. */ + private final PVCoordinatesProvider celestialBodyProvider; + /** Inertial frame in which beta angle is calculated. */ + private final Frame inertialFrame; + + /**Solar beta angle constructor. + *

          This method uses the default data context, assigns the sun as the celestial + * body and uses GCRF as the inertial frame.

          + * @param betaAngleThreshold beta angle threshold (radians) + */ + @DefaultDataContext + public BetaAngleDetector(final double betaAngleThreshold) { + this(betaAngleThreshold, CelestialBodyFactory.getSun(), FramesFactory.getGCRF()); + } + + /** Class constructor. + * @param betaAngleThreshold beta angle threshold (radians) + * @param celestialBodyProvider coordinate provider for the celestial provider + * @param inertialFrame inertial frame in which to compute the beta angle + */ + public BetaAngleDetector(final double betaAngleThreshold, final PVCoordinatesProvider celestialBodyProvider, + final Frame inertialFrame) { + this(AdaptableInterval.of(DEFAULT_MAXCHECK), DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, new StopOnEvent(), + betaAngleThreshold, celestialBodyProvider, inertialFrame); + } + + /** 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 betaAngleThreshold beta angle threshold (radians) + * @param celestialBodyProvider coordinate provider for the celestial provider + * @param inertialFrame inertial frame in which to compute the beta angle + */ + protected BetaAngleDetector(final AdaptableInterval maxCheck, final double threshold, + final int maxIter, final EventHandler handler, + final double betaAngleThreshold, final PVCoordinatesProvider celestialBodyProvider, + final Frame inertialFrame) { + super(maxCheck, threshold, maxIter, handler); + this.betaAngleThreshold = betaAngleThreshold; + this.celestialBodyProvider = celestialBodyProvider; + this.inertialFrame = inertialFrame; + } + + /** Coordinate provider for the celestial body. + * @return celestial body's coordinate provider + */ + public PVCoordinatesProvider getCelestialBodyProvider() { + return this.celestialBodyProvider; + } + + /** The inertial frame in which beta angle is computed. + * @return the inertial frame + */ + public Frame getInertialFrame() { + return this.inertialFrame; + } + + /** The beta angle threshold (radians). + * @return the beta angle threshold (radians) + */ + public double getBetaAngleThreshold() { + return this.betaAngleThreshold; + } + + /** Create a new instance with the provided coordinate provider. + *

          This method does not change the current instance.

          + * @param newProvider the new coordinate provider + * @return the new detector instance + */ + public BetaAngleDetector withCelestialProvider(final PVCoordinatesProvider newProvider) { + return new BetaAngleDetector(getMaxCheckInterval(), getThreshold(), getMaxIterationCount(), + getHandler(), getBetaAngleThreshold(), newProvider, getInertialFrame()); + } + + /** Create a new instance with the provided beta angle threshold. + *

          This method does not change the current instance.

          + * @param newBetaAngleThreshold the beta angle threshold (radians) + * @return the new detector instance + */ + public BetaAngleDetector withBetaThreshold(final double newBetaAngleThreshold) { + return new BetaAngleDetector(getMaxCheckInterval(), getThreshold(), getMaxIterationCount(), + getHandler(), newBetaAngleThreshold, getCelestialBodyProvider(), getInertialFrame()); + } + + /** Create a new instance with the provided inertial frame. + *

          This method does not change the current instance.

          + * @param newFrame the inertial frame + * @return the new detector instance + */ + public BetaAngleDetector withInertialFrame(final Frame newFrame) { + return new BetaAngleDetector(getMaxCheckInterval(), getThreshold(), getMaxIterationCount(), + getHandler(), getBetaAngleThreshold(), getCelestialBodyProvider(), newFrame); + } + + /** {@inheritDoc} */ + @Override + public double g(final SpacecraftState s) { + final double beta = calculateBetaAngle(s, celestialBodyProvider, inertialFrame); + return betaAngleThreshold - beta; + } + + /**Calculate the beta angle between the orbit plane and the celestial body. + *

          This method computes the beta angle using the frame from the spacecraft state.

          + * @param state spacecraft state + * @param celestialBodyProvider celestial body coordinate provider + * @return the beta angle (radians) + */ + public static double calculateBetaAngle(final SpacecraftState state, + final PVCoordinatesProvider celestialBodyProvider) { + return calculateBetaAngle(state, celestialBodyProvider, state.getFrame()); + } + + /**Calculate the beta angle between the orbit plane and the celestial body. + * @param state spacecraft state + * @param celestialBodyProvider celestial body coordinate provider + * @param frame inertial frame in which beta angle will be computed + * @return the beta angle (radians) + */ + public static double calculateBetaAngle(final SpacecraftState state, + final PVCoordinatesProvider celestialBodyProvider, final Frame frame) { + final Vector3D celestialP = celestialBodyProvider.getPosition(state.getDate(), frame); + final TimeStampedPVCoordinates pv = state.getPVCoordinates(frame); + return MathUtils.SEMI_PI - Vector3D.angle(celestialP, pv.getMomentum()); + } + + /** {@inheritDoc} */ + @Override + protected BetaAngleDetector create(final AdaptableInterval newMaxCheck, final double newThreshold, + final int newMaxIter, final EventHandler newHandler) { + return new BetaAngleDetector(newMaxCheck, newThreshold, newMaxIter, newHandler, + getBetaAngleThreshold(), getCelestialBodyProvider(), getInertialFrame()); + } +} diff --git a/src/main/java/org/orekit/propagation/events/CylindricalShadowEclipseDetector.java b/src/main/java/org/orekit/propagation/events/CylindricalShadowEclipseDetector.java new file mode 100644 index 0000000000..bf2f8f25bc --- /dev/null +++ b/src/main/java/org/orekit/propagation/events/CylindricalShadowEclipseDetector.java @@ -0,0 +1,101 @@ +/* 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.geometry.euclidean.threed.Vector3D; +import org.hipparchus.util.FastMath; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.events.handlers.EventHandler; +import org.orekit.utils.PVCoordinatesProvider; + +/** + * Event detector for eclipses from a single, infinitely-distant light source, occulted by a spherical central body. + * The shadow region is cylindrical, a model less accurate than a conical one but more computationally-performant. + *

          + * The so-called g function is negative in eclipse, positive otherwise. + *

          + * @author Romain Serra + * @see EclipseDetector + * @since 12.1 + */ +public class CylindricalShadowEclipseDetector extends AbstractDetector { + + /** Direction provider for the occulted light source i.e. the Sun (whose shadow is approximated as if the body was infinitely distant). */ + private final PVCoordinatesProvider sun; + + /** 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; + + /** + * Constructor. + * @param sun light source provider (infinitely distant) + * @param occultingBodyRadius occulting body radius + * @param maxCheck maximum check for event detection + * @param threshold threshold for event detection + * @param maxIter maximum iteration for event detection + * @param handler event handler + */ + public CylindricalShadowEclipseDetector(final PVCoordinatesProvider sun, + final double occultingBodyRadius, + final AdaptableInterval maxCheck, final double threshold, + final int maxIter, final EventHandler handler) { + super(maxCheck, threshold, maxIter, handler); + this.sun = sun; + this.occultingBodyRadius = FastMath.abs(occultingBodyRadius); + } + + /** + * Constructor with default detection settings. + * @param sun light source provider + * @param occultingBodyRadius occulting body radius + * @param handler event handler + */ + public CylindricalShadowEclipseDetector(final PVCoordinatesProvider sun, + final double occultingBodyRadius, final EventHandler handler) { + this(sun, occultingBodyRadius, AdaptableInterval.of(DEFAULT_MAXCHECK), DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, handler); + } + + /** + * Getter for occulting body radius. + * @return radius + */ + public double getOccultingBodyRadius() { + return occultingBodyRadius; + } + + /** {@inheritDoc} */ + @Override + public double g(final SpacecraftState s) { + final Vector3D sunDirection = sun.getPosition(s.getDate(), s.getFrame()).normalize(); + final Vector3D position = s.getPosition(); + final double dotProduct = position.dotProduct(sunDirection); + if (dotProduct >= 0.) { + return position.getNorm() / occultingBodyRadius; + } else { + final double distanceToCylinderAxis = (position.subtract(sunDirection.scalarMultiply(dotProduct))).getNorm(); + return distanceToCylinderAxis / occultingBodyRadius - 1.; + } + } + + /** {@inheritDoc} */ + @Override + protected CylindricalShadowEclipseDetector create(final AdaptableInterval newMaxCheck, final double newThreshold, + final int newMaxIter, final EventHandler newHandler) { + return new CylindricalShadowEclipseDetector(sun, occultingBodyRadius, newMaxCheck, newThreshold, newMaxIter, newHandler); + } +} diff --git a/src/main/java/org/orekit/propagation/events/DateDetector.java b/src/main/java/org/orekit/propagation/events/DateDetector.java index 04dc1c0063..16d3dd67e1 100644 --- a/src/main/java/org/orekit/propagation/events/DateDetector.java +++ b/src/main/java/org/orekit/propagation/events/DateDetector.java @@ -83,7 +83,7 @@ public class DateDetector extends AbstractDetector implements Time * @since 12.0 */ public DateDetector(final TimeStamped... dates) { - this(s -> DEFAULT_MAX_CHECK, DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, new StopOnEvent(), DEFAULT_MIN_GAP, dates); + this(AdaptableInterval.of(DEFAULT_MAX_CHECK), DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, new StopOnEvent(), DEFAULT_MIN_GAP, dates); } /** Protected constructor with full parameters. diff --git a/src/main/java/org/orekit/propagation/events/EclipseDetector.java b/src/main/java/org/orekit/propagation/events/EclipseDetector.java index 7a062caa59..61d8a00d69 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(s -> DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, + this(AdaptableInterval.of(DEFAULT_MAXCHECK), DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, new StopOnIncreasing(), occultationEngine, 0.0, true); } diff --git a/src/main/java/org/orekit/propagation/events/ElevationDetector.java b/src/main/java/org/orekit/propagation/events/ElevationDetector.java index cf427161c1..fdee7fd043 100644 --- a/src/main/java/org/orekit/propagation/events/ElevationDetector.java +++ b/src/main/java/org/orekit/propagation/events/ElevationDetector.java @@ -71,7 +71,7 @@ public ElevationDetector(final TopocentricFrame topo) { * Creates an instance of Elevation detector based on passed in topocentric frame * and overrides of default maximal checking interval and convergence threshold values. * @param maxCheck maximum checking interval (s) - * @param threshold maximum divergence threshold (s) + * @param threshold maximum convergence threshold (s) * @param topo reference to a topocentric model * @see #withConstantElevation(double) * @see #withElevationMask(ElevationMask) @@ -79,7 +79,24 @@ public ElevationDetector(final TopocentricFrame topo) { */ public ElevationDetector(final double maxCheck, final double threshold, final TopocentricFrame topo) { - this(s -> maxCheck, threshold, DEFAULT_MAX_ITER, + this(AdaptableInterval.of(maxCheck), threshold, topo); + } + + /** + * Creates an instance of Elevation detector based on passed in topocentric frame + * and overrides of default maximal checking interval and convergence threshold values. + * @param maxCheck maximum checking adaptable interval + * @param threshold maximum convergence threshold (s) + * @param topo reference to a topocentric model + * @see org.orekit.propagation.events.intervals.ElevationDetectionAdaptableIntervalFactory + * @see #withConstantElevation(double) + * @see #withElevationMask(ElevationMask) + * @see #withRefraction(AtmosphericRefractionModel) + * @since 12.1 + */ + public ElevationDetector(final AdaptableInterval maxCheck, final double threshold, + final TopocentricFrame topo) { + this(maxCheck, threshold, DEFAULT_MAX_ITER, new StopOnDecreasing(), 0.0, null, null, topo); } diff --git a/src/main/java/org/orekit/propagation/events/ElevationExtremumDetector.java b/src/main/java/org/orekit/propagation/events/ElevationExtremumDetector.java index 22fa1e50a9..45df8b362c 100644 --- a/src/main/java/org/orekit/propagation/events/ElevationExtremumDetector.java +++ b/src/main/java/org/orekit/propagation/events/ElevationExtremumDetector.java @@ -18,8 +18,8 @@ import org.hipparchus.analysis.differentiation.UnivariateDerivative1; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.orekit.frames.KinematicTransform; import org.orekit.frames.TopocentricFrame; -import org.orekit.frames.Transform; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.events.handlers.EventHandler; import org.orekit.propagation.events.handlers.StopOnIncreasing; @@ -63,7 +63,7 @@ public ElevationExtremumDetector(final TopocentricFrame topo) { */ public ElevationExtremumDetector(final double maxCheck, final double threshold, final TopocentricFrame topo) { - this(s -> maxCheck, threshold, DEFAULT_MAX_ITER, new StopOnIncreasing(), + this(AdaptableInterval.of(maxCheck), threshold, DEFAULT_MAX_ITER, new StopOnIncreasing(), topo); } @@ -119,9 +119,9 @@ public double getElevation(final SpacecraftState s) { */ public double g(final SpacecraftState s) { - // get position, velocity acceleration of spacecraft in topocentric frame - final Transform inertToTopo = s.getFrame().getTransformTo(topo, s.getDate()); - final TimeStampedPVCoordinates pvTopo = inertToTopo.transformPVCoordinates(s.getPVCoordinates()); + // get position, velocity of spacecraft in topocentric frame + final KinematicTransform inertToTopo = s.getFrame().getKinematicTransformTo(topo, s.getDate()); + final TimeStampedPVCoordinates pvTopo = inertToTopo.transformOnlyPV(s.getPVCoordinates()); // convert the coordinates to UnivariateDerivative1 based vector // instead of having vector position, then vector velocity then vector acceleration diff --git a/src/main/java/org/orekit/propagation/events/ExtremumApproachDetector.java b/src/main/java/org/orekit/propagation/events/ExtremumApproachDetector.java index 06add61ed8..6f28fe63a4 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 DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, new StopOnIncreasing(), secondaryPVProvider); + this(AdaptableInterval.of(DEFAULT_MAXCHECK), DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, new StopOnIncreasing(), secondaryPVProvider); } /** @@ -128,11 +128,25 @@ public double g(final SpacecraftState s) { * Compute the relative PV between primary and secondary objects. * * @param s Spacecraft state. + * * @return Relative position between primary (=s) and secondaryPVProvider. + * + * @deprecated The output type of this method shall be modified in the future to improve code efficiency (though it will + * still give access to the relative position and velocity) */ + @Deprecated public PVCoordinates computeDeltaPV(final SpacecraftState s) { - return new PVCoordinates(s.getPVCoordinates(), - secondaryPVProvider.getPVCoordinates(s.getDate(), s.getFrame())); + final Vector3D primaryPos = s.getPosition(); + final Vector3D primaryVel = s.getPVCoordinates().getVelocity(); + + final PVCoordinates secondaryPV = secondaryPVProvider.getPVCoordinates(s.getDate(), s.getFrame()); + final Vector3D secondaryPos = secondaryPV.getPosition(); + final Vector3D secondaryVel = secondaryPV.getVelocity(); + + final Vector3D relativePos = secondaryPos.subtract(primaryPos); + final Vector3D relativeVel = secondaryVel.subtract(primaryVel); + + return new PVCoordinates(relativePos, relativeVel); } /** {@inheritDoc} */ diff --git a/src/main/java/org/orekit/propagation/events/FieldAbstractDetector.java b/src/main/java/org/orekit/propagation/events/FieldAbstractDetector.java index 3198ef86e9..3ca0de65d7 100644 --- a/src/main/java/org/orekit/propagation/events/FieldAbstractDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldAbstractDetector.java @@ -118,7 +118,7 @@ public T getThreshold() { * @since 12.0 */ public D withMaxCheck(final double newMaxCheck) { - return withMaxCheck(s -> newMaxCheck); + return withMaxCheck(FieldAdaptableInterval.of(newMaxCheck)); } /** diff --git a/src/main/java/org/orekit/propagation/events/FieldAdaptableInterval.java b/src/main/java/org/orekit/propagation/events/FieldAdaptableInterval.java index 47c3aa06ce..eb2208698e 100644 --- a/src/main/java/org/orekit/propagation/events/FieldAdaptableInterval.java +++ b/src/main/java/org/orekit/propagation/events/FieldAdaptableInterval.java @@ -36,4 +36,14 @@ public interface FieldAdaptableInterval> { */ double currentInterval(FieldSpacecraftState state); + /** + * Method creating a constant interval provider. + * @param field type + * @param constantInterval value of constant interval + * @return adaptable interval ready to be added to an event detector + * @since 12.1 + */ + static > FieldAdaptableInterval of(final double constantInterval) { + return state -> constantInterval; + } } diff --git a/src/main/java/org/orekit/propagation/events/FieldAltitudeDetector.java b/src/main/java/org/orekit/propagation/events/FieldAltitudeDetector.java index 7205d2ae09..08523ca909 100644 --- a/src/main/java/org/orekit/propagation/events/FieldAltitudeDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldAltitudeDetector.java @@ -53,8 +53,8 @@ public class FieldAltitudeDetector> extends Fi * @param bodyShape body shape with respect to which altitude should be evaluated */ public FieldAltitudeDetector(final T altitude, final BodyShape bodyShape) { - this(altitude.getField().getZero().add(DEFAULT_MAXCHECK), - altitude.getField().getZero().add(DEFAULT_THRESHOLD), + this(altitude.getField().getZero().newInstance(DEFAULT_MAXCHECK), + altitude.getField().getZero().newInstance(DEFAULT_THRESHOLD), altitude, bodyShape); } @@ -71,7 +71,7 @@ public FieldAltitudeDetector(final T altitude, final BodyShape bodyShape) { public FieldAltitudeDetector(final T maxCheck, final T altitude, final BodyShape bodyShape) { - this(maxCheck, altitude.getField().getZero().add(DEFAULT_THRESHOLD), altitude, bodyShape); + this(maxCheck, altitude.getField().getZero().newInstance(DEFAULT_THRESHOLD), altitude, bodyShape); } /** Build a new altitude detector. @@ -90,7 +90,7 @@ public FieldAltitudeDetector(final T maxCheck, final T threshold, final T altitude, final BodyShape bodyShape) { - this(s -> maxCheck.getReal(), threshold, DEFAULT_MAX_ITER, new FieldStopOnDecreasing(), + this(FieldAdaptableInterval.of(maxCheck.getReal()), threshold, DEFAULT_MAX_ITER, new FieldStopOnDecreasing(), altitude, bodyShape); } diff --git a/src/main/java/org/orekit/propagation/events/FieldApsideDetector.java b/src/main/java/org/orekit/propagation/events/FieldApsideDetector.java index 88263c5ec6..858f6e6513 100644 --- a/src/main/java/org/orekit/propagation/events/FieldApsideDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldApsideDetector.java @@ -42,6 +42,17 @@ */ public class FieldApsideDetector> extends FieldAbstractDetector, T> { + /** Build a new instance. + *

          The Keplerian period is used only to set an upper bound for the + * max check interval to period/3 and to set the convergence threshold.

          + * @param keplerianPeriod estimate of the Keplerian period + * @since 12.1 + */ + public FieldApsideDetector(final T keplerianPeriod) { + super(FieldAdaptableInterval.of(keplerianPeriod.divide(3).getReal()), keplerianPeriod.multiply(1e-13), + DEFAULT_MAX_ITER, new FieldStopOnIncreasing<>()); + } + /** Build a new instance. *

          The orbit is used only to set an upper bound for the * max check interval to period/3 and to set the convergence @@ -49,7 +60,7 @@ public class FieldApsideDetector> extends Fiel * @param orbit initial orbit */ public FieldApsideDetector(final FieldOrbit orbit) { - this(orbit.getKeplerianPeriod().multiply(1.0e-13), orbit); + this(orbit.getKeplerianPeriod()); } /** Build a new instance. @@ -59,23 +70,21 @@ public FieldApsideDetector(final FieldOrbit orbit) { * @param orbit initial orbit */ public FieldApsideDetector(final T threshold, final FieldOrbit orbit) { - super(s -> orbit.getKeplerianPeriod().divide(3).getReal(), threshold, + super(FieldAdaptableInterval.of(orbit.getKeplerianPeriod().divide(3).getReal()), threshold, DEFAULT_MAX_ITER, new FieldStopOnIncreasing<>()); } /** 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. + * This constructor is public because otherwise all accessible ones would require an orbit. *

          * @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 */ - protected FieldApsideDetector(final FieldAdaptableInterval maxCheck, final T threshold, - final int maxIter, final FieldEventHandler handler) { + public FieldApsideDetector(final FieldAdaptableInterval maxCheck, final T threshold, + final int maxIter, final FieldEventHandler handler) { super(maxCheck, threshold, maxIter, handler); } diff --git a/src/main/java/org/orekit/propagation/events/FieldBetaAngleDetector.java b/src/main/java/org/orekit/propagation/events/FieldBetaAngleDetector.java new file mode 100644 index 0000000000..29210aa352 --- /dev/null +++ b/src/main/java/org/orekit/propagation/events/FieldBetaAngleDetector.java @@ -0,0 +1,195 @@ +/* Copyright 2002-2024 Joseph Reed + * 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. + * Joseph Reed 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.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.ode.events.Action; +import org.hipparchus.util.MathUtils; +import org.orekit.annotation.DefaultDataContext; +import org.orekit.bodies.CelestialBodyFactory; +import org.orekit.frames.Frame; +import org.orekit.frames.FramesFactory; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.events.handlers.FieldEventHandler; +import org.orekit.propagation.events.handlers.FieldStopOnEvent; +import org.orekit.utils.FieldPVCoordinatesProvider; +import org.orekit.utils.TimeStampedFieldPVCoordinates; + +/** Finder for beta angle crossing events. + *

          Locate events when the beta angle (the angle between the orbit plane and the celestial body) + * crosses a threshold. The {@link #g(FieldSpacecraftState)} function is negative when the beta angle + * is above the threshold and positive when the beta angle is below the threshold.

          + *

          The inertial frame provided must have it's origin centered at the satellite's orbit plane. The + * beta angle is computed as the angle between the celestial body's position in this frame with the + * satellite's orbital momentum vector.

          + *

          The default implementation behavior is to {@link Action#STOP stop} + * propagation at the first event date occurrence. This can be changed by calling + * {@link #withHandler(FieldEventHandler)} after construction.

          + * @see org.orekit.propagation.Propagator#addEventDetector(EventDetector) + * @param The field type + * @author Joe Reed + * @since 12.1 + */ +public class FieldBetaAngleDetector> extends FieldAbstractDetector, T> { + /** Beta angle crossing threshold. */ + private final T betaAngleThreshold; + /** Coordinate provider for the celestial body. */ + private final FieldPVCoordinatesProvider celestialBodyProvider; + /** Inertial frame in which beta angle is calculated. */ + private final Frame inertialFrame; + + /**Solar beta angle constructor. + *

          This method uses the default data context, assigns the sun as the celestial + * body and uses GCRF as the inertial frame.

          + * @param betaAngleThreshold beta angle threshold (radians) + */ + @DefaultDataContext + public FieldBetaAngleDetector(final T betaAngleThreshold) { + this(betaAngleThreshold.getField(), betaAngleThreshold, + CelestialBodyFactory.getSun().toFieldPVCoordinatesProvider(betaAngleThreshold.getField()), + FramesFactory.getGCRF()); + } + + /** Class constructor. + * @param field the field instance + * @param betaAngleThreshold beta angle threshold (radians) + * @param celestialBodyProvider coordinate provider for the celestial provider + * @param inertialFrame inertial frame in which to compute the beta angle + */ + public FieldBetaAngleDetector(final Field field, final T betaAngleThreshold, + final FieldPVCoordinatesProvider celestialBodyProvider, + final Frame inertialFrame) { + this(FieldAdaptableInterval.of(DEFAULT_MAXCHECK), field.getZero().newInstance(DEFAULT_THRESHOLD), DEFAULT_MAX_ITER, + new FieldStopOnEvent<>(), betaAngleThreshold, celestialBodyProvider, inertialFrame); + } + + /** 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 betaAngleThreshold beta angle threshold (radians) + * @param celestialBodyProvider coordinate provider for the celestial provider + * @param inertialFrame inertial frame in which to compute the beta angle + */ + protected FieldBetaAngleDetector(final FieldAdaptableInterval maxCheck, final T threshold, + final int maxIter, final FieldEventHandler handler, + final T betaAngleThreshold, final FieldPVCoordinatesProvider celestialBodyProvider, + final Frame inertialFrame) { + super(maxCheck, threshold, maxIter, handler); + this.betaAngleThreshold = betaAngleThreshold; + this.celestialBodyProvider = celestialBodyProvider; + this.inertialFrame = inertialFrame; + } + + /** Coordinate provider for the celestial body. + * @return celestial body's coordinate provider + */ + public FieldPVCoordinatesProvider getCelestialBodyProvider() { + return this.celestialBodyProvider; + } + + /** The inertial frame in which beta angle is computed. + * @return the inertial frame + */ + public Frame getInertialFrame() { + return this.inertialFrame; + } + + /** The beta angle threshold (radians). + * @return the beta angle threshold (radians) + */ + public T getBetaAngleThreshold() { + return this.betaAngleThreshold; + } + + /** Create a new instance with the provided coordinate provider. + *

          This method does not change the current instance.

          + * @param newProvider the new coordinate provider + * @return the new detector instance + */ + public FieldBetaAngleDetector withCelestialProvider(final FieldPVCoordinatesProvider newProvider) { + return new FieldBetaAngleDetector<>(getMaxCheckInterval(), getThreshold(), getMaxIterationCount(), + getHandler(), getBetaAngleThreshold(), newProvider, getInertialFrame()); + } + + /** Create a new instance with the provided beta angle threshold. + *

          This method does not change the current instance.

          + * @param newBetaAngleThreshold the beta angle threshold + * @return the new detector instance + */ + public FieldBetaAngleDetector withBetaThreshold(final T newBetaAngleThreshold) { + return new FieldBetaAngleDetector<>(getMaxCheckInterval(), getThreshold(), getMaxIterationCount(), + getHandler(), newBetaAngleThreshold, getCelestialBodyProvider(), getInertialFrame()); + } + + /** Create a new instance with the provided inertial frame. + *

          This method does not change the current instance.

          + * @param newFrame the inertial frame + * @return the new detector instance + */ + public FieldBetaAngleDetector withInertialFrame(final Frame newFrame) { + return new FieldBetaAngleDetector<>(getMaxCheckInterval(), getThreshold(), getMaxIterationCount(), + getHandler(), getBetaAngleThreshold(), getCelestialBodyProvider(), newFrame); + } + + /** {@inheritDoc} */ + @Override + public T g(final FieldSpacecraftState s) { + final T beta = calculateBetaAngle(s, celestialBodyProvider, inertialFrame); + return betaAngleThreshold.subtract(beta); + } + + /**Calculate the beta angle between the orbit plane and the celestial body. + *

          This method computes the beta angle using the frame from the spacecraft state.

          + * @param state spacecraft state + * @param celestialBodyProvider celestial body coordinate provider + * @param The field type + * @return the beta angle (radians) + */ + public static > T calculateBetaAngle(final FieldSpacecraftState state, + final FieldPVCoordinatesProvider celestialBodyProvider) { + return calculateBetaAngle(state, celestialBodyProvider, state.getFrame()); + } + + /**Calculate the beta angle between the orbit plane and the celestial body. + * @param state spacecraft state + * @param celestialBodyProvider celestial body coordinate provider + * @param frame inertial frame in which beta angle will be computed + * @param The field type + * @return the beta angle (radians) + */ + public static > T calculateBetaAngle(final FieldSpacecraftState state, + final FieldPVCoordinatesProvider celestialBodyProvider, final Frame frame) { + final FieldVector3D celestialP = celestialBodyProvider.getPosition(state.getDate(), frame); + final TimeStampedFieldPVCoordinates pv = state.getPVCoordinates(frame); + return FieldVector3D.angle(celestialP, pv.getMomentum()).negate().add(MathUtils.SEMI_PI); + } + + /** {@inheritDoc} */ + @Override + protected FieldBetaAngleDetector create(final FieldAdaptableInterval newMaxCheck, final T newThreshold, + final int newMaxIter, final FieldEventHandler newHandler) { + return new FieldBetaAngleDetector<>(newMaxCheck, newThreshold, newMaxIter, newHandler, + getBetaAngleThreshold(), getCelestialBodyProvider(), getInertialFrame()); + } +} diff --git a/src/main/java/org/orekit/propagation/events/FieldCylindricalShadowEclipseDetector.java b/src/main/java/org/orekit/propagation/events/FieldCylindricalShadowEclipseDetector.java new file mode 100644 index 0000000000..db206828c7 --- /dev/null +++ b/src/main/java/org/orekit/propagation/events/FieldCylindricalShadowEclipseDetector.java @@ -0,0 +1,105 @@ +/* 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.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.util.FastMath; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.events.handlers.FieldEventHandler; +import org.orekit.utils.ExtendedPositionProvider; + +/** + * Event detector for eclipses from a single, infinitely-distant light source, occulted by a spherical central body. + * The shadow region is cylindrical, a model less accurate than a conical one but more computationally-performant. + *

          + * The so-called g function is negative in eclipse, positive otherwise. + *

          + * @author Romain Serra + * @see FieldEclipseDetector + * @see CylindricalShadowEclipseDetector + * @since 12.1 + */ +public class FieldCylindricalShadowEclipseDetector> + extends FieldAbstractDetector, T> { + + /** Direction provider for the occulted light source i.e. the Sun (whose shadow is approximated as if the body was infinitely distant). */ + private final ExtendedPositionProvider sun; + + /** 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 T occultingBodyRadius; + + /** + * Constructor. + * @param sun light source provider (infinitely distant) + * @param occultingBodyRadius occulting body radius + * @param maxCheck maximum check for event detection + * @param threshold threshold for event detection + * @param maxIter maximum iteration for event detection + * @param handler event handler + */ + public FieldCylindricalShadowEclipseDetector(final ExtendedPositionProvider sun, + final T occultingBodyRadius, + final FieldAdaptableInterval maxCheck, final T threshold, + final int maxIter, final FieldEventHandler handler) { + super(maxCheck, threshold, maxIter, handler); + this.sun = sun; + this.occultingBodyRadius = FastMath.abs(occultingBodyRadius); + } + + /** + * Constructor with default detection settings. + * @param sun light source provider + * @param occultingBodyRadius occulting body radius + * @param handler event handler + */ + 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); + } + + /** + * Getter for occulting body radius. + * @return radius + */ + public T getOccultingBodyRadius() { + return occultingBodyRadius; + } + + /** {@inheritDoc} */ + @Override + public T g(final FieldSpacecraftState s) { + final FieldVector3D sunDirection = sun.getPosition(s.getDate(), s.getFrame()).normalize(); + final FieldVector3D position = s.getPosition(); + final T dotProduct = position.dotProduct(sunDirection); + if (dotProduct.getReal() >= 0.) { + return position.getNorm().divide(occultingBodyRadius); + } else { + final T distanceToCylinderAxis = (position.subtract(sunDirection.scalarMultiply(dotProduct))).getNorm(); + return distanceToCylinderAxis.divide(occultingBodyRadius).subtract(1.); + } + } + + /** {@inheritDoc} */ + @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); + } +} diff --git a/src/main/java/org/orekit/propagation/events/FieldDateDetector.java b/src/main/java/org/orekit/propagation/events/FieldDateDetector.java index d267a1a787..5e01b35ee0 100644 --- a/src/main/java/org/orekit/propagation/events/FieldDateDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldDateDetector.java @@ -89,7 +89,7 @@ public class FieldDateDetector> extends FieldA */ @SafeVarargs public FieldDateDetector(final Field field, final FieldTimeStamped... dates) { - this(s -> DEFAULT_MAX_CHECK, field.getZero().newInstance(DEFAULT_THRESHOLD), + this(FieldAdaptableInterval.of(DEFAULT_MAX_CHECK), field.getZero().newInstance(DEFAULT_THRESHOLD), DEFAULT_MAX_ITER, new FieldStopOnEvent<>(), DEFAULT_MIN_GAP, dates); } @@ -158,7 +158,7 @@ public List> getDates() { public T g(final FieldSpacecraftState s) { gDate = s.getDate(); if (currentIndex < 0) { - return s.getA().getField().getZero().add(-1); + return s.getA().getField().getZero().newInstance(-1); } else { final FieldEventDate event = getClosest(gDate); return event.isgIncrease() ? gDate.durationFrom(event.getDate()) : event.getDate().durationFrom(gDate); diff --git a/src/main/java/org/orekit/propagation/events/FieldEclipseDetector.java b/src/main/java/org/orekit/propagation/events/FieldEclipseDetector.java index 11a5961d36..9aaf3bcd10 100644 --- a/src/main/java/org/orekit/propagation/events/FieldEclipseDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldEclipseDetector.java @@ -75,7 +75,7 @@ public FieldEclipseDetector(final Field field, * @since 12.0 */ public FieldEclipseDetector(final Field field, final OccultationEngine occultationEngine) { - this(s -> DEFAULT_MAXCHECK, field.getZero().newInstance(DEFAULT_THRESHOLD), + this(FieldAdaptableInterval.of(DEFAULT_MAXCHECK), field.getZero().newInstance(DEFAULT_THRESHOLD), DEFAULT_MAX_ITER, new FieldStopOnIncreasing<>(), occultationEngine, field.getZero(), true); } diff --git a/src/main/java/org/orekit/propagation/events/FieldElevationDetector.java b/src/main/java/org/orekit/propagation/events/FieldElevationDetector.java index 0058bdc4e4..f3f1bccba4 100644 --- a/src/main/java/org/orekit/propagation/events/FieldElevationDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldElevationDetector.java @@ -69,8 +69,8 @@ public class FieldElevationDetector> extends F * @see #withRefraction(AtmosphericRefractionModel) */ public FieldElevationDetector(final Field field, final TopocentricFrame topo) { - this(s -> DEFAULT_MAXCHECK, - field.getZero().add(DEFAULT_THRESHOLD), DEFAULT_MAX_ITER, + this(FieldAdaptableInterval.of(DEFAULT_MAXCHECK), + field.getZero().newInstance(DEFAULT_THRESHOLD), DEFAULT_MAX_ITER, new FieldStopOnDecreasing<>(), 0.0, null, null, topo); } @@ -86,7 +86,7 @@ public FieldElevationDetector(final Field field, final TopocentricFrame topo) * @see #withRefraction(AtmosphericRefractionModel) */ public FieldElevationDetector(final T maxCheck, final T threshold, final TopocentricFrame topo) { - this(s -> maxCheck.getReal(), threshold, DEFAULT_MAX_ITER, + this(FieldAdaptableInterval.of(maxCheck.getReal()), threshold, DEFAULT_MAX_ITER, new FieldStopOnDecreasing<>(), 0.0, null, null, topo); } diff --git a/src/main/java/org/orekit/propagation/events/FieldElevationExtremumDetector.java b/src/main/java/org/orekit/propagation/events/FieldElevationExtremumDetector.java index 508e9b943f..2114551e3e 100644 --- a/src/main/java/org/orekit/propagation/events/FieldElevationExtremumDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldElevationExtremumDetector.java @@ -21,7 +21,7 @@ import org.hipparchus.analysis.differentiation.FieldUnivariateDerivative1; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.ode.events.FieldEventSlopeFilter; -import org.orekit.frames.FieldTransform; +import org.orekit.frames.FieldKinematicTransform; import org.orekit.frames.TopocentricFrame; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.events.handlers.FieldEventHandler; @@ -71,7 +71,7 @@ public FieldElevationExtremumDetector(final Field field, final TopocentricFra */ public FieldElevationExtremumDetector(final T maxCheck, final T threshold, final TopocentricFrame topo) { - this(s -> maxCheck.getReal(), threshold, DEFAULT_MAX_ITER, new FieldStopOnIncreasing<>(), + this(FieldAdaptableInterval.of(maxCheck.getReal()), threshold, DEFAULT_MAX_ITER, new FieldStopOnIncreasing<>(), topo); } @@ -128,8 +128,8 @@ public T getElevation(final FieldSpacecraftState s) { public T g(final FieldSpacecraftState s) { // get position, velocity acceleration of spacecraft in topocentric frame - final FieldTransform inertToTopo = s.getFrame().getTransformTo(topo, s.getDate()); - final TimeStampedFieldPVCoordinates pvTopo = inertToTopo.transformPVCoordinates(s.getPVCoordinates()); + final FieldKinematicTransform inertToTopo = s.getFrame().getKinematicTransformTo(topo, s.getDate()); + final TimeStampedFieldPVCoordinates pvTopo = inertToTopo.transformOnlyPV(s.getPVCoordinates()); // convert the coordinates to UnivariateDerivative1 based vector // instead of having vector position, then vector velocity then vector acceleration diff --git a/src/main/java/org/orekit/propagation/events/FieldEventState.java b/src/main/java/org/orekit/propagation/events/FieldEventState.java index a36189b69b..2386db1fcc 100644 --- a/src/main/java/org/orekit/propagation/events/FieldEventState.java +++ b/src/main/java/org/orekit/propagation/events/FieldEventState.java @@ -399,9 +399,9 @@ private boolean findRoot(final FieldOrekitStepInterpolator interpolator, final Interval interval = solver.solveInterval(maxIterationCount, f, tbDouble, 0); beforeRootT = date.apply(interval.getRightAbscissa()); - beforeRootG = zero.add(interval.getRightValue()); + beforeRootG = zero.newInstance(interval.getRightValue()); afterRootT = date.apply(interval.getLeftAbscissa()); - afterRootG = zero.add(interval.getLeftValue()); + afterRootG = zero.newInstance(interval.getLeftValue()); // CHECKSTYLE: stop IllegalCatch check } catch (RuntimeException e) { // CHECKSTYLE: resume IllegalCatch check diff --git a/src/main/java/org/orekit/propagation/events/FieldExtremumApproachDetector.java b/src/main/java/org/orekit/propagation/events/FieldExtremumApproachDetector.java new file mode 100644 index 0000000000..287b8fd938 --- /dev/null +++ b/src/main/java/org/orekit/propagation/events/FieldExtremumApproachDetector.java @@ -0,0 +1,224 @@ +/* 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.events; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.ode.events.Action; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.PropagatorsParallelizer; +import org.orekit.propagation.events.handlers.EventHandler; +import org.orekit.propagation.events.handlers.FieldEventHandler; +import org.orekit.propagation.events.handlers.FieldStopOnIncreasing; +import org.orekit.utils.FieldPVCoordinates; +import org.orekit.utils.FieldPVCoordinatesProvider; +import org.orekit.utils.PVCoordinatesProvider; +import org.orekit.utils.TimeStampedFieldPVCoordinates; +import org.orekit.utils.TimeStampedPVCoordinates; + +/** + * Finder for extremum approach events. + *

          + * This class finds extremum approach events (i.e. closest or farthest approach). + *

          + *

          + * The default implementation behavior is to {@link Action#CONTINUE continue} propagation at farthest approach and to + * {@link Action#STOP stop} propagation at closest approach. This can be changed by calling + * {@link FieldAbstractDetector#withHandler(FieldEventHandler)} after construction (go to the end of the documentation to see + * an example). + *

          + *

          + * As this detector needs two objects (moving relative to each other), it embeds one + * {@link FieldPVCoordinatesProvider fielded coordinates provider} for the secondary object and is registered as an event + * detector in the propagator of the primary object. The secondary object + * {@link FieldPVCoordinatesProvider fielded coordinates provider} will therefore be driven by this detector (and hence by + * the propagator in which this detector is registered). Note that you can also create this detector using a standard + * {@link PVCoordinatesProvider coordinates provider} + *

          + *

          + * In order to avoid infinite recursion, care must be taken to have the secondary object provider being completely + * independent from anything else. In particular, if the provider is a propagator, it should not be run + * together in a {@link PropagatorsParallelizer propagators parallelizer} with the propagator this detector is registered in. + * It is fine however to configure two separate propagators PsA and PsB with similar settings for the secondary object and + * one propagator Pm for the primary object and then use Psa in this detector registered within Pm while Pm and Psb are run + * in the context of a {@link PropagatorsParallelizer propagators parallelizer}. + *

          + *

          + * For efficiency reason during the event search loop, it is recommended to have the secondary provider be an analytical + * propagator or an ephemeris. A numerical propagator as a secondary propagator works but is expected to be computationally + * costly. + *

          + *

          + * Also, it is possible to detect solely one type of event using an {@link EventSlopeFilter event slope filter}. For example + * in order to only detect closest approach, one should type the following : + *

          + *
          {@code
          + * FieldExtremumApproachDetector extremumApproachDetector = new FieldExtremumApproachDetector<>(field, secondaryPVProvider);
          + * FieldEventDetector closeApproachDetector = new FieldEventSlopeFilter<>(extremumApproachDetector, FilterType.TRIGGER_ONLY_INCREASING_EVENTS);
          + *  }
          + * 
          + * + * @author Vincent Cucchietti + * @see org.orekit.propagation.FieldPropagator#addEventDetector(FieldEventDetector) + * @see FieldEventSlopeFilter + * @see FilterType + * @since 11.3 + */ +public class FieldExtremumApproachDetector> + extends FieldAbstractDetector, T> { + + /** + * PVCoordinates provider of the other object with which we want to find out the extremum approach. + */ + private final FieldPVCoordinatesProvider secondaryPVProvider; + + /** + * Constructor with default values. + *

          + * By default, the implemented behavior is to {@link Action#CONTINUE continue} propagation at farthest approach and to + * {@link Action#STOP stop} propagation at closest approach. + *

          + * BEWARE : This constructor will "fieldify" given secondary PV coordinates provider. + * + * @param field field the type of number to use + * @param secondaryPVProvider PVCoordinates provider of the other object with which we want to find out the extremum + * approach. + */ + public FieldExtremumApproachDetector(final Field field, final PVCoordinatesProvider secondaryPVProvider) { + this(field, (FieldPVCoordinatesProvider) (date, frame) -> { + final TimeStampedPVCoordinates timeStampedPV = + secondaryPVProvider.getPVCoordinates(date.toAbsoluteDate(), frame); + return new TimeStampedFieldPVCoordinates<>(field, timeStampedPV); + }); + } + + /** + * Constructor with default values. + *

          + * By default, the implemented behavior is to {@link Action#CONTINUE continue} propagation at farthest approach and to + * {@link Action#STOP stop} propagation at closest approach. + *

          + * + * @param field field the type of number to use + * @param secondaryPVProvider PVCoordinates provider of the other object with which we want to find out the extremum + * approach. + */ + public FieldExtremumApproachDetector(final Field field, final FieldPVCoordinatesProvider secondaryPVProvider) { + this(field.getZero().newInstance(DEFAULT_MAXCHECK), field.getZero().newInstance(DEFAULT_THRESHOLD), DEFAULT_MAX_ITER, + new FieldStopOnIncreasing<>(), secondaryPVProvider); + } + + /** + * Constructor. + *

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

          + * + * @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 secondaryPVProvider PVCoordinates provider of the other object with which we want to find out the extremum + * approach. + * + * @see FieldEventHandler + */ + protected FieldExtremumApproachDetector(final T maxCheck, final T threshold, final int maxIter, + final FieldEventHandler handler, + final FieldPVCoordinatesProvider secondaryPVProvider) { + this(FieldAdaptableInterval.of(maxCheck.getReal()), threshold, maxIter, handler, secondaryPVProvider); + } + + /** + * Constructor. + *

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

          + * + * @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 secondaryPVProvider PVCoordinates provider of the other object with which we want to find out the extremum + * approach. + * + * @see EventHandler + */ + protected FieldExtremumApproachDetector(final FieldAdaptableInterval maxCheck, final T threshold, final int maxIter, + final FieldEventHandler handler, + final FieldPVCoordinatesProvider secondaryPVProvider) { + super(maxCheck, threshold, maxIter, handler); + this.secondaryPVProvider = secondaryPVProvider; + } + + /** + * Compute the relative PV between primary and secondary objects. + * + * @param s Spacecraft state. + * + * @return Relative position between primary (=s) and secondaryPVProvider. + * + * @deprecated The output type of this method shall be modified in the future to improve code efficiency (though it will + * still give access to the relative position and velocity) + */ + @Deprecated + public FieldPVCoordinates computeDeltaPV(final FieldSpacecraftState s) { + final FieldVector3D primaryPos = s.getPosition(); + final FieldVector3D primaryVel = s.getPVCoordinates().getVelocity(); + + final FieldPVCoordinates secondaryPV = secondaryPVProvider.getPVCoordinates(s.getDate(), s.getFrame()); + final FieldVector3D secondaryPos = secondaryPV.getPosition(); + final FieldVector3D secondaryVel = secondaryPV.getVelocity(); + + final FieldVector3D relativePos = secondaryPos.subtract(primaryPos); + final FieldVector3D relativeVel = secondaryVel.subtract(primaryVel); + + return new FieldPVCoordinates<>(relativePos, relativeVel); + } + + /** + * Get the secondary position-velocity provider stored in this instance. + * + * @return the secondary position-velocity provider stored in this instance + */ + public FieldPVCoordinatesProvider getSecondaryPVProvider() { + return secondaryPVProvider; + } + + /** + * The {@code g} is positive when the primary object is getting further away from the secondary object and is negative + * when it is getting closer to it. + * + * @param s the current state information: date, kinematics, attitude + * + * @return value of the switching function + */ + @Override + public T g(final FieldSpacecraftState s) { + final FieldPVCoordinates deltaPV = computeDeltaPV(s); + return FieldVector3D.dotProduct(deltaPV.getPosition(), deltaPV.getVelocity()); + } + + /** {@inheritDoc} */ + @Override + protected FieldExtremumApproachDetector create(final FieldAdaptableInterval newMaxCheck, final T newThreshold, + final int newMaxIter, final FieldEventHandler newHandler) { + return new FieldExtremumApproachDetector<>(newMaxCheck, newThreshold, newMaxIter, newHandler, secondaryPVProvider); + } +} diff --git a/src/main/java/org/orekit/propagation/events/FieldFunctionalDetector.java b/src/main/java/org/orekit/propagation/events/FieldFunctionalDetector.java index d72651c2c0..537b7e7fa2 100644 --- a/src/main/java/org/orekit/propagation/events/FieldFunctionalDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldFunctionalDetector.java @@ -55,8 +55,8 @@ public class FieldFunctionalDetector> * @param field on which this detector is defined. */ public FieldFunctionalDetector(final Field field) { - this(s -> DEFAULT_MAXCHECK, - field.getZero().add(DEFAULT_THRESHOLD), + this(FieldAdaptableInterval.of(DEFAULT_MAXCHECK), + field.getZero().newInstance(DEFAULT_THRESHOLD), DEFAULT_MAX_ITER, new FieldContinueOnEvent<>(), value -> field.getOne()); } diff --git a/src/main/java/org/orekit/propagation/events/FieldLatitudeCrossingDetector.java b/src/main/java/org/orekit/propagation/events/FieldLatitudeCrossingDetector.java index 47c79e825a..36d1e6f6c7 100644 --- a/src/main/java/org/orekit/propagation/events/FieldLatitudeCrossingDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldLatitudeCrossingDetector.java @@ -52,8 +52,8 @@ public class FieldLatitudeCrossingDetector > public FieldLatitudeCrossingDetector(final Field field, final OneAxisEllipsoid body, final double latitude) { - this(s -> DEFAULT_MAXCHECK, - field.getZero().add(DEFAULT_THRESHOLD), DEFAULT_MAX_ITER, new FieldStopOnIncreasing<>(), + this(FieldAdaptableInterval.of(DEFAULT_MAXCHECK), + field.getZero().newInstance(DEFAULT_THRESHOLD), DEFAULT_MAX_ITER, new FieldStopOnIncreasing<>(), body, latitude); } @@ -68,7 +68,7 @@ public FieldLatitudeCrossingDetector(final T maxCheck, final T threshold, final OneAxisEllipsoid body, final double latitude) { - this(s -> maxCheck.getReal(), threshold, DEFAULT_MAX_ITER, new FieldStopOnIncreasing<>(), + this(FieldAdaptableInterval.of(maxCheck.getReal()), threshold, DEFAULT_MAX_ITER, new FieldStopOnIncreasing<>(), body, latitude); } diff --git a/src/main/java/org/orekit/propagation/events/FieldLatitudeRangeCrossingDetector.java b/src/main/java/org/orekit/propagation/events/FieldLatitudeRangeCrossingDetector.java new file mode 100644 index 0000000000..fd85c8f3ae --- /dev/null +++ b/src/main/java/org/orekit/propagation/events/FieldLatitudeRangeCrossingDetector.java @@ -0,0 +1,191 @@ +/* Copyright 2023-2024 Alberto Ferrero + * 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. + * Alberto Ferrero 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.hipparchus.util.FastMath; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.OneAxisEllipsoid; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.events.handlers.FieldEventHandler; +import org.orekit.propagation.events.handlers.FieldStopOnIncreasing; + + +/** Detector for geographic latitude crossing. + *

          This detector identifies when a spacecraft crosses a fixed + * latitude range with respect to a central body.

          + * @author Alberto Ferrero + * @since 12.0 + * @param type of the field elements + */ +public class FieldLatitudeRangeCrossingDetector > + extends FieldAbstractDetector, T> { + + /** + * Body on which the latitude is defined. + */ + private final OneAxisEllipsoid body; + + /** + * Fixed latitude to be crossed, lower boundary in radians. + */ + private final double fromLatitude; + + /** + * Fixed latitude to be crossed, upper boundary in radians. + */ + private final double toLatitude; + + /** + * Sign, to get reversed inclusion latitude range (lower > upper). + */ + private final double sign; + + /** + * Build a new detector. + *

          The new instance uses default values for maximal checking interval + * ({@link #DEFAULT_MAXCHECK}) and convergence threshold ({@link + * #DEFAULT_THRESHOLD}).

          + * @param field the type of numbers to use. + * @param body body on which the latitude is defined + * @param fromLatitude latitude to be crossed, lower range boundary + * @param toLatitude latitude to be crossed, upper range boundary + */ + public FieldLatitudeRangeCrossingDetector(final Field field, + final OneAxisEllipsoid body, + final double fromLatitude, + final double toLatitude) { + this(FieldAdaptableInterval.of(DEFAULT_MAXCHECK), + field.getZero().add(DEFAULT_THRESHOLD), + DEFAULT_MAX_ITER, + new FieldStopOnIncreasing<>(), + body, + fromLatitude, + toLatitude); + } + + /** + * Build a detector. + * + * @param maxCheck maximal checking interval (s) + * @param threshold convergence threshold (s) + * @param body body on which the latitude is defined + * @param fromLatitude latitude to be crossed, lower range boundary + * @param toLatitude latitude to be crossed, upper range boundary + */ + public FieldLatitudeRangeCrossingDetector(final T maxCheck, final T threshold, + final OneAxisEllipsoid body, final double fromLatitude, final double toLatitude) { + this(FieldAdaptableInterval.of(maxCheck.getReal()), threshold, DEFAULT_MAX_ITER, new FieldStopOnIncreasing<>(), + body, fromLatitude, toLatitude); + } + + /** + * Private constructor with full parameters. + *

          + * This constructor is private 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 (s) + * @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 body body on which the latitude is defined + * @param fromLatitude latitude to be crossed, lower range boundary + * @param toLatitude latitude to be crossed, upper range boundary + */ + protected FieldLatitudeRangeCrossingDetector(final FieldAdaptableInterval maxCheck, + final T threshold, + final int maxIter, + final FieldEventHandler handler, + final OneAxisEllipsoid body, + final double fromLatitude, + final double toLatitude) { + super(maxCheck, threshold, maxIter, handler); + this.body = body; + this.fromLatitude = fromLatitude; + this.toLatitude = toLatitude; + this.sign = FastMath.signum(toLatitude - fromLatitude); + } + + /** + * {@inheritDoc} + */ + @Override + protected FieldLatitudeRangeCrossingDetector create(final FieldAdaptableInterval newMaxCheck, + final T newThreshold, + final int newMaxIter, + final FieldEventHandler newHandler) { + return new FieldLatitudeRangeCrossingDetector<>(newMaxCheck, newThreshold, newMaxIter, newHandler, + body, fromLatitude, toLatitude); + } + + /** + * Get the body on which the geographic zone is defined. + * + * @return body on which the geographic zone is defined + */ + public OneAxisEllipsoid getBody() { + return body; + } + + /** + * Get the fixed latitude range to be crossed (radians), lower boundary. + * + * @return fixed lower boundary latitude range to be crossed (radians) + */ + public double getFromLatitude() { + return fromLatitude; + } + + /** + * Get the fixed latitude range to be crossed (radians), upper boundary. + * + * @return fixed lower boundary latitude range to be crossed (radians) + */ + public double getToLatitude() { + return toLatitude; + } + + /** + * Compute the value of the detection function. + *

          + * The value is positive if the spacecraft latitude is inside the latitude range. + * It is positive if the spacecraft is northward to lower boundary range and southward to upper boundary range, + * with respect to the fixed latitude range. + *

          + * + * @param s the current state information: date, kinematics, attitude + * @return positive if spacecraft inside the range + */ + public T g(final FieldSpacecraftState s) { + + // convert state to geodetic coordinates + final FieldGeodeticPoint gp = body.transform(s.getPVCoordinates().getPosition(), + s.getFrame(), s.getDate()); + + // point latitude + final T latitude = gp.getLatitude(); + + // inside or outside latitude range + return latitude.subtract(fromLatitude).multiply(latitude.negate().add(toLatitude)).multiply(sign); + + } + +} diff --git a/src/main/java/org/orekit/propagation/events/FieldLongitudeCrossingDetector.java b/src/main/java/org/orekit/propagation/events/FieldLongitudeCrossingDetector.java index cb4437a195..48da9a43b5 100644 --- a/src/main/java/org/orekit/propagation/events/FieldLongitudeCrossingDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldLongitudeCrossingDetector.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2024 Alberto Ferrero +/* Copyright 2023-2024 Alberto Ferrero * 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. @@ -64,8 +64,8 @@ public class FieldLongitudeCrossingDetector > * @param longitude longitude to be crossed */ public FieldLongitudeCrossingDetector(final Field field, final OneAxisEllipsoid body, final double longitude) { - this(s -> DEFAULT_MAXCHECK, - field.getZero().add(DEFAULT_THRESHOLD), DEFAULT_MAX_ITER, new FieldStopOnIncreasing<>(), body, longitude); + this(FieldAdaptableInterval.of(DEFAULT_MAXCHECK), + field.getZero().newInstance(DEFAULT_THRESHOLD), DEFAULT_MAX_ITER, new FieldStopOnIncreasing<>(), body, longitude); } /** @@ -80,7 +80,7 @@ public FieldLongitudeCrossingDetector(final T maxCheck, final T threshold, final OneAxisEllipsoid body, final double longitude) { - this(s -> maxCheck.getReal(), threshold, DEFAULT_MAX_ITER, new FieldStopOnIncreasing<>(), body, longitude); + this(FieldAdaptableInterval.of(maxCheck.getReal()), threshold, DEFAULT_MAX_ITER, new FieldStopOnIncreasing<>(), body, longitude); } /** diff --git a/src/main/java/org/orekit/propagation/events/FieldLongitudeRangeCrossingDetector.java b/src/main/java/org/orekit/propagation/events/FieldLongitudeRangeCrossingDetector.java new file mode 100644 index 0000000000..79c76b4fc7 --- /dev/null +++ b/src/main/java/org/orekit/propagation/events/FieldLongitudeRangeCrossingDetector.java @@ -0,0 +1,219 @@ +/* Copyright 2023-2024 Alberto Ferrero + * 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. + * Alberto Ferrero 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.hipparchus.util.FastMath; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.OneAxisEllipsoid; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.events.handlers.FieldEventHandler; +import org.orekit.propagation.events.handlers.FieldStopOnIncreasing; + + +/** Detector for geographic longitude crossing. + *

          This detector identifies when a spacecraft crosses a fixed + * longitude range with respect to a central body.

          + * @author Alberto Ferrero + * @since 12.0 + * @param type of the field elements + */ +public class FieldLongitudeRangeCrossingDetector > + extends FieldAbstractDetector, T> { + + /** + * Body on which the longitude is defined. + */ + private final OneAxisEllipsoid body; + + /** + * Fixed longitude to be crossed, lower boundary in radians. + */ + private final double fromLongitude; + + /** + * Fixed longitude to be crossed, upper boundary in radians. + */ + private final double toLongitude; + + /** + * Sign, to get reversed inclusion longitude range (lower > upper). + */ + private final double sign; + + /** + * Build a new detector. + *

          The new instance uses default values for maximal checking interval + * ({@link #DEFAULT_MAXCHECK}) and convergence threshold ({@link + * #DEFAULT_THRESHOLD}).

          + * @param field the type of numbers to use. + * @param body body on which the longitude is defined + * @param fromLongitude longitude to be crossed, lower range boundary + * @param toLongitude longitude to be crossed, upper range boundary + */ + public FieldLongitudeRangeCrossingDetector(final Field field, final OneAxisEllipsoid body, final double fromLongitude, final double toLongitude) { + this(FieldAdaptableInterval.of(DEFAULT_MAXCHECK), + field.getZero().add(DEFAULT_THRESHOLD), + DEFAULT_MAX_ITER, + new FieldStopOnIncreasing<>(), + body, + fromLongitude, + toLongitude); + } + + /** + * Build a detector. + * + * @param maxCheck maximal checking interval (s) + * @param threshold convergence threshold (s) + * @param body body on which the longitude is defined + * @param fromLongitude longitude to be crossed, lower range boundary + * @param toLongitude longitude to be crossed, upper range boundary + */ + public FieldLongitudeRangeCrossingDetector(final T maxCheck, final T threshold, + final OneAxisEllipsoid body, final double fromLongitude, final double toLongitude) { + this(FieldAdaptableInterval.of(maxCheck.getReal()), + threshold, + DEFAULT_MAX_ITER, + new FieldStopOnIncreasing<>(), + body, + fromLongitude, + toLongitude); + } + + /** + * Private constructor with full parameters. + *

          + * This constructor is private 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 (s) + * @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 body body on which the longitude is defined + * @param fromLongitude longitude to be crossed, lower range boundary + * @param toLongitude longitude to be crossed, upper range boundary + */ + protected FieldLongitudeRangeCrossingDetector(final FieldAdaptableInterval maxCheck, + final T threshold, + final int maxIter, + final FieldEventHandler handler, + final OneAxisEllipsoid body, + final double fromLongitude, + final double toLongitude) { + super(maxCheck, threshold, maxIter, handler); + this.body = body; + this.fromLongitude = ensureLongitudePositiveContinuity(fromLongitude); + this.toLongitude = ensureLongitudePositiveContinuity(toLongitude); + this.sign = FastMath.signum(this.toLongitude - this.fromLongitude); + } + + /** + * {@inheritDoc} + */ + @Override + protected FieldLongitudeRangeCrossingDetector create(final FieldAdaptableInterval newMaxCheck, + final T newThreshold, + final int newMaxIter, + final FieldEventHandler newHandler) { + return new FieldLongitudeRangeCrossingDetector(newMaxCheck, newThreshold, newMaxIter, newHandler, + body, fromLongitude, toLongitude); + } + + /** + * Get the body on which the geographic zone is defined. + * + * @return body on which the geographic zone is defined + */ + public OneAxisEllipsoid getBody() { + return body; + } + + /** Get the fixed longitude range to be crossed (radians), lower boundary. + * @return fixed lower boundary longitude range to be crossed (radians) + */ + public double getFromLongitude() { + return getLongitudeOverOriginalRange(fromLongitude); + } + + /** Get the fixed longitude range to be crossed (radians), upper boundary. + * @return fixed upper boundary longitude range to be crossed (radians) + */ + public double getToLongitude() { + return getLongitudeOverOriginalRange(toLongitude); + } + + /** + * Ensure continuity for negative angles, as longitude defined as [-PI, PI], transform negative to positive. + * New longitude angle definition from [0, 2 PI]. + * + * @param longitude original longitude value + * @return positive range longitude + */ + private T ensureFieldLongitudePositiveContinuity(final T longitude) { + return longitude.getReal() < 0 ? longitude.add(2 * FastMath.PI) : longitude; + } + + /** + * Ensure continuity for negative angles, as longitude defined as [-PI, PI], transform negative to positive. + * New longitude angle definition from [0, 2 PI]. + * + * @param longitude original longitude value + * @return positive range longitude + */ + private double ensureLongitudePositiveContinuity(final double longitude) { + return longitude < 0 ? longitude + 2 * FastMath.PI : longitude; + } + + /** + * Get longitude shifted over the original range [-PI, PI]. + * @param longitude longitude value to convert + * @return original range longitude + */ + private double getLongitudeOverOriginalRange(final double longitude) { + return longitude > FastMath.PI ? longitude - 2 * FastMath.PI : longitude; + } + + /** + * Compute the value of the detection function. + *

          + * The value is positive if the spacecraft longitude is inside the longitude range. + * The longitude value is reflected from [-PI, +PI] to [0, 2 PI] to ensure continuity. + *

          + * + * @param s the current state information: date, kinematics, attitude + * @return positive if spacecraft inside the range + */ + public T g(final FieldSpacecraftState s) { + + // convert state to geodetic coordinates + final FieldGeodeticPoint gp = body.transform(s.getPVCoordinates().getPosition(), + s.getFrame(), s.getDate()); + + // point longitude + final T longitude = ensureFieldLongitudePositiveContinuity(gp.getLongitude()); + + // inside or outside latitude range + return longitude.subtract(fromLongitude).multiply(longitude.negate().add(toLongitude)).multiply(sign); + + } + +} diff --git a/src/main/java/org/orekit/propagation/events/FieldNodeDetector.java b/src/main/java/org/orekit/propagation/events/FieldNodeDetector.java index 6479238a83..66d0c07e1d 100644 --- a/src/main/java/org/orekit/propagation/events/FieldNodeDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldNodeDetector.java @@ -74,9 +74,8 @@ public FieldNodeDetector(final FieldOrbit orbit, final Frame frame) { * {@link org.orekit.frames.FramesFactory#getITRF(org.orekit.utils.IERSConventions, boolean) ITRF}) */ public FieldNodeDetector(final T threshold, final FieldOrbit orbit, final Frame frame) { - this(s -> orbit.getA().getField().getZero().add(2 * estimateNodesTimeSeparation(orbit.toOrbit()) / 3).getReal(), threshold, - DEFAULT_MAX_ITER, new FieldStopOnIncreasing<>(), - frame); + this(FieldAdaptableInterval.of(orbit.getA().getField().getZero().newInstance(2 * estimateNodesTimeSeparation(orbit.toOrbit()) / 3).getReal()), + threshold, DEFAULT_MAX_ITER, new FieldStopOnIncreasing<>(), frame); } /** Protected constructor with full parameters. diff --git a/src/main/java/org/orekit/propagation/events/FieldOfViewDetector.java b/src/main/java/org/orekit/propagation/events/FieldOfViewDetector.java index c6bd22a589..980c00ffe9 100644 --- a/src/main/java/org/orekit/propagation/events/FieldOfViewDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldOfViewDetector.java @@ -81,7 +81,7 @@ public FieldOfViewDetector(final PVCoordinatesProvider pvTarget, final FieldOfVi */ public FieldOfViewDetector(final PVCoordinatesProvider pvTarget, final double radiusTarget, final VisibilityTrigger trigger, final FieldOfView fov) { - this(s -> DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, + this(AdaptableInterval.of(DEFAULT_MAXCHECK), DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, new StopOnIncreasing(), pvTarget, radiusTarget, trigger, fov); } @@ -157,7 +157,7 @@ public double g(final SpacecraftState s) { // get line of sight in spacecraft frame final Vector3D targetPosInert = targetPVProvider.getPosition(s.getDate(), s.getFrame()); - final Vector3D lineOfSightSC = s.toTransform().transformPosition(targetPosInert); + final Vector3D lineOfSightSC = s.toStaticTransform().transformPosition(targetPosInert); final double angularRadius = FastMath.asin(radiusTarget / lineOfSightSC.getNorm()); return fov.offsetFromBoundary(lineOfSightSC, angularRadius, trigger); diff --git a/src/main/java/org/orekit/propagation/events/FieldParameterDrivenDateIntervalDetector.java b/src/main/java/org/orekit/propagation/events/FieldParameterDrivenDateIntervalDetector.java index 3a60c79d98..74e8b30e5b 100644 --- a/src/main/java/org/orekit/propagation/events/FieldParameterDrivenDateIntervalDetector.java +++ b/src/main/java/org/orekit/propagation/events/FieldParameterDrivenDateIntervalDetector.java @@ -97,7 +97,7 @@ public FieldParameterDrivenDateIntervalDetector(final Field field, final Stri */ public FieldParameterDrivenDateIntervalDetector(final Field field, final String prefix, final AbsoluteDate refStart, final AbsoluteDate refStop) { - this(s -> DEFAULT_MAXCHECK, + this(FieldAdaptableInterval.of(DEFAULT_MAXCHECK), field.getZero().newInstance(DEFAULT_THRESHOLD), DEFAULT_MAX_ITER, new FieldStopOnEvent<>(), diff --git a/src/main/java/org/orekit/propagation/events/FieldRelativeDistanceDetector.java b/src/main/java/org/orekit/propagation/events/FieldRelativeDistanceDetector.java new file mode 100644 index 0000000000..62be3ae7d9 --- /dev/null +++ b/src/main/java/org/orekit/propagation/events/FieldRelativeDistanceDetector.java @@ -0,0 +1,137 @@ +/* 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.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.ode.events.Action; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.events.handlers.FieldEventHandler; +import org.orekit.propagation.events.handlers.FieldStopOnEvent; +import org.orekit.utils.FieldPVCoordinatesProvider; + +/** + * Detector of specific value for the distance relative to another trajectory (using the Euclidean norm). + *

          + * The default implementation behavior is to {@link Action#STOP stop} propagation. + * This can be changed by calling {@link #withHandler(org.orekit.propagation.events.handlers.FieldEventHandler)} after construction. + *

          + *

          + * As this detector needs two objects (moving relative to each other), it embeds one + * {@link org.orekit.utils.FieldPVCoordinatesProvider coordinates provider} for the secondary object and is registered as an event detector in + * the propagator of the primary object. The secondary object {@link org.orekit.utils.FieldPVCoordinatesProvider coordinates provider} will + * therefore be driven by this detector (and hence by the propagator in which this detector is registered). + *

          + *

          + * For efficiency reason during the event search loop, it is recommended to have the secondary provider be an analytical + * propagator or an ephemeris. A numerical propagator as a secondary propagator works but is expected to be + * computationally costly. + *

          + * + * @see org.orekit.propagation.FieldPropagator#addEventDetector(FieldEventDetector) + * @author Romain Serra + * @since 12.1 + */ +public class FieldRelativeDistanceDetector> + extends FieldAbstractDetector, T> { + + /** + * PVCoordinates provider of the other object used to define relative distance. + */ + private final FieldPVCoordinatesProvider secondaryPVProvider; + + /** Relative distance value triggering detection. */ + private final T distanceThreshold; + + /** + * Constructor with default values. + *

          + * By default, the implemented behavior is to {@link Action#STOP stop} propagation at detection. + *

          + * + * @param secondaryPVProvider PVCoordinates provider of the other object defining relative distance. + * @param distanceThreshold Relative distance threshold for event detection + */ + public FieldRelativeDistanceDetector(final FieldPVCoordinatesProvider secondaryPVProvider, + final T distanceThreshold) { + this(FieldAdaptableInterval.of(DEFAULT_MAXCHECK), distanceThreshold.getField().getZero().newInstance(DEFAULT_THRESHOLD), + 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 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 secondaryPVProvider PVCoordinates provider of the other object defining relative distance. + * @param distanceThreshold Relative distance threshold for event detection + * @see FieldEventHandler + */ + 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; + } + + /** + * The {@code g} is positive when the relative distance is larger or equal than the threshold, + * non-positive otherwise. + * + * @param s the current state information: date, kinematics, attitude + * @return value of the switching function + */ + @Override + public T g(final FieldSpacecraftState s) { + final FieldVector3D secondaryPosition = getSecondaryPVProvider().getPosition(s.getDate(), s.getFrame()); + final T relativeDistance = s.getPosition().subtract(secondaryPosition).getNorm(); + return relativeDistance.subtract(distanceThreshold); + } + + /** {@inheritDoc} */ + @Override + protected FieldRelativeDistanceDetector create(final FieldAdaptableInterval newMaxCheck, final T newThreshold, + final int newMaxIter, final FieldEventHandler newHandler) { + return new FieldRelativeDistanceDetector<>(newMaxCheck, newThreshold, newMaxIter, newHandler, secondaryPVProvider, + distanceThreshold); + } + + /** + * Get the secondary position-velocity provider stored in this instance. + * + * @return the secondary position-velocity provider stored in this instance + */ + public FieldPVCoordinatesProvider getSecondaryPVProvider() { + return secondaryPVProvider; + } + + /** + * Get the relative distance threshold. + * + * @return threshold triggering detection + */ + public T getDistanceThreshold() { + return distanceThreshold; + } +} diff --git a/src/main/java/org/orekit/propagation/events/FootprintOverlapDetector.java b/src/main/java/org/orekit/propagation/events/FootprintOverlapDetector.java index 2b3c9d1e7a..9870175048 100644 --- a/src/main/java/org/orekit/propagation/events/FootprintOverlapDetector.java +++ b/src/main/java/org/orekit/propagation/events/FootprintOverlapDetector.java @@ -103,7 +103,7 @@ public FootprintOverlapDetector(final FieldOfView fov, final OneAxisEllipsoid body, final SphericalPolygonsSet zone, final double samplingStep) { - this(s -> DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, + this(AdaptableInterval.of(DEFAULT_MAXCHECK), DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, new StopOnIncreasing(), fov, body, zone, samplingStep, sample(body, zone, samplingStep)); } @@ -275,7 +275,7 @@ public double g(final SpacecraftState s) { final StaticTransform bodyToSc = StaticTransform.compose( s.getDate(), body.getBodyFrame().getStaticTransformTo(s.getFrame(), s.getDate()), - s.toTransform()); + s.toStaticTransform()); for (final SamplingPoint point : sampledZone) { final Vector3D lineOfSightBody = point.getPosition().subtract(scBody); if (Vector3D.dotProduct(lineOfSightBody, point.getZenith()) <= 0) { diff --git a/src/main/java/org/orekit/propagation/events/FunctionalDetector.java b/src/main/java/org/orekit/propagation/events/FunctionalDetector.java index c68f4a8501..3104706670 100644 --- a/src/main/java/org/orekit/propagation/events/FunctionalDetector.java +++ b/src/main/java/org/orekit/propagation/events/FunctionalDetector.java @@ -48,7 +48,7 @@ public class FunctionalDetector extends AbstractDetector { * ContinueOnEvent}, and a g function that is identically unity. */ public FunctionalDetector() { - this(s -> DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, + this(AdaptableInterval.of(DEFAULT_MAXCHECK), DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, new ContinueOnEvent(), (ToDoubleFunction) value -> 1.0); } diff --git a/src/main/java/org/orekit/propagation/events/GeographicZoneDetector.java b/src/main/java/org/orekit/propagation/events/GeographicZoneDetector.java index 129078631f..e38d429337 100644 --- a/src/main/java/org/orekit/propagation/events/GeographicZoneDetector.java +++ b/src/main/java/org/orekit/propagation/events/GeographicZoneDetector.java @@ -77,7 +77,7 @@ public GeographicZoneDetector(final BodyShape body, public GeographicZoneDetector(final double maxCheck, final double threshold, final BodyShape body, final SphericalPolygonsSet zone, final double margin) { - this(s -> maxCheck, threshold, DEFAULT_MAX_ITER, new StopOnIncreasing(), + this(AdaptableInterval.of(maxCheck), threshold, DEFAULT_MAX_ITER, new StopOnIncreasing(), body, zone, zone.getEnclosingCap(), margin); } diff --git a/src/main/java/org/orekit/propagation/events/GroundAtNightDetector.java b/src/main/java/org/orekit/propagation/events/GroundAtNightDetector.java index 3895b723a9..4898482cb3 100644 --- a/src/main/java/org/orekit/propagation/events/GroundAtNightDetector.java +++ b/src/main/java/org/orekit/propagation/events/GroundAtNightDetector.java @@ -83,7 +83,7 @@ public GroundAtNightDetector(final TopocentricFrame groundLocation, final PVCoor final double dawnDuskElevation, final AtmosphericRefractionModel refractionModel) { this(groundLocation, sun, dawnDuskElevation, refractionModel, - s -> DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, + AdaptableInterval.of(DEFAULT_MAXCHECK), DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, new ContinueOnEvent()); } diff --git a/src/main/java/org/orekit/propagation/events/GroundFieldOfViewDetector.java b/src/main/java/org/orekit/propagation/events/GroundFieldOfViewDetector.java index 935ae14d41..c1434de1d8 100644 --- a/src/main/java/org/orekit/propagation/events/GroundFieldOfViewDetector.java +++ b/src/main/java/org/orekit/propagation/events/GroundFieldOfViewDetector.java @@ -64,7 +64,7 @@ public class GroundFieldOfViewDetector extends AbstractDetector DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, + this(AdaptableInterval.of(DEFAULT_MAXCHECK), DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, new StopOnIncreasing(), frame, fov); } diff --git a/src/main/java/org/orekit/propagation/events/HaloXZPlaneCrossingDetector.java b/src/main/java/org/orekit/propagation/events/HaloXZPlaneCrossingDetector.java index 6cca6722d7..90b6d7fb58 100644 --- a/src/main/java/org/orekit/propagation/events/HaloXZPlaneCrossingDetector.java +++ b/src/main/java/org/orekit/propagation/events/HaloXZPlaneCrossingDetector.java @@ -32,7 +32,7 @@ public class HaloXZPlaneCrossingDetector extends AbstractDetector maxCheck, threshold, DEFAULT_MAX_ITER, + this(AdaptableInterval.of(maxCheck), threshold, DEFAULT_MAX_ITER, new StopOnIncreasing()); } diff --git a/src/main/java/org/orekit/propagation/events/InterSatDirectViewDetector.java b/src/main/java/org/orekit/propagation/events/InterSatDirectViewDetector.java index 7e45095360..1c82e3418b 100644 --- a/src/main/java/org/orekit/propagation/events/InterSatDirectViewDetector.java +++ b/src/main/java/org/orekit/propagation/events/InterSatDirectViewDetector.java @@ -85,7 +85,7 @@ public class InterSatDirectViewDetector extends AbstractDetector DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, + this(body, 0.0, secondary, AdaptableInterval.of(DEFAULT_MAXCHECK), DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, new ContinueOnEvent()); } diff --git a/src/main/java/org/orekit/propagation/events/LatitudeCrossingDetector.java b/src/main/java/org/orekit/propagation/events/LatitudeCrossingDetector.java index fa01ff56a7..cc2b0c468b 100644 --- a/src/main/java/org/orekit/propagation/events/LatitudeCrossingDetector.java +++ b/src/main/java/org/orekit/propagation/events/LatitudeCrossingDetector.java @@ -55,7 +55,7 @@ public LatitudeCrossingDetector(final OneAxisEllipsoid body, final double latitu */ public LatitudeCrossingDetector(final double maxCheck, final double threshold, final OneAxisEllipsoid body, final double latitude) { - this(s -> maxCheck, threshold, DEFAULT_MAX_ITER, new StopOnIncreasing(), + this(AdaptableInterval.of(maxCheck), threshold, DEFAULT_MAX_ITER, new StopOnIncreasing(), body, latitude); } diff --git a/src/main/java/org/orekit/propagation/events/LatitudeExtremumDetector.java b/src/main/java/org/orekit/propagation/events/LatitudeExtremumDetector.java index e99d6d7787..59f7d61550 100644 --- a/src/main/java/org/orekit/propagation/events/LatitudeExtremumDetector.java +++ b/src/main/java/org/orekit/propagation/events/LatitudeExtremumDetector.java @@ -52,7 +52,7 @@ public LatitudeExtremumDetector(final OneAxisEllipsoid body) { */ public LatitudeExtremumDetector(final double maxCheck, final double threshold, final OneAxisEllipsoid body) { - this(s -> maxCheck, threshold, DEFAULT_MAX_ITER, new StopOnIncreasing(), + this(AdaptableInterval.of(maxCheck), threshold, DEFAULT_MAX_ITER, new StopOnIncreasing(), body); } diff --git a/src/main/java/org/orekit/propagation/events/LatitudeRangeCrossingDetector.java b/src/main/java/org/orekit/propagation/events/LatitudeRangeCrossingDetector.java new file mode 100644 index 0000000000..4e2029d25c --- /dev/null +++ b/src/main/java/org/orekit/propagation/events/LatitudeRangeCrossingDetector.java @@ -0,0 +1,152 @@ +/* Copyright 2023-2024 Alberto Ferrero + * 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. + * Alberto Ferrero 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.util.FastMath; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.bodies.OneAxisEllipsoid; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.events.handlers.EventHandler; +import org.orekit.propagation.events.handlers.StopOnDecreasing; + + +/** Detector for geographic latitude crossing. + *

          This detector identifies when a spacecraft crosses a fixed + * latitude range with respect to a central body.

          + * @author Alberto Ferrero + * @since 12.0 + */ +public class LatitudeRangeCrossingDetector extends AbstractDetector { + + /** Body on which the latitude is defined. */ + private final OneAxisEllipsoid body; + + /** Fixed latitude to be crossed, lower boundary in radians. */ + private final double fromLatitude; + + /** Fixed latitude to be crossed, upper boundary in radians. */ + private final double toLatitude; + + /** + * Sign, to get reversed inclusion latitude range (lower > upper). + */ + private final double sign; + + /** Build a new detector. + *

          The new instance uses default values for maximal checking interval + * ({@link #DEFAULT_MAXCHECK}) and convergence threshold ({@link + * #DEFAULT_THRESHOLD}).

          + * @param body body on which the latitude is defined + * @param fromLatitude latitude to be crossed, lower range boundary + * @param toLatitude latitude to be crossed, upper range boundary + */ + public LatitudeRangeCrossingDetector(final OneAxisEllipsoid body, final double fromLatitude, final double toLatitude) { + this(DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, body, fromLatitude, toLatitude); + } + + /** Build a detector. + * @param maxCheck maximal checking interval (s) + * @param threshold convergence threshold (s) + * @param body body on which the latitude is defined + * @param fromLatitude latitude to be crossed, lower range boundary + * @param toLatitude latitude to be crossed, upper range boundary + */ + public LatitudeRangeCrossingDetector(final double maxCheck, final double threshold, + final OneAxisEllipsoid body, final double fromLatitude, final double toLatitude) { + this(AdaptableInterval.of(maxCheck), threshold, DEFAULT_MAX_ITER, new StopOnDecreasing(), + body, fromLatitude, toLatitude); + } + + /** Private constructor with full parameters. + *

          + * This constructor is private 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 (s) + * @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 body body on which the latitude is defined + * @param fromLatitude latitude to be crossed, lower range boundary + * @param toLatitude latitude to be crossed, upper range boundary + */ + 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); + this.body = body; + this.fromLatitude = fromLatitude; + this.toLatitude = toLatitude; + this.sign = FastMath.signum(toLatitude - fromLatitude); + } + + /** {@inheritDoc} */ + @Override + protected LatitudeRangeCrossingDetector create(final AdaptableInterval newMaxCheck, + final double newThreshold, + final int newMaxIter, + final EventHandler newHandler) { + return new LatitudeRangeCrossingDetector(newMaxCheck, newThreshold, newMaxIter, newHandler, + body, fromLatitude, toLatitude); + } + + /** Get the body on which the geographic zone is defined. + * @return body on which the geographic zone is defined + */ + public OneAxisEllipsoid getBody() { + return body; + } + + /** Get the fixed latitude range to be crossed (radians), lower boundary. + * @return fixed lower boundary latitude range to be crossed (radians) + */ + public double getFromLatitude() { + return fromLatitude; + } + + /** Get the fixed latitude range to be crossed (radians), upper boundary. + * @return fixed lower boundary latitude range to be crossed (radians) + */ + public double getToLatitude() { + return toLatitude; + } + + /** Compute the value of the detection function. + *

          + * The value is positive if the spacecraft latitude is inside the latitude range. + * It is positive if the spacecraft is northward to lower boundary range and southward to upper boundary range, + * with respect to the fixed latitude range. + *

          + * @param s the current state information: date, kinematics, attitude + * @return positive if spacecraft inside the range + */ + public double g(final SpacecraftState s) { + + // convert state to geodetic coordinates + final GeodeticPoint gp = body.transform(s.getPVCoordinates().getPosition(), + s.getFrame(), s.getDate()); + + // point latitude + final double latitude = gp.getLatitude(); + + // inside or outside latitude range + return sign * (latitude - fromLatitude) * (toLatitude - latitude); + + } + +} diff --git a/src/main/java/org/orekit/propagation/events/LongitudeCrossingDetector.java b/src/main/java/org/orekit/propagation/events/LongitudeCrossingDetector.java index 9fec2add61..8f0e185ac1 100644 --- a/src/main/java/org/orekit/propagation/events/LongitudeCrossingDetector.java +++ b/src/main/java/org/orekit/propagation/events/LongitudeCrossingDetector.java @@ -62,7 +62,7 @@ public LongitudeCrossingDetector(final OneAxisEllipsoid body, final double longi */ public LongitudeCrossingDetector(final double maxCheck, final double threshold, final OneAxisEllipsoid body, final double longitude) { - this(s -> maxCheck, threshold, DEFAULT_MAX_ITER, new StopOnIncreasing(), + this(AdaptableInterval.of(maxCheck), threshold, DEFAULT_MAX_ITER, new StopOnIncreasing(), body, longitude); } diff --git a/src/main/java/org/orekit/propagation/events/LongitudeExtremumDetector.java b/src/main/java/org/orekit/propagation/events/LongitudeExtremumDetector.java index b9c610610c..d82b72f038 100644 --- a/src/main/java/org/orekit/propagation/events/LongitudeExtremumDetector.java +++ b/src/main/java/org/orekit/propagation/events/LongitudeExtremumDetector.java @@ -52,7 +52,7 @@ public LongitudeExtremumDetector(final OneAxisEllipsoid body) { */ public LongitudeExtremumDetector(final double maxCheck, final double threshold, final OneAxisEllipsoid body) { - this(s -> maxCheck, threshold, DEFAULT_MAX_ITER, new StopOnIncreasing(), + this(AdaptableInterval.of(maxCheck), threshold, DEFAULT_MAX_ITER, new StopOnIncreasing(), body); } diff --git a/src/main/java/org/orekit/propagation/events/LongitudeRangeCrossingDetector.java b/src/main/java/org/orekit/propagation/events/LongitudeRangeCrossingDetector.java new file mode 100644 index 0000000000..02489aeb8d --- /dev/null +++ b/src/main/java/org/orekit/propagation/events/LongitudeRangeCrossingDetector.java @@ -0,0 +1,170 @@ +/* Copyright 2023-2024 Alberto Ferrero + * 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. + * Alberto Ferrero 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.util.FastMath; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.bodies.OneAxisEllipsoid; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.events.handlers.EventHandler; +import org.orekit.propagation.events.handlers.StopOnDecreasing; + + +/** Detector for geographic longitude crossing. + *

          This detector identifies when a spacecraft crosses a fixed + * longitude range with respect to a central body.

          + * @author Alberto Ferrero + * @since 12.0 + */ +public class LongitudeRangeCrossingDetector extends AbstractDetector { + + /** Body on which the longitude is defined. */ + private final OneAxisEllipsoid body; + + /** Fixed longitude to be crossed, lower boundary in radians. */ + private final double fromLongitude; + + /** Fixed longitude to be crossed, upper boundary in radians. */ + private final double toLongitude; + + /** + * Sign, to get reversed inclusion longitude range (lower > upper). + */ + private final double sign; + + /** Build a new detector. + *

          The new instance uses default values for maximal checking interval + * ({@link #DEFAULT_MAXCHECK}) and convergence threshold ({@link + * #DEFAULT_THRESHOLD}).

          + * @param body body on which the longitude is defined + * @param fromLongitude longitude to be crossed, lower range boundary + * @param toLongitude longitude to be crossed, upper range boundary + */ + public LongitudeRangeCrossingDetector(final OneAxisEllipsoid body, final double fromLongitude, final double toLongitude) { + this(DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, body, fromLongitude, toLongitude); + } + + /** Build a detector. + * @param maxCheck maximal checking interval (s) + * @param threshold convergence threshold (s) + * @param body body on which the longitude is defined + * @param fromLongitude longitude to be crossed, lower range boundary + * @param toLongitude longitude to be crossed, upper range boundary + */ + public LongitudeRangeCrossingDetector(final double maxCheck, final double threshold, + final OneAxisEllipsoid body, final double fromLongitude, final double toLongitude) { + this(AdaptableInterval.of(maxCheck), threshold, DEFAULT_MAX_ITER, new StopOnDecreasing(), + body, fromLongitude, toLongitude); + } + + /** Private constructor with full parameters. + *

          + * This constructor is private 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 (s) + * @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 body body on which the longitude is defined + * @param fromLongitude longitude to be crossed, lower range boundary + * @param toLongitude longitude to be crossed, upper range boundary + */ + protected LongitudeRangeCrossingDetector(final AdaptableInterval maxCheck, final double threshold, final int maxIter, + final EventHandler handler, + final OneAxisEllipsoid body, final double fromLongitude, final double toLongitude) { + super(maxCheck, threshold, maxIter, handler); + this.body = body; + this.fromLongitude = ensureLongitudePositiveContinuity(fromLongitude); + this.toLongitude = ensureLongitudePositiveContinuity(toLongitude); + this.sign = FastMath.signum(this.toLongitude - this.fromLongitude); + } + + /** {@inheritDoc} */ + @Override + protected LongitudeRangeCrossingDetector create(final AdaptableInterval newMaxCheck, + final double newThreshold, + final int newMaxIter, + final EventHandler newHandler) { + return new LongitudeRangeCrossingDetector(newMaxCheck, newThreshold, newMaxIter, newHandler, + body, fromLongitude, toLongitude); + } + + /** Get the body on which the geographic zone is defined. + * @return body on which the geographic zone is defined + */ + public OneAxisEllipsoid getBody() { + return body; + } + + /** Get the fixed longitude range to be crossed (radians), lower boundary. + * @return fixed lower boundary longitude range to be crossed (radians) + */ + public double getFromLongitude() { + return getLongitudeOverOriginalRange(fromLongitude); + } + + /** Get the fixed longitude range to be crossed (radians), upper boundary. + * @return fixed upper boundary longitude range to be crossed (radians) + */ + public double getToLongitude() { + return getLongitudeOverOriginalRange(toLongitude); + } + + /** + * Ensure continuity for negative angles, as longitude defined as [-PI, PI], transform negative to positive. + * New longitude angle definition from [0, 2 PI]. + * @param longitude original longitude value + * @return positive range longitude + */ + private double ensureLongitudePositiveContinuity(final double longitude) { + return longitude < 0 ? longitude + 2 * FastMath.PI : longitude; + } + + /** + * Get longitude shifted over the original range [-PI, PI]. + * @param longitude longitude value to convert + * @return original range longitude + */ + private double getLongitudeOverOriginalRange(final double longitude) { + return longitude > FastMath.PI ? longitude - 2 * FastMath.PI : longitude; + } + + /** Compute the value of the detection function. + *

          + * The value is positive if the spacecraft longitude is inside the longitude range. + * The longitude value is reflected from [-PI, +PI] to [0, 2 PI] to ensure continuity. + *

          + * @param s the current state information: date, kinematics, attitude + * @return positive if spacecraft inside the range + */ + public double g(final SpacecraftState s) { + + // convert state to geodetic coordinates + final GeodeticPoint gp = body.transform(s.getPVCoordinates().getPosition(), + s.getFrame(), s.getDate()); + + // point longitude + final double longitude = ensureLongitudePositiveContinuity(gp.getLongitude()); + + // inside or outside longitude range + return sign * (longitude - fromLongitude) * (toLongitude - longitude); + + } + +} diff --git a/src/main/java/org/orekit/propagation/events/MagneticFieldDetector.java b/src/main/java/org/orekit/propagation/events/MagneticFieldDetector.java index 679c140f2f..f3d17bf807 100644 --- a/src/main/java/org/orekit/propagation/events/MagneticFieldDetector.java +++ b/src/main/java/org/orekit/propagation/events/MagneticFieldDetector.java @@ -143,7 +143,7 @@ public MagneticFieldDetector(final double maxCheck, final OneAxisEllipsoid body, final boolean atSeaLevel, final DataContext dataContext) { - this(s -> maxCheck, threshold, DEFAULT_MAX_ITER, new StopOnIncreasing(), + this(AdaptableInterval.of(maxCheck), threshold, DEFAULT_MAX_ITER, new StopOnIncreasing(), limit, model, body, atSeaLevel, dataContext); } diff --git a/src/main/java/org/orekit/propagation/events/NodeDetector.java b/src/main/java/org/orekit/propagation/events/NodeDetector.java index d2253f45e7..2db2aa1de8 100644 --- a/src/main/java/org/orekit/propagation/events/NodeDetector.java +++ b/src/main/java/org/orekit/propagation/events/NodeDetector.java @@ -67,7 +67,7 @@ public class NodeDetector extends AbstractDetector { * @since 10.3 */ public NodeDetector(final Frame frame) { - this(s -> DEFAULT_MAX_CHECK, DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, + this(AdaptableInterval.of(DEFAULT_MAX_CHECK), DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, new StopOnIncreasing(), frame); } @@ -94,7 +94,7 @@ 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(s -> 2 * estimateNodesTimeSeparation(orbit) / 3, threshold, + this(AdaptableInterval.of(2 * estimateNodesTimeSeparation(orbit) / 3), threshold, DEFAULT_MAX_ITER, new StopOnIncreasing(), frame); } diff --git a/src/main/java/org/orekit/propagation/events/PositionAngleDetector.java b/src/main/java/org/orekit/propagation/events/PositionAngleDetector.java index ad282af8c7..97e63969df 100644 --- a/src/main/java/org/orekit/propagation/events/PositionAngleDetector.java +++ b/src/main/java/org/orekit/propagation/events/PositionAngleDetector.java @@ -93,7 +93,7 @@ public PositionAngleDetector(final double maxCheck, final double threshold, final OrbitType orbitType, final PositionAngleType positionAngleType, final double angle) throws OrekitIllegalArgumentException { - this(s -> maxCheck, threshold, DEFAULT_MAX_ITER, new StopOnEvent(), + this(AdaptableInterval.of(maxCheck), threshold, DEFAULT_MAX_ITER, new StopOnEvent(), orbitType, positionAngleType, angle); } diff --git a/src/main/java/org/orekit/propagation/events/RelativeDistanceDetector.java b/src/main/java/org/orekit/propagation/events/RelativeDistanceDetector.java new file mode 100644 index 0000000000..ca57777062 --- /dev/null +++ b/src/main/java/org/orekit/propagation/events/RelativeDistanceDetector.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.propagation.events; + +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.ode.events.Action; +import org.orekit.propagation.PropagatorsParallelizer; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.events.handlers.EventHandler; +import org.orekit.propagation.events.handlers.StopOnEvent; +import org.orekit.utils.PVCoordinatesProvider; + +/** + * Detector of specific value for the distance relative to another trajectory (using the Euclidean norm). + *

          + * The default implementation behavior is to {@link Action#STOP stop} propagation. + * This can be changed by calling {@link #withHandler(EventHandler)} after construction. + *

          + *

          + * As this detector needs two objects (moving relative to each other), it embeds one + * {@link PVCoordinatesProvider coordinates provider} for the secondary object and is registered as an event detector in + * the propagator of the primary object. The secondary object {@link PVCoordinatesProvider coordinates provider} will + * therefore be driven by this detector (and hence by the propagator in which this detector is registered). + *

          + *

          + * In order to avoid infinite recursion, care must be taken to have the secondary object provider being completely + * independent from anything else. In particular, if the provider is a propagator, it should not be run + * together in a {@link PropagatorsParallelizer propagators parallelizer} with the propagator this detector is + * registered in. It is fine however to configure two separate propagators PsA and PsB with similar settings for the + * secondary object and one propagator Pm for the primary object and then use Psa in this detector registered within Pm + * while Pm and Psb are run in the context of a {@link PropagatorsParallelizer propagators parallelizer}. + *

          + *

          + * For efficiency reason during the event search loop, it is recommended to have the secondary provider be an analytical + * propagator or an ephemeris. A numerical propagator as a secondary propagator works but is expected to be + * computationally costly. + *

          + * + * @see org.orekit.propagation.Propagator#addEventDetector(EventDetector) + * @author Romain Serra + * @since 12.1 + */ +public class RelativeDistanceDetector extends AbstractDetector { + + /** + * PVCoordinates provider of the other object used to define relative distance. + */ + private final PVCoordinatesProvider secondaryPVProvider; + + /** Relative distance value triggering detection. */ + private final double distanceThreshold; + + /** + * Constructor with default values. + *

          + * By default, the implemented behavior is to {@link Action#STOP stop} propagation at detection. + *

          + * + * @param secondaryPVProvider PVCoordinates provider of the other object defining relative distance. + * @param distanceThreshold Relative distance threshold for event detection + */ + public RelativeDistanceDetector(final PVCoordinatesProvider secondaryPVProvider, + final double distanceThreshold) { + this(AdaptableInterval.of(DEFAULT_MAXCHECK), DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, new StopOnEvent(), secondaryPVProvider, + distanceThreshold); + } + + /** + * Constructor. + *

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

          + * + * @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 secondaryPVProvider PVCoordinates provider of the other object defining relative distance. + * @param distanceThreshold Relative distance threshold for event detection + * @see EventHandler + */ + 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; + } + + /** + * The {@code g} is positive when the relative distance is larger or equal than the threshold, + * non-positive otherwise. + * + * @param s the current state information: date, kinematics, attitude + * @return value of the switching function + */ + public double g(final SpacecraftState s) { + final Vector3D secondaryPosition = getSecondaryPVProvider().getPosition(s.getDate(), s.getFrame()); + final double relativeDistance = s.getPosition().subtract(secondaryPosition).getNorm(); + return relativeDistance - distanceThreshold; + } + + /** {@inheritDoc} */ + @Override + protected RelativeDistanceDetector create(final AdaptableInterval newMaxCheck, final double newThreshold, + final int newMaxIter, final EventHandler newHandler) { + return new RelativeDistanceDetector(newMaxCheck, newThreshold, newMaxIter, newHandler, secondaryPVProvider, + distanceThreshold); + } + + /** + * Get the secondary position-velocity provider stored in this instance. + * + * @return the secondary position-velocity provider stored in this instance + */ + public PVCoordinatesProvider getSecondaryPVProvider() { + return secondaryPVProvider; + } + + /** + * Get the relative distance threshold. + * + * @return threshold triggering detection + */ + public double getDistanceThreshold() { + return distanceThreshold; + } +} diff --git a/src/main/java/org/orekit/propagation/events/handlers/FieldRecallLastOccurrence.java b/src/main/java/org/orekit/propagation/events/handlers/FieldRecallLastOccurrence.java new file mode 100644 index 0000000000..3ccc07dd45 --- /dev/null +++ b/src/main/java/org/orekit/propagation/events/handlers/FieldRecallLastOccurrence.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.propagation.events.handlers; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.ode.events.Action; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.events.FieldEventDetector; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; + +/** + * Event handler wrapping another, arbitrary one whilst remembering date of last detection. + * If never used, the cache is null. + * If used but nothing detected, it returns past infinity in case of forward propagation and future infinity otherwise. + * @author Romain Serra + * @see RecallLastOccurrence + * @since 12.1 + * @param field type + */ +public class FieldRecallLastOccurrence> implements FieldEventHandler { + + /** Wrapped event handler. */ + private final FieldEventHandler wrappedHandler; + + /** Last date at which the wrapped event occurred. */ + private FieldAbsoluteDate lastOccurrence; + + /** Constructor. + * @param wrappedHandler event handler to wrap + */ + public FieldRecallLastOccurrence(final FieldEventHandler wrappedHandler) { + this.wrappedHandler = wrappedHandler; + } + + /** Getter for last occurrence. + * @return last date when underlying event was detected + */ + public FieldAbsoluteDate getLastOccurrence() { + return lastOccurrence; + } + + /** {@inheritDoc} */ + @Override + public void init(final FieldSpacecraftState initialState, final FieldAbsoluteDate target, + final FieldEventDetector detector) { + final boolean isForward = target.isAfter(initialState.getDate()); + final Field field = target.getField(); + final AbsoluteDate date = isForward ? AbsoluteDate.PAST_INFINITY : AbsoluteDate.FUTURE_INFINITY; + lastOccurrence = new FieldAbsoluteDate<>(field, date); + wrappedHandler.init(initialState, target, detector); + } + + /** {@inheritDoc} */ + @Override + public Action eventOccurred(final FieldSpacecraftState s, final FieldEventDetector detector, + final boolean increasing) { + lastOccurrence = s.getDate(); + return wrappedHandler.eventOccurred(s, detector, increasing); + } + + /** {@inheritDoc} */ + @Override + public FieldSpacecraftState resetState(final FieldEventDetector detector, + final FieldSpacecraftState oldState) { + return wrappedHandler.resetState(detector, oldState); + } +} diff --git a/src/main/java/org/orekit/propagation/events/handlers/RecallLastOccurrence.java b/src/main/java/org/orekit/propagation/events/handlers/RecallLastOccurrence.java new file mode 100644 index 0000000000..7263d2397e --- /dev/null +++ b/src/main/java/org/orekit/propagation/events/handlers/RecallLastOccurrence.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.propagation.events.handlers; + +import org.hipparchus.ode.events.Action; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.events.EventDetector; +import org.orekit.time.AbsoluteDate; + +/** + * Event handler wrapping another, arbitrary one whilst remembering date of last detection. + * If never used, the cache is null. + * If used but nothing detected, it returns past infinity in case of forward propagation and future infinity otherwise. + * @author Romain Serra + * @see RecordAndContinue + * @since 12.1 + */ +public class RecallLastOccurrence implements EventHandler { + + /** Wrapped event handler. */ + private final EventHandler wrappedHandler; + + /** Last date at which the wrapped event occurred. */ + private AbsoluteDate lastOccurrence; + + /** Constructor. + * @param wrappedHandler event handler to wrap + */ + public RecallLastOccurrence(final EventHandler wrappedHandler) { + this.wrappedHandler = wrappedHandler; + } + + /** Getter for last occurrence. + * @return last date when underlying event was detected + */ + public AbsoluteDate getLastOccurrence() { + return lastOccurrence; + } + + /** {@inheritDoc} */ + @Override + public void init(final SpacecraftState initialState, final AbsoluteDate target, final EventDetector detector) { + final boolean isForward = target.isAfter(initialState.getDate()); + lastOccurrence = isForward ? AbsoluteDate.PAST_INFINITY : AbsoluteDate.FUTURE_INFINITY; + wrappedHandler.init(initialState, target, detector); + } + + /** {@inheritDoc} */ + @Override + public Action eventOccurred(final SpacecraftState s, final EventDetector detector, final boolean increasing) { + lastOccurrence = s.getDate(); + return wrappedHandler.eventOccurred(s, detector, increasing); + } + + /** {@inheritDoc} */ + @Override + public SpacecraftState resetState(final EventDetector detector, final SpacecraftState oldState) { + return wrappedHandler.resetState(detector, oldState); + } +} diff --git a/src/main/java/org/orekit/propagation/events/intervals/ApsideDetectionAdaptableIntervalFactory.java b/src/main/java/org/orekit/propagation/events/intervals/ApsideDetectionAdaptableIntervalFactory.java new file mode 100644 index 0000000000..a9fbe2b06a --- /dev/null +++ b/src/main/java/org/orekit/propagation/events/intervals/ApsideDetectionAdaptableIntervalFactory.java @@ -0,0 +1,196 @@ +/* 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.intervals; + +import org.hipparchus.util.FastMath; +import org.hipparchus.util.MathUtils; +import org.orekit.orbits.KeplerianOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; +import org.orekit.propagation.events.AdaptableInterval; + +/** + * Factory class for {@link AdaptableInterval} suitable for apside detection on eccentric orbits. + * It requires {@link org.orekit.propagation.SpacecraftState} to be based on {@link Orbit} in order to work. + * @see org.orekit.propagation.events.AdaptableInterval + * @see org.orekit.propagation.events.ApsideDetector + * @see org.orekit.propagation.events.EventSlopeFilter + * @author Romain Serra + * @since 12.1 + */ +public class ApsideDetectionAdaptableIntervalFactory { + + /** + * Private constructor. + */ + private ApsideDetectionAdaptableIntervalFactory() { + // factory class + } + + /** + * Method providing a candidate {@link AdaptableInterval} for arbitrary apside detection with forward propagation. + * It uses a Keplerian, eccentric approximation. + * @return adaptable interval for forward apside detection + */ + public static AdaptableInterval getForwardApsideDetectionAdaptableInterval() { + return state -> { + final Orbit orbit = state.getOrbit(); + final KeplerianOrbit keplerianOrbit = convertOrbitIntoKeplerianOne(orbit); + final double meanMotion = keplerianOrbit.getKeplerianMeanMotion(); + final double meanAnomaly = keplerianOrbit.getMeanAnomaly(); + final double durationToNextPeriapsis = computeKeplerianDurationToNextPeriapsis(meanAnomaly, meanMotion); + final double durationToNextApoapsis = computeKeplerianDurationToNextApoapsis(meanAnomaly, meanMotion); + return FastMath.min(durationToNextPeriapsis, durationToNextApoapsis); + }; + } + + /** + * Method providing a candidate {@link AdaptableInterval} for arbitrary apside detection with backward propagation. + * It uses a Keplerian, eccentric approximation. + * @return adaptable interval for backward apside detection + */ + public static AdaptableInterval getBackwardApsideDetectionAdaptableInterval() { + return state -> { + final Orbit orbit = state.getOrbit(); + final KeplerianOrbit keplerianOrbit = convertOrbitIntoKeplerianOne(orbit); + final double meanMotion = keplerianOrbit.getKeplerianMeanMotion(); + final double meanAnomaly = keplerianOrbit.getMeanAnomaly(); + final double durationFromPreviousPeriapsis = computeKeplerianDurationFromPreviousPeriapsis(meanAnomaly, + meanMotion); + final double durationFromPreviousApoapsis = computeKeplerianDurationFromPreviousApoapsis(meanAnomaly, + meanMotion); + return FastMath.min(durationFromPreviousApoapsis, durationFromPreviousPeriapsis); + }; + } + + /** + * Method providing a candidate {@link AdaptableInterval} for periapsis detection with forward propagation. + * It uses a Keplerian, eccentric approximation. + * @return adaptable interval for forward periaspsis detection + */ + public static AdaptableInterval getForwardPeriapsisDetectionAdaptableInterval() { + return state -> { + final Orbit orbit = state.getOrbit(); + final KeplerianOrbit keplerianOrbit = convertOrbitIntoKeplerianOne(orbit); + final double meanMotion = keplerianOrbit.getKeplerianMeanMotion(); + final double meanAnomaly = keplerianOrbit.getMeanAnomaly(); + return computeKeplerianDurationToNextPeriapsis(meanAnomaly, meanMotion); + }; + } + + /** + * Method providing a candidate {@link AdaptableInterval} for periapsis detection with backward propagation. + * It uses a Keplerian, eccentric approximation. + * @return adaptable interval for backward periaspsis detection + */ + public static AdaptableInterval getBackwardPeriapsisDetectionAdaptableInterval() { + return state -> { + final Orbit orbit = state.getOrbit(); + final KeplerianOrbit keplerianOrbit = convertOrbitIntoKeplerianOne(orbit); + final double meanMotion = keplerianOrbit.getKeplerianMeanMotion(); + final double meanAnomaly = keplerianOrbit.getMeanAnomaly(); + return computeKeplerianDurationFromPreviousPeriapsis(meanAnomaly, meanMotion); + }; + } + + /** + * Method providing a candidate {@link AdaptableInterval} for apoapsis detection with forward propagation. + * It uses a Keplerian, eccentric approximation. + * @return adaptable interval for forward apoapsis detection + */ + public static AdaptableInterval getForwardApoapsisDetectionAdaptableInterval() { + return state -> { + final Orbit orbit = state.getOrbit(); + final KeplerianOrbit keplerianOrbit = convertOrbitIntoKeplerianOne(orbit); + final double meanMotion = keplerianOrbit.getKeplerianMeanMotion(); + final double meanAnomaly = keplerianOrbit.getMeanAnomaly(); + return computeKeplerianDurationToNextApoapsis(meanAnomaly, meanMotion); + }; + } + + /** + * Method providing a candidate {@link AdaptableInterval} for apoapsis detection with backward propagation. + * It uses a Keplerian, eccentric approximation. + * @return adaptable interval for backward apoapsis detection + */ + public static AdaptableInterval getBackwardApoapsisDetectionAdaptableInterval() { + return state -> { + final Orbit orbit = state.getOrbit(); + final KeplerianOrbit keplerianOrbit = convertOrbitIntoKeplerianOne(orbit); + final double meanMotion = keplerianOrbit.getKeplerianMeanMotion(); + final double meanAnomaly = keplerianOrbit.getMeanAnomaly(); + return computeKeplerianDurationFromPreviousApoapsis(meanAnomaly, meanMotion); + }; + } + + /** + * Convert a generic {@link Orbit} into a {@link KeplerianOrbit}. + * @param orbit orbit to convert + * @return Keplerian orbit + */ + private static KeplerianOrbit convertOrbitIntoKeplerianOne(final Orbit orbit) { + return (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(orbit); + } + + /** + * Method computing time to go until next periapsis, assuming Keplerian motion. + * @param meanAnomaly mean anomaly + * @param meanMotion Keplerian mean motion + * @return duration to next periapsis + */ + private static double computeKeplerianDurationToNextPeriapsis(final double meanAnomaly, + final double meanMotion) { + final double normalizedMeanAnomaly = MathUtils.normalizeAngle(meanAnomaly, FastMath.PI); + return (MathUtils.TWO_PI - normalizedMeanAnomaly) / meanMotion; + } + + /** + * Method computing time elapsed since last periapsis, assuming Keplerian motion. + * @param meanAnomaly mean anomaly + * @param meanMotion Keplerian mean motion + * @return duration elapsed since last periapsis + */ + public static double computeKeplerianDurationFromPreviousPeriapsis(final double meanAnomaly, + final double meanMotion) { + final double normalizedMeanAnomaly = MathUtils.normalizeAngle(meanAnomaly, FastMath.PI); + return normalizedMeanAnomaly / meanMotion; + } + + /** + * Method computing time to go until next apoapsis, assuming Keplerian motion. + * @param meanAnomaly mean anomaly + * @param meanMotion Keplerian mean motion + * @return duration to next apoapsis + */ + private static double computeKeplerianDurationToNextApoapsis(final double meanAnomaly, + final double meanMotion) { + final double normalizedMeanAnomaly = MathUtils.normalizeAngle(meanAnomaly, MathUtils.TWO_PI); + return (MathUtils.TWO_PI + FastMath.PI - normalizedMeanAnomaly) / meanMotion; + } + + /** + * Method computing time elapsed since last apoapsis, assuming Keplerian motion. + * @param meanAnomaly mean anomaly + * @param meanMotion Keplerian mean motion + * @return duration elapsed since last apoapsis + */ + public static double computeKeplerianDurationFromPreviousApoapsis(final double meanAnomaly, + final double meanMotion) { + final double normalizedMeanAnomaly = MathUtils.normalizeAngle(meanAnomaly, MathUtils.TWO_PI); + return (normalizedMeanAnomaly - FastMath.PI) / meanMotion; + } +} diff --git a/src/main/java/org/orekit/propagation/events/intervals/ElevationDetectionAdaptableIntervalFactory.java b/src/main/java/org/orekit/propagation/events/intervals/ElevationDetectionAdaptableIntervalFactory.java new file mode 100644 index 0000000000..f444c9e25a --- /dev/null +++ b/src/main/java/org/orekit/propagation/events/intervals/ElevationDetectionAdaptableIntervalFactory.java @@ -0,0 +1,85 @@ +/* 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.propagation.events.intervals; + +import org.hipparchus.util.FastMath; +import org.orekit.frames.TopocentricFrame; +import org.orekit.frames.Transform; +import org.orekit.orbits.Orbit; +import org.orekit.propagation.events.AdaptableInterval; + +/** + * Factory class for {@link AdaptableInterval} suitable for elevation detection on eccentric orbits. + * It requires {@link org.orekit.propagation.SpacecraftState} to be based on {@link Orbit} in order to work. + * @see AdaptableInterval + * @see org.orekit.propagation.events.ApsideDetector + * @see org.orekit.propagation.events.EventSlopeFilter + * @author Luc Maisonobe + * @since 12.1 + */ +public class ElevationDetectionAdaptableIntervalFactory { + + /** Default elevation abovde which interval should be switched to fine interval (-5°). */ + public static final double DEFAULT_ELEVATION_SWITCH = FastMath.toRadians(-5.0); + + /** + * Private constructor. + */ + private ElevationDetectionAdaptableIntervalFactory() { + // factory class + } + + /** + * Method providing a candidate {@link AdaptableInterval} for arbitrary elevation detection with forward propagation. + * It uses a Keplerian, eccentric approximation. + * @param topo topocentric frame centered at ground interest point + * @param elevationSwitch elevation above which interval will switch to {@code fineCheckInterval} + * (typically {@link #DEFAULT_ELEVATION_SWITCH} which is -5°) + * @param fineCheckInterval check interval to use when elevation is above {@code elevationSwitch} + * @return adaptable interval for detection of elevation with respect to {@code topo} + */ + public static AdaptableInterval getAdaptableInterval(final TopocentricFrame topo, + final double elevationSwitch, + final double fineCheckInterval) { + return state -> { + final double elevation = topo.getElevation(state.getPosition(), state.getFrame(), state.getDate()); + if (elevation <= elevationSwitch) { + // we are far from visibility, estimate some large interval with huge margins + + // rotation rate of the topocentric frame + final Transform topoToInertial = topo.getTransformTo(state.getFrame(), state.getDate()); + final double topoAngularVelocity = topoToInertial.getAngular().getRotationRate().getNorm(); + + // max angular rate of spacecraft (i.e. rate at perigee) + final double e = state.getE(); + final double rp = state.getA() * (1 - e); + final double vp = FastMath.sqrt(state.getMu() * (1 + e) / rp); + final double rateP = vp / rp; + + // upper boundary of elevation rate + final double maxElevationRate = topoAngularVelocity + rateP; + + return FastMath.max(fineCheckInterval, (elevationSwitch - elevation) / maxElevationRate); + + } else { + // we are close to visibility, switch to fine check interval + return fineCheckInterval; + } + }; + } + +} diff --git a/src/main/java/org/orekit/propagation/events/intervals/package-info.java b/src/main/java/org/orekit/propagation/events/intervals/package-info.java new file mode 100644 index 0000000000..96b50bff70 --- /dev/null +++ b/src/main/java/org/orekit/propagation/events/intervals/package-info.java @@ -0,0 +1,27 @@ +/* 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 built-in implementations of {@link org.orekit.propagation.events.AdaptableInterval}. + *

          + * They are meant for specific {@link org.orekit.propagation.events.EventDetector} to optimize performance + * (by minimizing the number of evaluations for the so-called g function). + *

          + * @author Romain Serra + * @since 12.1 + */ +package org.orekit.propagation.events.intervals; diff --git a/src/main/java/org/orekit/propagation/events/package-info.java b/src/main/java/org/orekit/propagation/events/package-info.java index 38a8567f71..bc58ea0d07 100644 --- a/src/main/java/org/orekit/propagation/events/package-info.java +++ b/src/main/java/org/orekit/propagation/events/package-info.java @@ -78,12 +78,18 @@ *
        • {@link org.orekit.propagation.events.LatitudeCrossingDetector LatitudeCrossingDetector} * detects satellite crossing a parallel (and by default stop at northward crossing) *
        • + *
        • {@link org.orekit.propagation.events.LatitudeRangeCrossingDetector LatitudeRangeCrossingDetector} + * detects satellite crossing a parallel range (and by default stop exiting range) + *
        • *
        • {@link org.orekit.propagation.events.LatitudeExtremumDetector LatitudeExtremumDetector} * detects satellite maximum/minimum latitude (and by default stop at minimum) *
        • *
        • {@link org.orekit.propagation.events.LongitudeCrossingDetector LongitudeCrossingDetector} * detects satellite crossing a meridian (the increasing/decreasing flag is irrelevant for this detector) *
        • + *
        • {@link org.orekit.propagation.events.LongitudeRangeCrossingDetector LongitudeRangeCrossingDetector} + * detects satellite crossing a meridian range (and by default stop exiting range) + *
        • *
        • {@link org.orekit.propagation.events.LongitudeExtremumDetector LongitudeExtremumDetector} * detects satellite maximum/minimum longitude (and by default stop at minimum) *
        • diff --git a/src/main/java/org/orekit/propagation/integration/AbstractGradientConverter.java b/src/main/java/org/orekit/propagation/integration/AbstractGradientConverter.java index d547654534..215949740f 100644 --- a/src/main/java/org/orekit/propagation/integration/AbstractGradientConverter.java +++ b/src/main/java/org/orekit/propagation/integration/AbstractGradientConverter.java @@ -27,6 +27,7 @@ import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.orekit.attitudes.AttitudeProvider; import org.orekit.attitudes.FieldAttitude; +import org.orekit.orbits.OrbitType; import org.orekit.orbits.FieldCartesianOrbit; import org.orekit.orbits.FieldEquinoctialOrbit; import org.orekit.orbits.FieldOrbit; @@ -252,22 +253,17 @@ public FieldSpacecraftState getState(final ParameterDriversProvider pa final FieldSpacecraftState spacecraftState; if (s0.isOrbitDefined()) { final FieldOrbit orbit = s0.getOrbit(); - if (orbit instanceof FieldEquinoctialOrbit) { + if (orbit.getType().equals(OrbitType.EQUINOCTIAL)) { // for DSST, which always uses EquinoctialOrbit, not CartesianOrbit - // wish there was a way to do this without casting... - final FieldEquinoctialOrbit equinoctialOrbit = - (FieldEquinoctialOrbit) orbit; - final PositionAngleType angleType = - equinoctialOrbit.getCachedPositionAngleType(); spacecraftState = new FieldSpacecraftState<>( new FieldEquinoctialOrbit<>( - extend(equinoctialOrbit.getA(), freeParameters), - extend(equinoctialOrbit.getEquinoctialEx(), freeParameters), - extend(equinoctialOrbit.getEquinoctialEy(), freeParameters), - extend(equinoctialOrbit.getHx(), freeParameters), - extend(equinoctialOrbit.getHy(), freeParameters), - extend(equinoctialOrbit.getL(angleType), freeParameters), - angleType, + extend(orbit.getA(), freeParameters), + extend(orbit.getEquinoctialEx(), freeParameters), + extend(orbit.getEquinoctialEy(), freeParameters), + extend(orbit.getHx(), freeParameters), + extend(orbit.getHy(), freeParameters), + extend(orbit.getLM(), freeParameters), + PositionAngleType.MEAN, s0.getFrame(), extend(s0.getDate(), freeParameters), extend(s0.getMu(), freeParameters) diff --git a/src/main/java/org/orekit/propagation/integration/AbstractIntegratedPropagator.java b/src/main/java/org/orekit/propagation/integration/AbstractIntegratedPropagator.java index 5515e5227d..d8b176c9f6 100644 --- a/src/main/java/org/orekit/propagation/integration/AbstractIntegratedPropagator.java +++ b/src/main/java/org/orekit/propagation/integration/AbstractIntegratedPropagator.java @@ -85,7 +85,7 @@ public abstract class AbstractIntegratedPropagator extends AbstractPropagator { /** Integrator selected by the user for the orbital extrapolation process. */ private final ODEIntegrator integrator; - /** Offsets of secondary states managed by {@link AdditionalEquations}. + /** Offsets of secondary states managed by {@link AdditionalDerivativesProvider}. * @since 11.1 */ private final Map secondaryOffsets; @@ -93,7 +93,7 @@ public abstract class AbstractIntegratedPropagator extends AbstractPropagator { /** Additional derivatives providers. * @since 11.1 */ - private List additionalDerivativesProviders; + private final List additionalDerivativesProviders; /** Map of secondary equation offset in main /** Counter for differential equations calls. */ @@ -102,6 +102,12 @@ public abstract class AbstractIntegratedPropagator extends AbstractPropagator { /** Mapper between raw double components and space flight dynamics objects. */ private StateMapper stateMapper; + /** + * Attitude provider when evaluating derivatives. Can be a frozen one for performance. + * @since 12.1 + */ + private AttitudeProvider attitudeProviderForDerivatives; + /** Flag for resetting the state at end of propagation. */ private boolean resetAtEnd; @@ -111,7 +117,7 @@ public abstract class AbstractIntegratedPropagator extends AbstractPropagator { * mean and short periodic elements. It is ignored by the Numerical propagator. *

          */ - private PropagationType propagationType; + private final PropagationType propagationType; /** Build a new instance. * @param integrator numerical integrator to use for propagation. @@ -151,6 +157,14 @@ public boolean getResetAtEnd() { return this.resetAtEnd; } + /** + * Method called when initializing the attitude provider used when evaluating derivatives. + * @return attitude provider for derivatives + */ + protected AttitudeProvider initializeAttitudeProviderForDerivatives() { + return getAttitudeProvider(); + } + /** Initialize the mapper. */ protected void initMapper() { stateMapper = createMapper(null, Double.NaN, null, null, null, null); @@ -165,6 +179,7 @@ public String getIntegratorName() { } /** {@inheritDoc} */ + @Override public void setAttitudeProvider(final AttitudeProvider attitudeProvider) { super.setAttitudeProvider(attitudeProvider); stateMapper = createMapper(stateMapper.getReferenceDate(), stateMapper.getMu(), @@ -374,6 +389,7 @@ protected abstract StateMapper createMapper(AbsoluteDate referenceDate, double m protected abstract MainStateEquations getMainStateEquations(ODEIntegrator integ); /** {@inheritDoc} */ + @Override public SpacecraftState propagate(final AbsoluteDate target) { if (getStartDate() == null) { if (getInitialState() == null) { @@ -455,7 +471,7 @@ private SpacecraftState integrateDynamics(final AbsoluteDate tEnd, final boolean stateMapper = createMapper(getInitialState().getDate(), stateMapper.getMu(), stateMapper.getOrbitType(), stateMapper.getPositionAngleType(), stateMapper.getAttitudeProvider(), getInitialState().getFrame()); - + attitudeProviderForDerivatives = initializeAttitudeProviderForDerivatives(); if (Double.isNaN(getMu())) { setMu(getInitialState().getMu()); @@ -486,19 +502,7 @@ private SpacecraftState integrateDynamics(final AbsoluteDate tEnd, final boolean mathFinalState.getPrimaryDerivative(), propagationType); - if (!additionalDerivativesProviders.isEmpty()) { - final double[] secondary = mathFinalState.getSecondaryState(1); - final double[] secondaryDerivatives = mathFinalState.getSecondaryDerivative(1); - for (AdditionalDerivativesProvider provider : additionalDerivativesProviders) { - final String name = provider.getName(); - final int offset = secondaryOffsets.get(name); - final int dimension = provider.getDimension(); - finalState = finalState. - addAdditionalState(name, Arrays.copyOfRange(secondary, offset, offset + dimension)). - addAdditionalStateDerivative(name, Arrays.copyOfRange(secondaryDerivatives, offset, offset + dimension)); - } - } - finalState = updateAdditionalStates(finalState); + finalState = updateAdditionalStatesAndDerivatives(finalState, mathFinalState); if (resetAtEnd || forceResetAtEnd) { resetInitialState(finalState); @@ -512,6 +516,31 @@ private SpacecraftState integrateDynamics(final AbsoluteDate tEnd, final boolean } } + /** + * Returns an updated version of the inputted state with additional states, including + * from derivatives providers. + * @param originalState input state + * @param os ODE state and derivative + * @return new state + * @since 12.1 + */ + private SpacecraftState updateAdditionalStatesAndDerivatives(final SpacecraftState originalState, + final ODEStateAndDerivative os) { + SpacecraftState updatedState = originalState; + if (os.getNumberOfSecondaryStates() > 0) { + final double[] secondary = os.getSecondaryState(1); + final double[] secondaryDerivative = os.getSecondaryDerivative(1); + for (final AdditionalDerivativesProvider provider : additionalDerivativesProviders) { + final String name = provider.getName(); + final int offset = secondaryOffsets.get(name); + final int dimension = provider.getDimension(); + updatedState = updatedState.addAdditionalState(name, Arrays.copyOfRange(secondary, offset, offset + dimension)); + updatedState = updatedState.addAdditionalStateDerivative(name, Arrays.copyOfRange(secondaryDerivative, offset, offset + dimension)); + } + } + return updateAdditionalStates(updatedState); + } + /** Get the initial state for integration. * @return initial state for integration */ @@ -648,25 +677,9 @@ protected ODEIntegrator getIntegrator() { */ private SpacecraftState convert(final ODEStateAndDerivative os) { - SpacecraftState s = stateMapper.mapArrayToState(os.getTime(), - os.getPrimaryState(), - os.getPrimaryDerivative(), - propagationType); - if (os.getNumberOfSecondaryStates() > 0) { - final double[] secondary = os.getSecondaryState(1); - final double[] secondaryDerivative = os.getSecondaryDerivative(1); - for (final AdditionalDerivativesProvider provider : additionalDerivativesProviders) { - final String name = provider.getName(); - final int offset = secondaryOffsets.get(name); - final int dimension = provider.getDimension(); - s = s.addAdditionalState(name, Arrays.copyOfRange(secondary, offset, offset + dimension)); - s = s.addAdditionalStateDerivative(name, Arrays.copyOfRange(secondaryDerivative, offset, offset + dimension)); - } - } - s = updateAdditionalStates(s); - - return s; - + final SpacecraftState s = stateMapper.mapArrayToState(os.getTime(), os.getPrimaryState(), + os.getPrimaryDerivative(), propagationType); + return updateAdditionalStatesAndDerivatives(s, os); } /** Convert a state from space flight dynamics world to mathematical world. @@ -738,8 +751,31 @@ public void init(final double t0, final double[] y0, final double finalTime) { // update space dynamics view SpacecraftState initialState = stateMapper.mapArrayToState(t0, y0, null, PropagationType.MEAN); initialState = updateAdditionalStates(initialState); + initialState = updateStatesFromAdditionalDerivativesIfKnown(initialState); final AbsoluteDate target = stateMapper.mapDoubleToDate(finalTime); main.init(initialState, target); + attitudeProviderForDerivatives = initializeAttitudeProviderForDerivatives(); + } + + /** + * Returns an updated version of the inputted state, with additional states from + * derivatives providers as given in the stored initial state. + * @param originalState input state + * @return new state + * @since 12.1 + */ + private SpacecraftState updateStatesFromAdditionalDerivativesIfKnown(final SpacecraftState originalState) { + SpacecraftState updatedState = originalState; + final SpacecraftState storedInitialState = getInitialState(); + final double originalTime = stateMapper.mapDateToDouble(originalState.getDate()); + if (storedInitialState != null && stateMapper.mapDateToDouble(storedInitialState.getDate()) == originalTime) { + for (final AdditionalDerivativesProvider provider: additionalDerivativesProviders) { + final String name = provider.getName(); + final double[] value = storedInitialState.getAdditionalState(name); + updatedState = updatedState.addAdditionalState(name, value); + } + } + return updatedState; } /** {@inheritDoc} */ @@ -749,7 +785,9 @@ public double[] computeDerivatives(final double t, final double[] y) { ++calls; // update space dynamics view + stateMapper.setAttitudeProvider(attitudeProviderForDerivatives); SpacecraftState currentState = stateMapper.mapArrayToState(t, y, null, PropagationType.MEAN); + stateMapper.setAttitudeProvider(getAttitudeProvider()); currentState = updateAdditionalStates(currentState); // compute main state differentials @@ -917,6 +955,7 @@ public BracketedUnivariateSolver getSolver() { } /** {@inheritDoc} */ + @Override public void init(final ODEStateAndDerivative s0, final double t) { detector.init(convert(s0), stateMapper.mapDoubleToDate(t)); this.lastT = Double.NaN; @@ -943,6 +982,7 @@ public Action eventOccurred(final ODEStateAndDerivative s, final ODEEventDetecto } /** {@inheritDoc} */ + @Override public ODEState resetState(final ODEEventDetector d, final ODEStateAndDerivative s) { final SpacecraftState oldState = convert(s); @@ -989,6 +1029,7 @@ private class AdaptedStepHandler implements ODEStepHandler { } /** {@inheritDoc} */ + @Override public void init(final ODEStateAndDerivative s0, final double t) { handler.init(convert(s0), stateMapper.mapDoubleToDate(t)); } diff --git a/src/main/java/org/orekit/propagation/integration/FieldAbstractIntegratedPropagator.java b/src/main/java/org/orekit/propagation/integration/FieldAbstractIntegratedPropagator.java index b7a2a0ac60..b5ca913ae1 100644 --- a/src/main/java/org/orekit/propagation/integration/FieldAbstractIntegratedPropagator.java +++ b/src/main/java/org/orekit/propagation/integration/FieldAbstractIntegratedPropagator.java @@ -88,7 +88,7 @@ public abstract class FieldAbstractIntegratedPropagator integrator; - /** Offsets of secondary states managed by {@link AdditionalEquations}. + /** Offsets of secondary states managed by {@link FieldAdditionalDerivativesProvider}. * @since 11.1 */ private final Map secondaryOffsets; @@ -96,7 +96,7 @@ public abstract class FieldAbstractIntegratedPropagator> additionalDerivativesProviders; + private final List> additionalDerivativesProviders; /** Counter for differential equations calls. */ private int calls; @@ -104,6 +104,12 @@ public abstract class FieldAbstractIntegratedPropagator stateMapper; + /** + * Attitude provider when evaluating derivatives. Can be a frozen one for performance. + * @since 12.1 + */ + private AttitudeProvider attitudeProviderForDerivatives; + /** Flag for resetting the state at end of propagation. */ private boolean resetAtEnd; @@ -113,7 +119,7 @@ public abstract class FieldAbstractIntegratedPropagator */ - private PropagationType propagationType; + private final PropagationType propagationType; /** Build a new instance. * @param integrator numerical integrator to use for propagation. @@ -155,6 +161,14 @@ public boolean getResetAtEnd() { return this.resetAtEnd; } + /** + * Method called when initializing the attitude provider used when evaluating derivatives. + * @return attitude provider for derivatives + */ + protected AttitudeProvider initializeAttitudeProviderForDerivatives() { + return getAttitudeProvider(); + } + /** Initialize the mapper. * @param field Field used by default */ @@ -172,6 +186,7 @@ public String getIntegratorName() { } /** {@inheritDoc} */ + @Override public void setAttitudeProvider(final AttitudeProvider attitudeProvider) { super.setAttitudeProvider(attitudeProvider); stateMapper = createMapper(stateMapper.getReferenceDate(), stateMapper.getMu(), @@ -384,6 +399,7 @@ protected abstract FieldStateMapper createMapper(FieldAbsoluteDate referen protected abstract MainStateEquations getMainStateEquations(FieldODEIntegrator integ); /** {@inheritDoc} */ + @Override public FieldSpacecraftState propagate(final FieldAbsoluteDate target) { if (getStartDate() == null) { if (getInitialState() == null) { @@ -452,9 +468,7 @@ private FieldSpacecraftState integrateDynamics(final FieldAbsoluteDate tEn stateMapper.getOrbitType(), stateMapper.getPositionAngleType(), stateMapper.getAttitudeProvider(), getInitialState().getFrame()); - // set propagation orbit type - //final FieldOrbit initialOrbit = stateMapper.getOrbitType().convertType(getInitialState().getOrbit()); if (Double.isNaN(getMu().getReal())) { setMu(getInitialState().getMu()); } @@ -482,19 +496,8 @@ private FieldSpacecraftState integrateDynamics(final FieldAbsoluteDate tEn mathFinalState.getPrimaryState(), mathFinalState.getPrimaryDerivative(), propagationType); - if (!additionalDerivativesProviders.isEmpty()) { - final T[] secondary = mathFinalState.getSecondaryState(1); - final T[] secondaryDerivatives = mathFinalState.getSecondaryDerivative(1); - for (FieldAdditionalDerivativesProvider provider : additionalDerivativesProviders) { - final String name = provider.getName(); - final int offset = secondaryOffsets.get(name); - final int dimension = provider.getDimension(); - finalState = finalState. - addAdditionalState(name, Arrays.copyOfRange(secondary, offset, offset + dimension)). - addAdditionalStateDerivative(name, Arrays.copyOfRange(secondaryDerivatives, offset, offset + dimension)); - } - } - finalState = updateAdditionalStates(finalState); + + finalState = updateAdditionalStatesAndDerivatives(finalState, mathFinalState); if (resetAtEnd) { resetInitialState(finalState); @@ -510,6 +513,31 @@ private FieldSpacecraftState integrateDynamics(final FieldAbsoluteDate tEn } } + /** + * Returns an updated version of the inputted state with additional states, including + * from derivatives providers. + * @param originalState input state + * @param os ODE state and derivative + * @return new state + * @since 12.1 + */ + private FieldSpacecraftState updateAdditionalStatesAndDerivatives(final FieldSpacecraftState originalState, + final FieldODEStateAndDerivative os) { + FieldSpacecraftState updatedState = originalState; + if (os.getNumberOfSecondaryStates() > 0) { + final T[] secondary = os.getSecondaryState(1); + final T[] secondaryDerivative = os.getSecondaryDerivative(1); + for (final FieldAdditionalDerivativesProvider provider : additionalDerivativesProviders) { + final String name = provider.getName(); + final int offset = secondaryOffsets.get(name); + final int dimension = provider.getDimension(); + updatedState = updatedState.addAdditionalState(name, Arrays.copyOfRange(secondary, offset, offset + dimension)); + updatedState = updatedState.addAdditionalStateDerivative(name, Arrays.copyOfRange(secondaryDerivative, offset, offset + dimension)); + } + } + return updateAdditionalStates(updatedState); + } + /** Get the initial state for integration. * @return initial state for integration */ @@ -739,9 +767,33 @@ public void init(final T t0, final T[] y0, final T finalTime) { // update space dynamics view FieldSpacecraftState initialState = stateMapper.mapArrayToState(t0, y0, null, PropagationType.MEAN); initialState = updateAdditionalStates(initialState); + initialState = updateStatesFromAdditionalDerivativesIfKnown(initialState); final FieldAbsoluteDate target = stateMapper.mapDoubleToDate(finalTime); main.init(initialState, target); + attitudeProviderForDerivatives = initializeAttitudeProviderForDerivatives(); + } + + /** + * Returns an updated version of the inputted state, with additional states from + * derivatives providers as given in the stored initial state. + * @param originalState input state + * @return new state + * @since 12.1 + */ + private FieldSpacecraftState updateStatesFromAdditionalDerivativesIfKnown(final FieldSpacecraftState originalState) { + FieldSpacecraftState updatedState = originalState; + final FieldSpacecraftState storedInitialState = getInitialState(); + final T originalTime = stateMapper.mapDateToDouble(originalState.getDate()); + if (storedInitialState != null && stateMapper.mapDateToDouble(storedInitialState.getDate()).subtract(originalTime).isZero()) { + for (final FieldAdditionalDerivativesProvider provider: additionalDerivativesProviders) { + final String name = provider.getName(); + final T[] value = storedInitialState.getAdditionalState(name); + updatedState = updatedState.addAdditionalState(name, value); + } + } + return updatedState; } + /** {@inheritDoc} */ public T[] computeDerivatives(final T t, final T[] y) { @@ -749,7 +801,9 @@ public T[] computeDerivatives(final T t, final T[] y) { ++calls; // update space dynamics view + stateMapper.setAttitudeProvider(attitudeProviderForDerivatives); FieldSpacecraftState currentState = stateMapper.mapArrayToState(t, y, null, PropagationType.MEAN); + stateMapper.setAttitudeProvider(getAttitudeProvider()); currentState = updateAdditionalStates(currentState); // compute main state differentials @@ -870,7 +924,6 @@ private FieldSpacecraftState convert(final T t, final T[] primary, /** Adapt an {@link org.orekit.propagation.events.FieldEventDetector} * to Hipparchus {@link org.hipparchus.ode.events.FieldODEEventDetector} interface. - * @param class type for the generic version * @author Fabien Maussion */ private class FieldAdaptedEventDetector implements FieldODEEventDetector { @@ -919,6 +972,7 @@ public FieldBracketingNthOrderBrentSolver getSolver() { } /** {@inheritDoc} */ + @Override public void init(final FieldODEStateAndDerivative s0, final T t) { detector.init(convert(s0), stateMapper.mapDoubleToDate(t)); this.lastT = getField().getZero().add(Double.NaN); @@ -947,6 +1001,7 @@ public Action eventOccurred(final FieldODEStateAndDerivative s, } /** {@inheritDoc} */ + @Override public FieldODEState resetState(final FieldODEEventDetector d, final FieldODEStateAndDerivative s) { @@ -993,6 +1048,7 @@ private class FieldAdaptedStepHandler implements FieldODEStepHandler { } /** {@inheritDoc} */ + @Override public void init(final FieldODEStateAndDerivative s0, final T t) { handler.init(convert(s0), stateMapper.mapDoubleToDate(t)); } diff --git a/src/main/java/org/orekit/propagation/integration/FieldIntegratedEphemeris.java b/src/main/java/org/orekit/propagation/integration/FieldIntegratedEphemeris.java index 5087ae03c7..95dfed8801 100644 --- a/src/main/java/org/orekit/propagation/integration/FieldIntegratedEphemeris.java +++ b/src/main/java/org/orekit/propagation/integration/FieldIntegratedEphemeris.java @@ -35,7 +35,6 @@ import org.orekit.time.FieldAbsoluteDate; import org.orekit.utils.FieldArrayDictionary; import org.orekit.utils.ParameterDriver; -import org.orekit.utils.TimeStampedFieldPVCoordinates; /** This class stores sequentially generated orbital parameters for * later retrieval. @@ -203,12 +202,6 @@ protected T getMass(final FieldAbsoluteDate date) { return basicPropagate(date).getMass(); } - /** {@inheritDoc} */ - @Override - public TimeStampedFieldPVCoordinates getPVCoordinates(final FieldAbsoluteDate date, final Frame frame) { - return propagate(date).getPVCoordinates(frame); - } - /** Get the first date of the range. * @return the first date of the range */ diff --git a/src/main/java/org/orekit/propagation/integration/FieldStateMapper.java b/src/main/java/org/orekit/propagation/integration/FieldStateMapper.java index 6abe8fe972..a365481227 100644 --- a/src/main/java/org/orekit/propagation/integration/FieldStateMapper.java +++ b/src/main/java/org/orekit/propagation/integration/FieldStateMapper.java @@ -41,7 +41,7 @@ public abstract class FieldStateMapper> { private final PositionAngleType angleType; /** Attitude provider. */ - private final AttitudeProvider attitudeProvider; + private AttitudeProvider attitudeProvider; /** Central attraction coefficient. */ private final T mu; @@ -121,6 +121,14 @@ public AttitudeProvider getAttitudeProvider() { return attitudeProvider; } + /** + * Setter for the attitude provider. + * @param attitudeProvider new attitude provider + */ + public void setAttitudeProvider(final AttitudeProvider attitudeProvider) { + this.attitudeProvider = attitudeProvider; + } + /** Map the raw double time offset to a date. * @param t date offset * @return date diff --git a/src/main/java/org/orekit/propagation/integration/IntegratedEphemeris.java b/src/main/java/org/orekit/propagation/integration/IntegratedEphemeris.java index 24ccced7fe..5a76c9155e 100644 --- a/src/main/java/org/orekit/propagation/integration/IntegratedEphemeris.java +++ b/src/main/java/org/orekit/propagation/integration/IntegratedEphemeris.java @@ -33,7 +33,6 @@ import org.orekit.propagation.analytical.AbstractAnalyticalPropagator; import org.orekit.time.AbsoluteDate; import org.orekit.utils.DoubleArrayDictionary; -import org.orekit.utils.TimeStampedPVCoordinates; /** This class stores sequentially generated orbital parameters for * later retrieval. @@ -197,11 +196,6 @@ protected double getMass(final AbsoluteDate date) { return basicPropagate(date).getMass(); } - /** {@inheritDoc} */ - public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame frame) { - return propagate(date).getPVCoordinates(frame); - } - /** Get the first date of the range. * @return the first date of the range */ diff --git a/src/main/java/org/orekit/propagation/numerical/FieldNumericalPropagator.java b/src/main/java/org/orekit/propagation/numerical/FieldNumericalPropagator.java index fef454c66a..347b50c399 100644 --- a/src/main/java/org/orekit/propagation/numerical/FieldNumericalPropagator.java +++ b/src/main/java/org/orekit/propagation/numerical/FieldNumericalPropagator.java @@ -28,6 +28,7 @@ import org.hipparchus.util.MathArrays; import org.orekit.annotation.DefaultDataContext; import org.orekit.attitudes.AttitudeProvider; +import org.orekit.attitudes.AttitudeProviderModifier; import org.orekit.attitudes.FieldAttitude; import org.orekit.data.DataContext; import org.orekit.errors.OrekitException; @@ -126,7 +127,7 @@ * *

          By default, at the end of the propagation, the propagator resets the initial state to the final state, * thus allowing a new propagation to be started from there without recomputing the part already performed. - * This behaviour can be chenged by calling {@link #setResetAtEnd(boolean)}. + * This behaviour can be changed by calling {@link #setResetAtEnd(boolean)}. *

          *

          Beware the same instance cannot be used simultaneously by different threads, the class is not * thread-safe.

          @@ -156,6 +157,11 @@ public class FieldNumericalPropagator> extends /** boolean to ignore or not the creation of a NewtonianAttraction. */ private boolean ignoreCentralAttraction = false; + /** + * boolean to know if a full attitude (with rates) is needed when computing derivatives for the ODE. + */ + private boolean needFullAttitudeForDerivatives = true; + /** Create a new instance of NumericalPropagator, based on orbit definition mu. * After creation, the instance is empty, i.e. the attitude provider is set to an * unspecified default law and there are no perturbing forces at all. @@ -196,7 +202,7 @@ public FieldNumericalPropagator(final Field field, final AttitudeProvider attitudeProvider) { super(field, integrator, PropagationType.OSCULATING); this.field = field; - forceModels = new ArrayList(); + forceModels = new ArrayList<>(); initMapper(field); setAttitudeProvider(attitudeProvider); setMu(field.getZero().add(Double.NaN)); @@ -213,16 +219,17 @@ public void setIgnoreCentralAttraction(final boolean ignoreCentralAttraction) { this.ignoreCentralAttraction = ignoreCentralAttraction; } - /** Set the central attraction coefficient μ. - *

          - * Setting the central attraction coefficient is - * equivalent to {@link #addForceModel(ForceModel) add} - * a {@link NewtonianAttraction} force model. - *

          + /** Set the central attraction coefficient μ. + *

          + * Setting the central attraction coefficient is + * equivalent to {@link #addForceModel(ForceModel) add} + * a {@link NewtonianAttraction} force model. + *

          * @param mu central attraction coefficient (m³/s²) * @see #addForceModel(ForceModel) * @see #getAllForceModels() */ + @Override public void setMu(final T mu) { if (ignoreCentralAttraction) { superSetMu(mu); @@ -268,13 +275,13 @@ public void addForceModel(final ForceModel model) { @Override public void valueChanged(final double previousValue, final ParameterDriver driver, final AbsoluteDate date) { // mu PDriver should have only 1 span - superSetMu(field.getZero().add(driver.getValue(date))); + superSetMu(field.getZero().newInstance(driver.getValue(date))); } /** {@inheritDoc} */ @Override public void valueSpanMapChanged(final TimeSpanMap previousValue, final ParameterDriver driver) { // mu PDriver should have only 1 span - superSetMu(field.getZero().add(driver.getValue())); + superSetMu(field.getZero().newInstance(driver.getValue())); } }); } catch (OrekitException oe) { @@ -327,6 +334,7 @@ public List getAllForceModels() { /** Set propagation orbit type. * @param orbitType orbit type to use for propagation */ + @Override public void setOrbitType(final OrbitType orbitType) { super.setOrbitType(orbitType); } @@ -334,6 +342,7 @@ public void setOrbitType(final OrbitType orbitType) { /** Get propagation parameter type. * @return orbit type used for propagation */ + @Override public OrbitType getOrbitType() { return superGetOrbitType(); } @@ -354,6 +363,7 @@ private OrbitType superGetOrbitType() { *

          * @param positionAngleType angle type to use for propagation */ + @Override public void setPositionAngleType(final PositionAngleType positionAngleType) { super.setPositionAngleType(positionAngleType); } @@ -361,6 +371,7 @@ public void setPositionAngleType(final PositionAngleType positionAngleType) { /** Get propagation parameter type. * @return angle type to use for propagation */ + @Override public PositionAngleType getPositionAngleType() { return super.getPositionAngleType(); } @@ -373,6 +384,7 @@ public void setInitialState(final FieldSpacecraftState initialState) { } /** {@inheritDoc} */ + @Override public void resetInitialState(final FieldSpacecraftState state) { super.resetInitialState(state); if (!hasNewtonianAttraction()) { @@ -382,8 +394,11 @@ public void resetInitialState(final FieldSpacecraftState state) { } /** {@inheritDoc} */ - public TimeStampedFieldPVCoordinates getPVCoordinates(final FieldAbsoluteDate date, final Frame frame) { - return propagate(date).getPVCoordinates(frame); + @Override + protected AttitudeProvider initializeAttitudeProviderForDerivatives() { + final AttitudeProvider attitudeProvider = getAttitudeProvider(); + return needFullAttitudeForDerivatives ? attitudeProvider : + AttitudeProviderModifier.getFrozenAttitudeProvider(attitudeProvider); } /** {@inheritDoc} */ @@ -488,6 +503,9 @@ private class Main implements MainStateEquations, FieldTimeDerivativesEquatio /** Jacobian of the orbital parameters with respect to the Cartesian parameters. */ private T[][] jacobian; + /** Flag keeping track whether Jacobian matrix needs to be recomputed or not. */ + private boolean recomputingJacobian; + /** Simple constructor. * @param integrator numerical integrator to use for propagation. */ @@ -495,17 +513,16 @@ private class Main implements MainStateEquations, FieldTimeDerivativesEquatio this.yDot = MathArrays.buildArray(getField(), 7); this.jacobian = MathArrays.buildArray(getField(), 6, 6); + this.recomputingJacobian = true; + for (final ForceModel forceModel : forceModels) { forceModel.getFieldEventDetectors(getField()).forEach(detector -> setUpEventDetector(integrator, detector)); } - if (superGetOrbitType() == null) { - // propagation uses absolute position-velocity-acceleration - // we can set Jacobian once and for all - for (int i = 0; i < jacobian.length; ++i) { - Arrays.fill(jacobian[i], getField().getZero()); - jacobian[i][i] = getField().getOne(); - } + // default value for Jacobian is identity + for (int i = 0; i < jacobian.length; ++i) { + Arrays.fill(jacobian[i], getField().getZero()); + jacobian[i][i] = getField().getOne(); } } @@ -513,7 +530,21 @@ private class Main implements MainStateEquations, FieldTimeDerivativesEquatio /** {@inheritDoc} */ @Override public void init(final FieldSpacecraftState initialState, final FieldAbsoluteDate target) { + needFullAttitudeForDerivatives = forceModels.stream().anyMatch(ForceModel::dependsOnAttitudeRate); + forceModels.forEach(fm -> fm.init(initialState, target)); + + final int numberOfForces = forceModels.size(); + final OrbitType orbitType = superGetOrbitType(); + if (orbitType != null && orbitType != OrbitType.CARTESIAN && numberOfForces > 0) { + if (numberOfForces > 1) { + recomputingJacobian = true; + } else { + recomputingJacobian = !(forceModels.get(0) instanceof NewtonianAttraction); + } + } else { + recomputingJacobian = false; + } } /** {@inheritDoc} */ @@ -522,8 +553,8 @@ public T[] computeDerivatives(final FieldSpacecraftState state) { final T zero = state.getA().getField().getZero(); currentState = state; Arrays.fill(yDot, zero); - if (superGetOrbitType() != null) { - // propagation uses regular orbits + if (recomputingJacobian) { + // propagation uses Jacobian matrix of orbital parameters w.r.t. Cartesian ones currentState.getOrbit().getJacobianWrtCartesian(getPositionAngleType(), jacobian); } diff --git a/src/main/java/org/orekit/propagation/numerical/GLONASSNumericalPropagator.java b/src/main/java/org/orekit/propagation/numerical/GLONASSNumericalPropagator.java index 63dbe80dc8..016334bda4 100644 --- a/src/main/java/org/orekit/propagation/numerical/GLONASSNumericalPropagator.java +++ b/src/main/java/org/orekit/propagation/numerical/GLONASSNumericalPropagator.java @@ -16,8 +16,6 @@ */ package org.orekit.propagation.numerical; -import java.util.Arrays; - import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.ode.ODEIntegrator; import org.hipparchus.ode.nonstiff.ClassicalRungeKuttaIntegrator; @@ -50,6 +48,8 @@ import org.orekit.utils.PVCoordinates; import org.orekit.utils.TimeStampedPVCoordinates; +import java.util.Arrays; + /** * This class propagates GLONASS orbits using numerical integration. *

          @@ -214,13 +214,11 @@ public SpacecraftState propagate(final AbsoluteDate date) { dataContext.getFrames().getPZ9011(IERSConventions.IERS_2010, true), stateInInertial.getDate(), pvInPZ90); final TimeStampedPVCoordinates pvInInertial = absPV.getPVCoordinates(eci); - final SpacecraftState transformedState = new SpacecraftState(new CartesianOrbit(pvInInertial, eci, pvInInertial.getDate(), GNSSConstants.GLONASS_MU), - stateInInertial.getAttitude(), - stateInInertial.getMass(), - stateInInertial.getAdditionalStatesValues(), - stateInInertial.getAdditionalStatesDerivatives()); - - return transformedState; + return new SpacecraftState(new CartesianOrbit(pvInInertial, eci, pvInInertial.getDate(), GNSSConstants.GLONASS_MU), + stateInInertial.getAttitude(), + stateInInertial.getMass(), + stateInInertial.getAdditionalStatesValues(), + stateInInertial.getAdditionalStatesDerivatives()); } /** @@ -464,7 +462,7 @@ private static double eMeSinE(final double E, final double e) { double term = E; double d = 0; // the inequality test below IS intentional and should NOT be replaced by a check with a small tolerance - for (double x0 = Double.NaN; !Double.valueOf(x).equals(Double.valueOf(x0));) { + for (double x0 = Double.NaN; !Double.valueOf(x).equals(x0);) { d += 2; term *= mE2 / (d * (d + 1)); x0 = x; diff --git a/src/main/java/org/orekit/propagation/numerical/NumericalPropagator.java b/src/main/java/org/orekit/propagation/numerical/NumericalPropagator.java index 3bec3beba1..75b25b7c52 100644 --- a/src/main/java/org/orekit/propagation/numerical/NumericalPropagator.java +++ b/src/main/java/org/orekit/propagation/numerical/NumericalPropagator.java @@ -32,6 +32,7 @@ import org.orekit.annotation.DefaultDataContext; import org.orekit.attitudes.Attitude; import org.orekit.attitudes.AttitudeProvider; +import org.orekit.attitudes.AttitudeProviderModifier; import org.orekit.data.DataContext; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitIllegalArgumentException; @@ -146,7 +147,7 @@ * *

          By default, at the end of the propagation, the propagator resets the initial state to the final state, * thus allowing a new propagation to be started from there without recomputing the part already performed. - * This behaviour can be chenged by calling {@link #setResetAtEnd(boolean)}. + * This behaviour can be changed by calling {@link #setResetAtEnd(boolean)}. *

          *

          Beware the same instance cannot be used simultaneously by different threads, the class is not * thread-safe.

          @@ -181,6 +182,12 @@ public class NumericalPropagator extends AbstractIntegratedPropagator { /** boolean to ignore or not the creation of a NewtonianAttraction. */ private boolean ignoreCentralAttraction; + /** + * boolean to know if a full attitude (with rates) is needed when computing derivatives for the ODE. + * since 12.1 + */ + private boolean needFullAttitudeForDerivatives = true; + /** Create a new instance of NumericalPropagator, based on orbit definition mu. * After creation, the instance is empty, i.e. the attitude provider is set to an * unspecified default law and there are no perturbing forces at all. @@ -218,7 +225,7 @@ public NumericalPropagator(final ODEIntegrator integrator) { public NumericalPropagator(final ODEIntegrator integrator, final AttitudeProvider attitudeProvider) { super(integrator, PropagationType.OSCULATING); - forceModels = new ArrayList(); + forceModels = new ArrayList<>(); ignoreCentralAttraction = false; initMapper(); setAttitudeProvider(attitudeProvider); @@ -240,11 +247,12 @@ public void setIgnoreCentralAttraction(final boolean ignoreCentralAttraction) { * Setting the central attraction coefficient is * equivalent to {@link #addForceModel(ForceModel) add} * a {@link NewtonianAttraction} force model. - *

          - * @param mu central attraction coefficient (m³/s²) - * @see #addForceModel(ForceModel) - * @see #getAllForceModels() - */ + * *

          + * @param mu central attraction coefficient (m³/s²) + * @see #addForceModel(ForceModel) + * @see #getAllForceModels() + */ + @Override public void setMu(final double mu) { if (ignoreCentralAttraction) { superSetMu(mu); @@ -357,6 +365,7 @@ public List getAllForceModels() { * @param orbitType orbit type to use for propagation, null for * propagating using {@link org.orekit.utils.AbsolutePVCoordinates} rather than {@link Orbit} */ + @Override public void setOrbitType(final OrbitType orbitType) { super.setOrbitType(orbitType); } @@ -365,6 +374,7 @@ public void setOrbitType(final OrbitType orbitType) { * @return orbit type used for propagation, null for * propagating using {@link org.orekit.utils.AbsolutePVCoordinates} rather than {@link Orbit} */ + @Override public OrbitType getOrbitType() { return super.getOrbitType(); } @@ -378,6 +388,7 @@ public OrbitType getOrbitType() { *

          * @param positionAngleType angle type to use for propagation */ + @Override public void setPositionAngleType(final PositionAngleType positionAngleType) { super.setPositionAngleType(positionAngleType); } @@ -385,6 +396,7 @@ public void setPositionAngleType(final PositionAngleType positionAngleType) { /** Get propagation parameter type. * @return angle type to use for propagation */ + @Override public PositionAngleType getPositionAngleType() { return super.getPositionAngleType(); } @@ -397,6 +409,7 @@ public void setInitialState(final SpacecraftState initialState) { } /** {@inheritDoc} */ + @Override public void resetInitialState(final SpacecraftState state) { super.resetInitialState(state); if (!hasNewtonianAttraction()) { @@ -793,8 +806,10 @@ private void setInitialColumn(final String columnName, final double[] dYdQ) { /** {@inheritDoc} */ @Override - public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame frame) { - return propagate(date).getPVCoordinates(frame); + protected AttitudeProvider initializeAttitudeProviderForDerivatives() { + final AttitudeProvider attitudeProvider = getAttitudeProvider(); + return needFullAttitudeForDerivatives ? attitudeProvider : + AttitudeProviderModifier.getFrozenAttitudeProvider(attitudeProvider); } /** {@inheritDoc} */ @@ -902,6 +917,9 @@ private class Main implements MainStateEquations, TimeDerivativesEquations { /** Jacobian of the orbital parameters with respect to the Cartesian parameters. */ private double[][] jacobian; + /** Flag keeping track whether Jacobian matrix needs to be recomputed or not. */ + private boolean recomputingJacobian; + /** Simple constructor. * @param integrator numerical integrator to use for propagation. */ @@ -909,18 +927,16 @@ private class Main implements MainStateEquations, TimeDerivativesEquations { this.yDot = new double[7]; this.jacobian = new double[6][6]; + this.recomputingJacobian = true; for (final ForceModel forceModel : forceModels) { forceModel.getEventDetectors().forEach(detector -> setUpEventDetector(integrator, detector)); } - if (getOrbitType() == null) { - // propagation uses absolute position-velocity-acceleration - // we can set Jacobian once and for all - for (int i = 0; i < jacobian.length; ++i) { - Arrays.fill(jacobian[i], 0.0); - jacobian[i][i] = 1.0; - } + // default value for Jacobian is identity + for (int i = 0; i < jacobian.length; ++i) { + Arrays.fill(jacobian[i], 0.0); + jacobian[i][i] = 1.0; } } @@ -928,7 +944,21 @@ private class Main implements MainStateEquations, TimeDerivativesEquations { /** {@inheritDoc} */ @Override public void init(final SpacecraftState initialState, final AbsoluteDate target) { + needFullAttitudeForDerivatives = forceModels.stream().anyMatch(ForceModel::dependsOnAttitudeRate); + forceModels.forEach(fm -> fm.init(initialState, target)); + + final int numberOfForces = forceModels.size(); + final OrbitType orbitType = getOrbitType(); + if (orbitType != null && orbitType != OrbitType.CARTESIAN && numberOfForces > 0) { + if (numberOfForces > 1) { + recomputingJacobian = true; + } else { + recomputingJacobian = !(forceModels.get(0) instanceof NewtonianAttraction); + } + } else { + recomputingJacobian = false; + } } /** {@inheritDoc} */ @@ -937,8 +967,8 @@ public double[] computeDerivatives(final SpacecraftState state) { currentState = state; Arrays.fill(yDot, 0.0); - if (getOrbitType() != null) { - // propagation uses regular orbits + if (recomputingJacobian) { + // propagation uses Jacobian matrix of orbital parameters w.r.t. Cartesian ones currentState.getOrbit().getJacobianWrtCartesian(getPositionAngleType(), jacobian); } diff --git a/src/main/java/org/orekit/propagation/numerical/StateTransitionMatrixGenerator.java b/src/main/java/org/orekit/propagation/numerical/StateTransitionMatrixGenerator.java index df8b71a015..c647d60e4c 100644 --- a/src/main/java/org/orekit/propagation/numerical/StateTransitionMatrixGenerator.java +++ b/src/main/java/org/orekit/propagation/numerical/StateTransitionMatrixGenerator.java @@ -28,6 +28,7 @@ import org.hipparchus.linear.RealMatrix; import org.hipparchus.util.Precision; import org.orekit.attitudes.AttitudeProvider; +import org.orekit.attitudes.AttitudeProviderModifier; import org.orekit.errors.OrekitException; import org.orekit.forces.ForceModel; import org.orekit.orbits.OrbitType; @@ -83,7 +84,7 @@ class StateTransitionMatrixGenerator implements AdditionalDerivativesProvider { /** Register an observer for partial derivatives. *

          - * The observer {@link PartialsObserver#partialsComputed(double[], double[]) partialsComputed} + * The observer {@link PartialsObserver#partialsComputed(SpacecraftState, double[], double[])} partialsComputed} * method will be called when partial derivatives are computed, as a side effect of * calling {@link #generate(SpacecraftState)} *

          @@ -240,8 +241,11 @@ private double[] computePartials(final SpacecraftState state) { final DoubleArrayDictionary accelerationPartials = new DoubleArrayDictionary(); // evaluate contribution of all force models - final NumericalGradientConverter fullConverter = new NumericalGradientConverter(state, STATE_DIMENSION, attitudeProvider); - final NumericalGradientConverter posOnlyConverter = new NumericalGradientConverter(state, SPACE_DIMENSION, attitudeProvider); + final AttitudeProvider equivalentAttitudeProvider = wrapAttitudeProviderIfPossible(forceModels, attitudeProvider); + final boolean isThereAnyForceNotDependingOnlyOnPosition = forceModels.stream().anyMatch(force -> !force.dependsOnPositionOnly()); + final NumericalGradientConverter posOnlyConverter = new NumericalGradientConverter(state, SPACE_DIMENSION, equivalentAttitudeProvider); + final NumericalGradientConverter fullConverter = isThereAnyForceNotDependingOnlyOnPosition ? + new NumericalGradientConverter(state, STATE_DIMENSION, equivalentAttitudeProvider) : posOnlyConverter; for (final ForceModel forceModel : forceModels) { @@ -314,6 +318,26 @@ private double[] computePartials(final SpacecraftState state) { } + /** + * Method that first checks if it is possible to replace the attitude provider with a computationally cheaper one + * to evaluate. If applicable, the new provider only computes the rotation and uses dummy rate and acceleration, + * since they should not be used later on. + * @param forceModels list of forces + * @param attitudeProvider original attitude provider + * @return same provider if at least one forces used attitude derivatives, otherwise one wrapping the old one for + * the rotation + */ + private static AttitudeProvider wrapAttitudeProviderIfPossible(final List forceModels, + final AttitudeProvider attitudeProvider) { + if (forceModels.stream().anyMatch(ForceModel::dependsOnAttitudeRate)) { + // at least one force uses an attitude rate, need to keep the original provider + return attitudeProvider; + } else { + // the original provider can be replaced by a lighter one for performance + return AttitudeProviderModifier.getFrozenAttitudeProvider(attitudeProvider); + } + } + /** Interface for observing partials derivatives. */ public interface PartialsObserver { diff --git a/src/main/java/org/orekit/propagation/numerical/cr3bp/CR3BPForceModel.java b/src/main/java/org/orekit/propagation/numerical/cr3bp/CR3BPForceModel.java index cbc6c9d638..470139d6b5 100644 --- a/src/main/java/org/orekit/propagation/numerical/cr3bp/CR3BPForceModel.java +++ b/src/main/java/org/orekit/propagation/numerical/cr3bp/CR3BPForceModel.java @@ -143,7 +143,7 @@ public DerivativeStructure getPotential(final SpacecraftState s) { // Get CR3BP System mass ratio // By construction, mudriver has 1 value for the all time period that is why // the getValue can be called with any date argument or null argument - final DerivativeStructure mu = zero.add(muParameterDriver.getValue(s.getDate())); + final DerivativeStructure mu = zero.newInstance(muParameterDriver.getValue(s.getDate())); // Normalized distances between primaries and barycenter in CR3BP final DerivativeStructure d1 = mu; @@ -151,17 +151,17 @@ public DerivativeStructure getPotential(final SpacecraftState s) { // Norm of the Spacecraft position relative to the primary body final DerivativeStructure r1 = - FastMath.sqrt((fpx.add(d1)).multiply(fpx.add(d1)).add(fpy.multiply(fpy)) - .add(fpz.multiply(fpz))); + FastMath.sqrt((fpx.add(d1)).multiply(fpx.add(d1)).add(fpy.square()) + .add(fpz.square())); // Norm of the Spacecraft position relative to the secondary body final DerivativeStructure r2 = FastMath.sqrt((fpx.subtract(d2)).multiply(fpx.subtract(d2)) - .add(fpy.multiply(fpy)).add(fpz.multiply(fpz))); + .add(fpy.square()).add(fpz.square())); // Potential of the Spacecraft return (mu.negate().add(1.0).divide(r1)).add(mu.divide(r2)) - .add(fpx.multiply(fpx).add(fpy.multiply(fpy)).multiply(0.5)).add(d1.multiply(d2).multiply(0.5)); + .add(fpx.square().add(fpy.square()).multiply(0.5)).add(d1.multiply(d2).multiply(0.5)); } /** @@ -186,7 +186,7 @@ public > FieldDerivativeStructure getPotent // Get CR3BP System mass ratio // By construction, mudriver has 1 value for the all time period that is why // the getValue can be called with any date argument or null argument - final FieldDerivativeStructure mu = zero.add(muParameterDriver.getValue(s.getDate().toAbsoluteDate())); + final FieldDerivativeStructure mu = zero.newInstance(muParameterDriver.getValue(s.getDate().toAbsoluteDate())); // Normalized distances between primaries and barycenter in CR3BP final FieldDerivativeStructure d1 = mu; @@ -194,17 +194,17 @@ public > FieldDerivativeStructure getPotent // Norm of the Spacecraft position relative to the primary body final FieldDerivativeStructure r1 = - FastMath.sqrt((fpx.add(d1)).multiply(fpx.add(d1)).add(fpy.multiply(fpy)) - .add(fpz.multiply(fpz))); + FastMath.sqrt((fpx.add(d1)).multiply(fpx.add(d1)).add(fpy.square()) + .add(fpz.square())); // Norm of the Spacecraft position relative to the secondary body final FieldDerivativeStructure r2 = FastMath.sqrt((fpx.subtract(d2)).multiply(fpx.subtract(d2)) - .add(fpy.multiply(fpy)).add(fpz.multiply(fpz))); + .add(fpy.square()).add(fpz.square())); // Potential of the Spacecraft return (mu.negate().add(1.0).divide(r1)).add(mu.divide(r2)) - .add(fpx.multiply(fpx).add(fpy.multiply(fpy)).multiply(0.5)).add(d1.multiply(d2).multiply(0.5)); + .add(fpx.square().add(fpy.square()).multiply(0.5)).add(d1.multiply(d2).multiply(0.5)); } /** {@inheritDoc} */ 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 241879c3bd..a031e60225 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/DSSTPropagator.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/DSSTPropagator.java @@ -16,14 +16,6 @@ */ 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; @@ -67,6 +59,14 @@ 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. *

          @@ -204,7 +204,7 @@ public DSSTPropagator(final ODEIntegrator integrator, final PropagationType propagationType, final AttitudeProvider attitudeProvider) { super(integrator, propagationType); - forceModels = new ArrayList(); + forceModels = new ArrayList<>(); initMapper(); // DSST uses only equinoctial orbits and mean longitude argument setOrbitType(OrbitType.EQUINOCTIAL); @@ -310,8 +310,7 @@ public void resetInitialState(final SpacecraftState state) { * (null means no coefficients are selected, empty set means all coefficients are selected) */ public void setSelectedCoefficients(final Set selectedCoefficients) { - mapper.setSelectedCoefficients(selectedCoefficients == null ? - null : new HashSet(selectedCoefficients)); + mapper.setSelectedCoefficients(selectedCoefficients == null ? null : new HashSet<>(selectedCoefficients)); } /** Get the selected short periodic coefficients that must be stored as additional states. @@ -653,7 +652,7 @@ public static SpacecraftState computeOsculatingState(final SpacecraftState mean, final AuxiliaryElements aux = new AuxiliaryElements(mean.getOrbit(), I); // Set the force models - final List shortPeriodTerms = new ArrayList(); + final List shortPeriodTerms = new ArrayList<>(); for (final DSSTForceModel force : forces) { force.registerAttitudeProvider(attitudeProvider); shortPeriodTerms.addAll(force.initializeShortPeriodTerms(aux, PropagationType.OSCULATING, force.getParameters(mean.getDate()))); @@ -790,7 +789,7 @@ protected void beforeIntegration(final SpacecraftState initialState, final AuxiliaryElements aux = new AuxiliaryElements(initialState.getOrbit(), I); // initialize all perturbing forces - final List shortPeriodTerms = new ArrayList(); + final List shortPeriodTerms = new ArrayList<>(); for (final DSSTForceModel force : forceModels) { shortPeriodTerms.addAll(force.initializeShortPeriodTerms(aux, type, force.getParameters(initialState.getDate()))); } @@ -803,7 +802,7 @@ protected void beforeIntegration(final SpacecraftState initialState, for (DSSTForceModel forceModel : forceModels) { forceModel.updateShortPeriodTerms(forceModel.getParametersAllValues(), initialState); } - final Collection stepHandlers = new ArrayList(); + final Collection stepHandlers = new ArrayList<>(); stepHandlers.add(spHandler); final ODEIntegrator integrator = getIntegrator(); final Collection existing = integrator.getStepHandlers(); @@ -823,7 +822,7 @@ protected void beforeIntegration(final SpacecraftState initialState, protected void afterIntegration() { // remove the special short periodics step handler if added before if (getPropagationType() == PropagationType.OSCULATING) { - final List preserved = new ArrayList(); + final List preserved = new ArrayList<>(); final ODEIntegrator integrator = getIntegrator(); for (final ODEStepHandler sp : integrator.getStepHandlers()) { if (!(sp instanceof ShortPeriodicsHandler)) { @@ -882,7 +881,7 @@ private static Orbit computeMeanOrbit(final SpacecraftState osculating, final AuxiliaryElements aux = new AuxiliaryElements(meanOrbit, I); // Set the force models - final List shortPeriodTerms = new ArrayList(); + final List shortPeriodTerms = new ArrayList<>(); for (final DSSTForceModel force : forceModels) { shortPeriodTerms.addAll(force.initializeShortPeriodTerms(aux, PropagationType.OSCULATING, force.getParameters(meanState.getDate()))); force.updateShortPeriodTerms(force.getParametersAllValues(), meanState); @@ -897,7 +896,7 @@ private static Orbit computeMeanOrbit(final SpacecraftState osculating, final double deltaEy = osculating.getEquinoctialEy() - rebuilt.getEquinoctialEy(); final double deltaHx = osculating.getHx() - rebuilt.getHx(); final double deltaHy = osculating.getHy() - rebuilt.getHy(); - final double deltaLv = MathUtils.normalizeAngle(osculating.getLv() - rebuilt.getLv(), 0.0); + final double deltaLM = MathUtils.normalizeAngle(osculating.getLM() - rebuilt.getLM(), 0.0); // check convergence if (FastMath.abs(deltaA) < thresholdA && @@ -905,7 +904,7 @@ private static Orbit computeMeanOrbit(final SpacecraftState osculating, FastMath.abs(deltaEy) < thresholdE && FastMath.abs(deltaHx) < thresholdI && FastMath.abs(deltaHy) < thresholdI && - FastMath.abs(deltaLv) < thresholdL) { + FastMath.abs(deltaLM) < thresholdL) { return meanOrbit; } @@ -915,8 +914,8 @@ private static Orbit computeMeanOrbit(final SpacecraftState osculating, meanOrbit.getEquinoctialEy() + deltaEy, meanOrbit.getHx() + deltaHx, meanOrbit.getHy() + deltaHy, - meanOrbit.getLv() + deltaLv, - PositionAngleType.TRUE, meanOrbit.getFrame(), + meanOrbit.getLM() + deltaLM, + PositionAngleType.MEAN, meanOrbit.getFrame(), meanOrbit.getDate(), meanOrbit.getMu()); } 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 a7b6d3e679..30eac9acc0 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/FieldDSSTPropagator.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/FieldDSSTPropagator.java @@ -16,14 +16,6 @@ */ 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; @@ -66,6 +58,14 @@ 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. *

          @@ -207,7 +207,7 @@ public FieldDSSTPropagator(final Field field, final AttitudeProvider attitudeProvider) { super(field, integrator, propagationType); this.field = field; - forceModels = new ArrayList(); + forceModels = new ArrayList<>(); initMapper(field); // DSST uses only equinoctial orbits and mean longitude argument setOrbitType(OrbitType.EQUINOCTIAL); @@ -255,7 +255,7 @@ public FieldDSSTPropagator(final Field field, final AttitudeProvider attitudeProvider) { super(field, integrator, PropagationType.MEAN); this.field = field; - forceModels = new ArrayList(); + forceModels = new ArrayList<>(); initMapper(field); // DSST uses only equinoctial orbits and mean longitude argument setOrbitType(OrbitType.EQUINOCTIAL); @@ -419,13 +419,13 @@ public void addForceModel(final DSSTForceModel force) { @Override public void valueChanged(final double previousValue, final ParameterDriver driver, final AbsoluteDate date) { // mu PDriver should have only 1 span - superSetMu(field.getZero().add(driver.getValue())); + superSetMu(field.getZero().newInstance(driver.getValue())); } /** {@inheritDoc} */ @Override public void valueSpanMapChanged(final TimeSpanMap previousValue, final ParameterDriver driver) { // mu PDriver should have only 1 span - superSetMu(field.getZero().add(driver.getValue())); + superSetMu(field.getZero().newInstance(driver.getValue())); } }); } catch (OrekitException oe) { @@ -526,7 +526,7 @@ public static > FieldSpacecraftState comput final FieldAuxiliaryElements aux = new FieldAuxiliaryElements<>(mean.getOrbit(), I); // Set the force models - final List> shortPeriodTerms = new ArrayList>(); + final List> shortPeriodTerms = new ArrayList<>(); for (final DSSTForceModel force : forces) { force.registerAttitudeProvider(attitudeProvider); shortPeriodTerms.addAll(force.initializeShortPeriodTerms(aux, PropagationType.OSCULATING, force.getParameters(mean.getDate().getField(), mean.getDate()))); @@ -647,7 +647,7 @@ protected void beforeIntegration(final FieldSpacecraftState initialState, final FieldAuxiliaryElements aux = new FieldAuxiliaryElements<>(initialState.getOrbit(), I); // initialize all perturbing forces - final List> shortPeriodTerms = new ArrayList>(); + final List> shortPeriodTerms = new ArrayList<>(); for (final DSSTForceModel force : forceModels) { shortPeriodTerms.addAll(force.initializeShortPeriodTerms(aux, type, force.getParameters(field, initialState.getDate()))); } @@ -661,7 +661,7 @@ protected void beforeIntegration(final FieldSpacecraftState initialState, forceModel.updateShortPeriodTerms(forceModel.getParametersAllValues(field), initialState); } - final Collection> stepHandlers = new ArrayList>(); + final Collection> stepHandlers = new ArrayList<>(); stepHandlers.add(spHandler); final FieldODEIntegrator integrator = getIntegrator(); final Collection> existing = integrator.getStepHandlers(); @@ -681,7 +681,7 @@ protected void beforeIntegration(final FieldSpacecraftState initialState, protected void afterIntegration() { // remove the special short periodics step handler if added before if (isMeanOrbit() == PropagationType.OSCULATING) { - final List> preserved = new ArrayList>(); + final List> preserved = new ArrayList<>(); final FieldODEIntegrator integrator = getIntegrator(); // clear the list @@ -721,7 +721,7 @@ private static > FieldOrbit computeMeanOrbi FieldEquinoctialOrbit meanOrbit = (FieldEquinoctialOrbit) OrbitType.EQUINOCTIAL.convertType(osculating.getOrbit()); // threshold for each parameter - final T epsilonT = zero.add(epsilon); + final T epsilonT = zero.newInstance(epsilon); final T thresholdA = epsilonT.multiply(FastMath.abs(meanOrbit.getA()).add(1.)); final T thresholdE = epsilonT.multiply(meanOrbit.getE().add(1.)); final T thresholdI = epsilonT.multiply(meanOrbit.getI().add(1.)); @@ -741,7 +741,7 @@ private static > FieldOrbit computeMeanOrbi final FieldAuxiliaryElements aux = new FieldAuxiliaryElements<>(meanOrbit, I); // Set the force models - final List> shortPeriodTerms = new ArrayList>(); + final List> shortPeriodTerms = new ArrayList<>(); for (final DSSTForceModel force : forceModel) { shortPeriodTerms.addAll(force.initializeShortPeriodTerms(aux, PropagationType.OSCULATING, force.getParameters(osculating.getDate().getField(), osculating.getDate()))); @@ -1050,8 +1050,8 @@ public T[] computeDerivatives(final FieldSpacecraftState state) { * @param state current state * @param auxiliaryElements auxiliary elements related to the current orbit * @param parameters force model parameters (all span values for each parameters) - * the extract parameter method {@link #extractParameters(double[], AbsoluteDate)} is called in - * the method to select the right parameter. + * the extract parameter method {@link #extractParameters(double[], AbsoluteDate)} is called in + * the method to select the right parameter. * @return the mean equinoctial elements rates dai / dt */ private T[] elementRates(final DSSTForceModel forceModel, diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/AbstractGaussianContribution.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/AbstractGaussianContribution.java index 2f7d55902f..3146cd6597 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/AbstractGaussianContribution.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/AbstractGaussianContribution.java @@ -16,19 +16,13 @@ */ package org.orekit.propagation.semianalytical.dsst.forces; -import java.lang.reflect.Array; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.analysis.CalculusFieldUnivariateVectorFunction; import org.hipparchus.analysis.UnivariateVectorFunction; +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.FastMath; import org.hipparchus.util.FieldSinCos; @@ -59,6 +53,14 @@ import org.orekit.utils.ParameterDriver; import org.orekit.utils.TimeSpanMap; +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + /** * Common handling of {@link DSSTForceModel} methods for Gaussian contributions * to DSST propagation. @@ -202,19 +204,11 @@ public > void init(final FieldSpacecraftState< /** {@inheritDoc} */ @Override public List getParametersDrivers() { - - // Parameter drivers - final List drivers = new ArrayList<>(); - - // Loop on drivers (without central attraction coefficient driver) - for (final ParameterDriver driver : getParametersDriversWithoutMu()) { - drivers.add(driver); - } - + // Initialize drivers (without central attraction coefficient driver) + final List drivers = new ArrayList<>(getParametersDriversWithoutMu()); // We put central attraction coefficient driver at the end of the array drivers.add(gmParameterDriver); return drivers; - } /** @@ -234,9 +228,9 @@ public List getParametersDrivers() { public List initializeShortPeriodTerms(final AuxiliaryElements auxiliaryElements, final PropagationType type, final double[] parameters) { - final List list = new ArrayList(); + final List list = new ArrayList<>(); gaussianSPCoefs = new GaussianShortPeriodicCoefficients(coefficientsKeyPrefix, JMAX, INTERPOLATION_POINTS, - new TimeSpanMap(new Slot(JMAX, INTERPOLATION_POINTS))); + new TimeSpanMap<>(new Slot(JMAX, INTERPOLATION_POINTS))); list.add(gaussianSPCoefs); return list; @@ -474,7 +468,7 @@ private > T getRatesDiff(final T[] meanRef, fi final FieldAuxiliaryElements auxiliaryElements = context.getFieldAuxiliaryElements(); T maxDiff = FastMath.abs(meanRef[0].subtract(meanCur[0])).divide(auxiliaryElements.getSma()); - ; + // Corrects mean element rates for (int i = 1; i < meanRef.length; i++) { maxDiff = FastMath.max(maxDiff, FastMath.abs(meanRef[i].subtract(meanCur[i]))); @@ -504,7 +498,7 @@ public void updateShortPeriodTerms(final double[] parameters, final SpacecraftSt final AbstractGaussianContributionContext context = initializeStep(auxiliaryElements, extractedParameters); // Compute rhoj and sigmaj - final double[][] currentRhoSigmaj = computeRhoSigmaCoefficients(meanState.getDate(), auxiliaryElements); + final double[][] currentRhoSigmaj = computeRhoSigmaCoefficients(auxiliaryElements); // Generate the Cij and Sij coefficients final FourierCjSjCoefficients fourierCjSj = new FourierCjSjCoefficients(meanState, JMAX, auxiliaryElements, @@ -543,7 +537,7 @@ public > void updateShortPeriodTerms(final T[] final FieldAbstractGaussianContributionContext context = initializeStep(auxiliaryElements, extractedParameters); // Compute rhoj and sigmaj - final T[][] currentRhoSigmaj = computeRhoSigmaCoefficients(meanState.getDate(), context, field); + final T[][] currentRhoSigmaj = computeRhoSigmaCoefficients(context, field); // Generate the Cij and Sij coefficients final FieldFourierCjSjCoefficients fourierCjSj = new FieldFourierCjSjCoefficients<>(meanState, JMAX, @@ -567,11 +561,10 @@ public > void updateShortPeriodTerms(final T[] * ρj = (1+jB)(-b)jCj(k, h)
          * σj = (1+jB)(-b)jSj(k, h)
          *

          - * @param date current date * @param auxiliaryElements auxiliary elements related to the current orbit * @return computed coefficients */ - private double[][] computeRhoSigmaCoefficients(final AbsoluteDate date, final AuxiliaryElements auxiliaryElements) { + private double[][] computeRhoSigmaCoefficients(final AuxiliaryElements auxiliaryElements) { final double[][] currentRhoSigmaj = new double[2][3 * JMAX + 1]; final CjSjCoefficient cjsjKH = new CjSjCoefficient(auxiliaryElements.getK(), auxiliaryElements.getH()); final double b = 1. / (1 + auxiliaryElements.getB()); @@ -598,13 +591,11 @@ private double[][] computeRhoSigmaCoefficients(final AbsoluteDate date, final Au * σj = (1+jB)(-b)jSj(k, h)
          *

          * @param type of the elements - * @param date current date * @param context container for attributes * @param field field used by default * @return computed coefficients */ - private > T[][] computeRhoSigmaCoefficients(final FieldAbsoluteDate date, - final FieldAbstractGaussianContributionContext context, final Field field) { + private > T[][] computeRhoSigmaCoefficients(final FieldAbstractGaussianContributionContext context, final Field field) { // zero final T zero = field.getZero(); @@ -615,7 +606,7 @@ private > T[][] computeRhoSigmaCoefficients(fi final T b = auxiliaryElements.getB().add(1.).reciprocal(); // (-b)j - T mbtj = zero.add(1.); + T mbtj = zero.newInstance(1.); for (int j = 1; j <= 3 * JMAX; j++) { @@ -688,9 +679,10 @@ public FieldIntegrableFunction(final FieldSpacecraftState state, final boolea this.context = new FieldAbstractGaussianContributionContext<>(auxiliaryElements, this.parameters); // remove derivatives from state final T[] stateVector = MathArrays.buildArray(field, 6); - OrbitType.EQUINOCTIAL.mapOrbitToArray(state.getOrbit(), PositionAngleType.TRUE, stateVector, null); + final PositionAngleType positionAngleType = PositionAngleType.MEAN; + OrbitType.EQUINOCTIAL.mapOrbitToArray(state.getOrbit(), positionAngleType, stateVector, null); final FieldOrbit fixedOrbit = OrbitType.EQUINOCTIAL.mapArrayToOrbit(stateVector, null, - PositionAngleType.TRUE, state.getDate(), context.getMu(), state.getFrame()); + positionAngleType, state.getDate(), context.getMu(), state.getFrame()); this.state = new FieldSpacecraftState<>(fixedOrbit, state.getAttitude(), state.getMass()); } @@ -722,27 +714,33 @@ public T[] value(final T x) { final FieldVector3D vel = new FieldVector3D<>(Xdot, auxiliaryElements.getVectorF(), Ydot, auxiliaryElements.getVectorG()); - // Compute acceleration - FieldVector3D acc = FieldVector3D.getZero(field); - // shift the orbit to dt final FieldOrbit shiftedOrbit = state.getOrbit().shiftedBy(dt); // Recompose an orbit with time held fixed to be compliant with DSST theory final FieldOrbit recomposedOrbit = new FieldEquinoctialOrbit<>(shiftedOrbit.getA(), shiftedOrbit.getEquinoctialEx(), shiftedOrbit.getEquinoctialEy(), shiftedOrbit.getHx(), - shiftedOrbit.getHy(), shiftedOrbit.getLv(), PositionAngleType.TRUE, shiftedOrbit.getFrame(), + shiftedOrbit.getHy(), shiftedOrbit.getLM(), PositionAngleType.MEAN, shiftedOrbit.getFrame(), state.getDate(), context.getMu()); // Get the corresponding attitude - final FieldAttitude recomposedAttitude = attitudeProvider.getAttitude(recomposedOrbit, - recomposedOrbit.getDate(), recomposedOrbit.getFrame()); + final FieldAttitude recomposedAttitude; + if (contribution.dependsOnAttitudeRate()) { + recomposedAttitude = attitudeProvider.getAttitude(recomposedOrbit, + recomposedOrbit.getDate(), recomposedOrbit.getFrame()); + } else { + final FieldRotation rotation = attitudeProvider.getAttitudeRotation(recomposedOrbit, + recomposedOrbit.getDate(), recomposedOrbit.getFrame()); + final FieldVector3D zeroVector = FieldVector3D.getZero(recomposedOrbit.getA().getField()); + recomposedAttitude = new FieldAttitude<>(recomposedOrbit.getDate(), recomposedOrbit.getFrame(), + rotation, zeroVector, zeroVector); + } // create shifted SpacecraftState with attitude at specified time final FieldSpacecraftState shiftedState = new FieldSpacecraftState<>(recomposedOrbit, recomposedAttitude, state.getMass()); - acc = contribution.acceleration(shiftedState, parameters); + final FieldVector3D acc = contribution.acceleration(shiftedState, parameters); // Compute the derivatives of the elements by the speed final T[] deriv = MathArrays.buildArray(field, dimension); @@ -760,7 +758,7 @@ public T[] value(final T x) { deriv[5] = getLoV(X, Y, Xdot, Ydot).dotProduct(acc); // Compute mean elements rates - T[] val = null; + final T[] val; if (meanMode) { val = MathArrays.buildArray(field, dimension); for (int i = 0; i < 6; i++) { @@ -966,13 +964,15 @@ protected class IntegrableFunction implements UnivariateVectorFunction { this.context = new AbstractGaussianContributionContext(auxiliaryElements, this.parameters); // remove derivatives from state final double[] stateVector = new double[6]; - OrbitType.EQUINOCTIAL.mapOrbitToArray(state.getOrbit(), PositionAngleType.TRUE, stateVector, null); - final Orbit fixedOrbit = OrbitType.EQUINOCTIAL.mapArrayToOrbit(stateVector, null, PositionAngleType.TRUE, + final PositionAngleType positionAngleType = PositionAngleType.MEAN; + OrbitType.EQUINOCTIAL.mapOrbitToArray(state.getOrbit(), positionAngleType, stateVector, null); + final Orbit fixedOrbit = OrbitType.EQUINOCTIAL.mapArrayToOrbit(stateVector, null, positionAngleType, state.getDate(), context.getMu(), state.getFrame()); this.state = new SpacecraftState(fixedOrbit, state.getAttitude(), state.getMass()); } /** {@inheritDoc} */ + @SuppressWarnings("checkstyle:FinalLocalVariable") @Override public double[] value(final double x) { @@ -995,27 +995,33 @@ public double[] value(final double x) { final Vector3D vel = new Vector3D(Xdot, auxiliaryElements.getVectorF(), Ydot, auxiliaryElements.getVectorG()); - // Compute acceleration - Vector3D acc = Vector3D.ZERO; - // shift the orbit to dt final Orbit shiftedOrbit = state.getOrbit().shiftedBy(dt); // Recompose an orbit with time held fixed to be compliant with DSST theory final Orbit recomposedOrbit = new EquinoctialOrbit(shiftedOrbit.getA(), shiftedOrbit.getEquinoctialEx(), - shiftedOrbit.getEquinoctialEy(), shiftedOrbit.getHx(), shiftedOrbit.getHy(), shiftedOrbit.getLv(), - PositionAngleType.TRUE, shiftedOrbit.getFrame(), state.getDate(), context.getMu()); + shiftedOrbit.getEquinoctialEy(), shiftedOrbit.getHx(), shiftedOrbit.getHy(), shiftedOrbit.getLM(), + PositionAngleType.MEAN, shiftedOrbit.getFrame(), state.getDate(), context.getMu()); // Get the corresponding attitude - final Attitude recomposedAttitude = attitudeProvider.getAttitude(recomposedOrbit, recomposedOrbit.getDate(), - recomposedOrbit.getFrame()); + final Attitude recomposedAttitude; + if (contribution.dependsOnAttitudeRate()) { + recomposedAttitude = attitudeProvider.getAttitude(recomposedOrbit, + recomposedOrbit.getDate(), recomposedOrbit.getFrame()); + } else { + final Rotation rotation = attitudeProvider.getAttitudeRotation(recomposedOrbit, + recomposedOrbit.getDate(), recomposedOrbit.getFrame()); + final Vector3D zeroVector = Vector3D.ZERO; + recomposedAttitude = new Attitude(recomposedOrbit.getDate(), recomposedOrbit.getFrame(), + rotation, zeroVector, zeroVector); + } // create shifted SpacecraftState with attitude at specified time final SpacecraftState shiftedState = new SpacecraftState(recomposedOrbit, recomposedAttitude, state.getMass()); // here parameters is a list of all span values of each parameter driver - acc = contribution.acceleration(shiftedState, parameters); + final Vector3D acc = contribution.acceleration(shiftedState, parameters); // Compute the derivatives of the elements by the speed final double[] deriv = new double[6]; @@ -1033,7 +1039,7 @@ public double[] value(final double x) { deriv[5] = getLoV(X, Y, Xdot, Ydot).dotProduct(acc); // Compute mean elements rates - double[] val = null; + final double[] val; if (meanMode) { val = new double[6]; for (int i = 0; i < 6; i++) { @@ -1417,8 +1423,8 @@ public > T[] integrate(final CalculusFieldUniv final T[] adaptedWeights = MathArrays.buildArray(field, numberOfPoints); for (int i = 0; i < numberOfPoints; i++) { - adaptedPoints[i] = zero.add(nodePoints[i]); - adaptedWeights[i] = zero.add(nodeWeights[i]); + adaptedPoints[i] = zero.newInstance(nodePoints[i]); + adaptedWeights[i] = zero.newInstance(nodeWeights[i]); } transform(adaptedPoints, adaptedWeights, lowerBound, upperBound); @@ -1531,7 +1537,6 @@ private > T[] basicIntegrate(final CalculusFie } final T[] t = y.clone(); final T[] c = MathArrays.buildArray(field, v.length); - ; final T[] s = t.clone(); for (int i = 1; i < points.length; i++) { x = points[i]; @@ -1917,8 +1922,8 @@ private void computeCoefficients(final SpacecraftState state, final Slot slot, } // add the computed coefficients to Ci⁰ - currentCij[0][i] += -(currentCij[j][i] * uijvij.currentRhoSigmaj[0][j] + - currentSij[j][i] * uijvij.currentRhoSigmaj[1][j]); + currentCij[0][i] -= currentCij[j][i] * uijvij.currentRhoSigmaj[0][j] + + currentSij[j][i] * uijvij.currentRhoSigmaj[1][j]; } } @@ -2026,7 +2031,7 @@ public Map getCoefficients(final AbsoluteDate date, final Set< // select the coefficients slot final Slot slot = slots.get(date); - final Map coefficients = new HashMap(2 * JMAX + 3); + final Map coefficients = new HashMap<>(2 * JMAX + 3); storeIfSelected(coefficients, selected, slot.cij[0].value(date), "d", 0); storeIfSelected(coefficients, selected, slot.dij[1].value(date), "d", 1); storeIfSelected(coefficients, selected, slot.dij[2].value(date), "d", 2); @@ -2221,8 +2226,7 @@ private void computeCoefficients(final FieldSpacecraftState state, final Fiel * @return the coefficient k₂⁰ */ private T computeK20(final int kMax, final T[][] currentRhoSigmaj, final Field field) { - final T zero = field.getZero(); - T k20 = zero; + T k20 = field.getZero(); for (int kIndex = 1; kIndex <= kMax; kIndex++) { // After inserting 2.5.3-(8) into 2.5.3-(9a) the result becomes: @@ -2254,7 +2258,7 @@ public T[] value(final FieldOrbit meanOrbit) { // Compute the center (l - λ) final T center = L.subtract(meanOrbit.getLM()); // Compute (l - λ)² - final T center2 = center.multiply(center); + final T center2 = center.square(); // Initialize short periodic variations final T[] shortPeriodicVariation = slot.cij[0].value(meanOrbit.getDate()); @@ -2302,7 +2306,7 @@ public Map getCoefficients(final FieldAbsoluteDate date, final S // select the coefficients slot final FieldSlot slot = slots.get(date); - final Map coefficients = new HashMap(2 * JMAX + 3); + final Map coefficients = new HashMap<>(2 * JMAX + 3); storeIfSelected(coefficients, selected, slot.cij[0].value(date), "d", 0); storeIfSelected(coefficients, selected, slot.dij[1].value(date), "d", 1); storeIfSelected(coefficients, selected, slot.dij[2].value(date), "d", 2); @@ -2736,7 +2740,7 @@ protected static class FieldUijVijCoefficients // compute the coefficients computeU1V1Coefficients(field); - computeU2V2Coefficients(field); + computeU2V2Coefficients(); } /** @@ -2869,9 +2873,8 @@ private void computeU1V1Coefficients(final Field field) { *

          * Only the coefficients for Fourier index = 1 (i == 0) are required. *

          - * @param field field used by default */ - private void computeU2V2Coefficients(final Field field) { + private void computeU2V2Coefficients() { for (int j = 1; j <= jMax; j++) { // compute 1 / j final double ooj = 1. / j; diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTAtmosphericDrag.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTAtmosphericDrag.java index a1aff6d2d2..470ef6632e 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTAtmosphericDrag.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTAtmosphericDrag.java @@ -51,14 +51,16 @@ public class DSSTAtmosphericDrag extends AbstractGaussianContribution { /** Threshold for the choice of the Gauss quadrature order. */ private static final double GAUSS_THRESHOLD = 6.0e-10; - /** Upper limit for atmospheric drag (m) . */ - private static final double ATMOSPHERE_ALTITUDE_MAX = 1000000.; + /** Default upper limit for atmospheric drag (m) . */ + private static final double DEFAULT_MAX_ATMOSPHERE_ALTITUDE = 1000000.; /** Atmospheric drag force model. */ private final DragForce drag; - /** Critical distance from the center of the central body for entering/leaving the atmosphere. */ - private final double rbar; + /** Critical distance from the center of the central body for + * entering/leaving the atmosphere, i.e. upper limit of atmosphere. + */ + private double rbar; /** Simple constructor with custom force. * @param force atmospheric drag force model @@ -68,7 +70,7 @@ public DSSTAtmosphericDrag(final DragForce force, final double mu) { //Call to the constructor from superclass using the numerical drag model as ForceModel super("DSST-drag-", GAUSS_THRESHOLD, force, mu); this.drag = force; - this.rbar = ATMOSPHERE_ALTITUDE_MAX + Constants.WGS84_EARTH_EQUATORIAL_RADIUS; + this.rbar = DEFAULT_MAX_ATMOSPHERE_ALTITUDE + Constants.WGS84_EARTH_EQUATORIAL_RADIUS; } /** Simple constructor assuming spherical spacecraft. @@ -111,6 +113,16 @@ public double getRbar() { return rbar; } + /** Set the critical distance from the center of the central body at which + * the atmosphere is considered to end, i.e. beyond this distance + * atmospheric drag is not considered. + * + * @param rbar the critical distance from the center of the central body (m) + */ + public void setRbar(final double rbar) { + this.rbar = rbar; + } + /** {@inheritDoc} */ public Stream getEventDetectors() { return drag.getEventDetectors(); diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTForceModel.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTForceModel.java index fa5aad44f3..c25ab4abbd 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTForceModel.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTForceModel.java @@ -16,9 +16,6 @@ */ package org.orekit.propagation.semianalytical.dsst.forces; -import java.util.List; -import java.util.stream.Stream; - import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; import org.hipparchus.util.MathArrays; @@ -38,6 +35,9 @@ import org.orekit.utils.ParameterDriversProvider; import org.orekit.utils.TimeSpanMap.Span; +import java.util.List; +import java.util.stream.Stream; + /** This interface represents a force modifying spacecraft motion for a {@link * org.orekit.propagation.semianalytical.dsst.DSSTPropagator DSSTPropagator}. *

          @@ -150,8 +150,7 @@ default double[] extractParameters(final double[] parameters, final AbsoluteDate final double[] outParameters = new double[allParameters.size()]; int index = 0; int paramIndex = 0; - for (int i = 0; i < allParameters.size(); i++) { - final ParameterDriver driver = allParameters.get(i); + for (final ParameterDriver driver : allParameters) { final String driverNameforDate = driver.getNameSpan(date); // Loop on the spans for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { @@ -182,8 +181,7 @@ default > T[] extractParameters(final T[] para final T[] outParameters = MathArrays.buildArray(date.getField(), allParameters.size()); int index = 0; int paramIndex = 0; - for (int i = 0; i < allParameters.size(); i++) { - final ParameterDriver driver = allParameters.get(i); + for (final ParameterDriver driver : allParameters) { final String driverNameforDate = driver.getNameSpan(date.toAbsoluteDate()); // Loop on the spans for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTNewtonianAttractionContext.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTNewtonianAttractionContext.java index 2e1c970ec3..3f22ea1e43 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTNewtonianAttractionContext.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTNewtonianAttractionContext.java @@ -38,7 +38,6 @@ public class DSSTNewtonianAttractionContext extends ForceModelContext { * @param parameters values of the force model parameters */ public DSSTNewtonianAttractionContext(final AuxiliaryElements auxiliaryElements, final double[] parameters) { - super(auxiliaryElements); this.gm = parameters[0]; } diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTSolarRadiationPressure.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTSolarRadiationPressure.java index 544dcdb4b9..0be73fc687 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTSolarRadiationPressure.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTSolarRadiationPressure.java @@ -315,8 +315,8 @@ protected > T[] getLLimits(final FieldSpacecra final T h2 = auxiliaryElements.getH().multiply(auxiliaryElements.getH()); final T k2 = auxiliaryElements.getK().multiply(auxiliaryElements.getK()); final T m = (auxiliaryElements.getSma().multiply(auxiliaryElements.getB())).divide(ae).reciprocal(); - final T m2 = m.multiply(m); - final T m4 = m2.multiply(m2); + final T m2 = m.square(); + final T m4 = m2.square(); final T bb = alpha.multiply(beta).add(m2.multiply(auxiliaryElements.getH()).multiply(auxiliaryElements.getK())); final T b2 = bb.multiply(bb); final T cc = alpha.multiply(alpha).subtract(bet2).add(m2.multiply(k2.subtract(h2))); @@ -498,7 +498,7 @@ private > int realQuarticRoots(final T[] a, fi // Solve resolvant cubic final T[] z3 = MathArrays.buildArray(field, 3); final T[] i = MathArrays.buildArray(field, 4); - i[0] = zero.add(1.0); + i[0] = zero.newInstance(1.0); i[1] = c.negate(); i[2] = b.multiply(d).subtract(e.multiply(4.0)); i[3] = e.multiply(c.multiply(4.).subtract(b.multiply(b))).subtract(d.multiply(d)); @@ -521,13 +521,13 @@ private > int realQuarticRoots(final T[] a, fi // Solve quadratic factors of quartic equation final T[] y1 = MathArrays.buildArray(field, 2); final T[] n = MathArrays.buildArray(field, 3); - n[0] = zero.add(1.0); + n[0] = zero.newInstance(1.0); n[1] = bh.subtract(pp); n[2] = zh.subtract(qq); final int n1 = realQuadraticRoots(n, y1); final T[] y2 = MathArrays.buildArray(field, 2); final T[] nn = MathArrays.buildArray(field, 3); - nn[0] = zero.add(1.0); + nn[0] = zero.newInstance(1.0); nn[1] = bh.add(pp); nn[2] = zh.add(qq); final int n2 = realQuadraticRoots(nn, y2); @@ -658,7 +658,7 @@ private > int realCubicRoots(final T[] a, fina final T b = a[1].divide(a[0].multiply(3.)).negate(); final T c = a[2].divide(a[0]); final T d = a[3].divide(a[0]); - final T b2 = b.multiply(b); + final T b2 = b.square(); final T p = b2.subtract(c.divide(3.)); final T q = b.multiply(b2.subtract(c.multiply(0.5))).subtract(d.multiply(0.5)); @@ -789,7 +789,7 @@ private > int realQuadraticRoots(final T[] a, final T c = a[2].divide(a[0]); // Compute discriminant - final T d = b.multiply(b).subtract(c); + final T d = b.square().subtract(c); if (d.getReal() < 0.) { // No real roots 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 504de2cec0..8f3df8ddfc 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,17 +16,6 @@ */ 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; @@ -69,6 +58,17 @@ 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. @@ -302,8 +302,8 @@ public DSSTTesseral(final Frame centralBodyFrame, this.maxFrequencyShortPeriodics = maxFrequencyShortPeriodics; // Initialize default values - this.resOrders = new ArrayList(); - this.nonResOrders = new TreeMap>(); + this.resOrders = new ArrayList<>(); + this.nonResOrders = new TreeMap<>(); // Initialize default values this.fieldShortPeriodTerms = new HashMap<>(); @@ -355,10 +355,9 @@ public List initializeShortPeriodTerms(final AuxiliaryElements shortPeriodTerms = new TesseralShortPeriodicCoefficients(bodyFrame, maxOrderMdailyTesseralSP, maxDegreeTesseralSP < 0, nonResOrders, mMax, maxFrequencyShortPeriodics, INTERPOLATION_POINTS, - new TimeSpanMap(new Slot(mMax, maxFrequencyShortPeriodics, - INTERPOLATION_POINTS))); + new TimeSpanMap<>(new Slot(mMax, maxFrequencyShortPeriodics, INTERPOLATION_POINTS))); - final List list = new ArrayList(); + final List list = new ArrayList<>(); list.add(shortPeriodTerms); return list; @@ -790,7 +789,7 @@ private void getResonantAndNonResonantTerms(final PropagationType type, final do if (type == PropagationType.OSCULATING && maxDegreeTesseralSP >= 0 && m <= maxOrderTesseralSP) { //compute non resonant orders in the tesseral harmonic field - final List listJofM = new ArrayList(); + final List listJofM = new ArrayList<>(); //for the moment we take only the pairs (j,m) with |j| <= maxDegree + maxEccPow (from |s-j| <= maxEccPow and |s| <= maxDegree) for (int j = -maxFrequencyShortPeriodics; j <= maxFrequencyShortPeriodics; j++) { if (j != 0 && j != jRes) { @@ -1017,7 +1016,7 @@ private > T[][] computeNSum(final FieldAbsolut if ((n - s) % 2 == 0) { // Vmns coefficient - final T vMNS = zero.add(CoefficientsFactory.getVmns(m, n, s)); + final T vMNS = zero.newInstance(CoefficientsFactory.getVmns(m, n, s)); // Inclination function Gamma and derivative final T gaMNS = gammaMNS.getValue(m, n, s); @@ -1046,8 +1045,8 @@ private > T[][] computeNSum(final FieldAbsolut JacobiPolynomials.getValue(l, v, w, FieldGradient.variable(1, 0, auxiliaryElements.getGamma())); // Geopotential coefficients - final T cnm = zero.add(harmonics.getUnnormalizedCnm(n, m)); - final T snm = zero.add(harmonics.getUnnormalizedSnm(n, m)); + final T cnm = zero.newInstance(harmonics.getUnnormalizedCnm(n, m)); + final T snm = zero.newInstance(harmonics.getUnnormalizedSnm(n, m)); // Common factors from expansion of equations 3.3-4 final T cf_0 = roaPow[n].multiply(Im).multiply(vMNS); @@ -1439,7 +1438,7 @@ private class FieldFourierCjSjCoefficients > { this.cCoef = MathArrays.buildArray(field, rows, columns, 6); this.sCoef = MathArrays.buildArray(field, rows, columns, 6); this.roaPow = MathArrays.buildArray(field, maxDegree + 1); - roaPow[0] = zero.add(1.); + roaPow[0] = zero.newInstance(1.); } /** @@ -1831,9 +1830,7 @@ public Map getCoefficients(final AbsoluteDate date, final Set< final Slot slot = slots.get(date); if (!nonResOrders.isEmpty() || mDailiesOnly) { - final Map coefficients = - new HashMap(12 * maxOrderMdailyTesseralSP + - 12 * nonResOrders.size()); + final Map coefficients = new HashMap<>(12 * maxOrderMdailyTesseralSP + 12 * nonResOrders.size()); for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) { storeIfSelected(coefficients, selected, slot.getCijm(0, m, date), DSSTTesseral.CM_COEFFICIENTS, m); @@ -2066,9 +2063,7 @@ public Map getCoefficients(final FieldAbsoluteDate date, final S final FieldSlot slot = slots.get(date); if (!nonResOrders.isEmpty() || mDailiesOnly) { - final Map coefficients = - new HashMap(12 * maxOrderMdailyTesseralSP + - 12 * nonResOrders.size()); + final Map coefficients = new HashMap<>(12 * maxOrderMdailyTesseralSP + 12 * nonResOrders.size()); for (int m = 1; m <= maxOrderMdailyTesseralSP; m++) { storeIfSelected(coefficients, selected, slot.getCijm(0, m, date), DSSTTesseral.CM_COEFFICIENTS, m); @@ -2562,7 +2557,7 @@ private class FieldUAnddU > { // R / a up to power degree final T[] roaPow = MathArrays.buildArray(field, maxDegree + 1); - roaPow[0] = zero.add(1.); + roaPow[0] = zero.newInstance(1.); for (int i = 1; i <= maxDegree; i++) { roaPow[i] = roaPow[i - 1].multiply(context.getRoa()); } diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTThirdBody.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTThirdBody.java index c3201e04bd..9159f33fbb 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTThirdBody.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTThirdBody.java @@ -16,16 +16,6 @@ */ 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.analysis.differentiation.FieldGradient; @@ -59,6 +49,16 @@ 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; + /** Third body attraction perturbation to the * {@link org.orekit.propagation.semianalytical.dsst.DSSTPropagator DSSTPropagator}. * @@ -212,9 +212,9 @@ public List initializeShortPeriodTerms(final AuxiliaryElements final int jMax = staticContext.getMaxAR3Pow() + 1; shortPeriods = new ThirdBodyShortPeriodicCoefficients(jMax, INTERPOLATION_POINTS, staticContext.getMaxFreqF(), body.getName(), - new TimeSpanMap(new Slot(jMax, INTERPOLATION_POINTS))); + new TimeSpanMap<>(new Slot(jMax, INTERPOLATION_POINTS))); - final List list = new ArrayList(); + final List list = new ArrayList<>(); list.add(shortPeriods); return list; @@ -1642,7 +1642,7 @@ private class FieldWnsjEtomjmsCoefficient > { //initialise fields c = auxiliaryElements.getEcc().multiply(context.getb()); - final T c2 = c.multiply(c); + final T c2 = c.square(); //b² * χ final T b2Chi = context.getb().multiply(context.getb()).multiply(context.getX()); @@ -1667,8 +1667,8 @@ private class FieldWnsjEtomjmsCoefficient > { opc2tn = MathArrays.buildArray(field, staticContext.getMaxAR3Pow() + staticContext.getMaxFreqF() + 2); final T omc2 = c2.negate().add(1.); final T opc2 = c2.add(1.); - omc2tn[0] = zero.add(1.); - opc2tn[0] = zero.add(1.); + omc2tn[0] = zero.newInstance(1.); + opc2tn[0] = zero.newInstance(1.); for (int i = 1; i <= staticContext.getMaxAR3Pow() + staticContext.getMaxFreqF() + 1; i++) { omc2tn[i] = omc2tn[i - 1].multiply(omc2); opc2tn[i] = opc2tn[i - 1].multiply(opc2); @@ -1676,7 +1676,7 @@ private class FieldWnsjEtomjmsCoefficient > { //Compute the powers of b btjms = MathArrays.buildArray(field, staticContext.getMaxAR3Pow() + staticContext.getMaxFreqF() + 1); - btjms[0] = zero.add(1.); + btjms[0] = zero.newInstance(1.); for (int i = 1; i <= staticContext.getMaxAR3Pow() + staticContext.getMaxFreqF(); i++) { btjms[i] = btjms[i - 1].multiply(context.getb()); } @@ -1715,15 +1715,15 @@ public T[] computeWjnsEmjmsAndDeriv(final int j, final int s, final int n, final T factCoef; if (absS > absJ) { //factCoef = (fact[n + s] / fact[n + j]) * (fact[n - s] / fact[n - j]); - factCoef = zero.add((CombinatoricsUtils.factorialDouble(n + s) / CombinatoricsUtils.factorialDouble(n + j)) * (CombinatoricsUtils.factorialDouble(n - s) / CombinatoricsUtils.factorialDouble(n - j))); + factCoef = zero.newInstance((CombinatoricsUtils.factorialDouble(n + s) / CombinatoricsUtils.factorialDouble(n + j)) * (CombinatoricsUtils.factorialDouble(n - s) / CombinatoricsUtils.factorialDouble(n - j))); l = n - absS; } else { - factCoef = zero.add(1.); + factCoef = zero.newInstance(1.); l = n - absJ; } // (-1)|j-s| - final T sign = absJmS % 2 != 0 ? zero.add(-1.) : zero.add(1.); + final T sign = absJmS % 2 != 0 ? zero.newInstance(-1.) : zero.newInstance(1.); //(1 - c²)n-|s| / (1 + c²)n final T coef1 = omc2tn[l].divide(opc2tn[n]); //-b|j-s| @@ -1782,13 +1782,13 @@ private class GnsCoefficients { private final int sMax; /** The coefficients Gn,s. */ - private final double gns[][]; + private final double[][] gns; /** The derivatives of the coefficients Gn,s by a. */ - private final double dgnsda[][]; + private final double[][] dgnsda; /** The derivatives of the coefficients Gn,s by γ. */ - private final double dgnsdgamma[][]; + private final double[][] dgnsdgamma; /** Standard constructor. * @@ -1901,13 +1901,13 @@ private class FieldGnsCoefficients > { private final int sMax; /** The coefficients Gn,s. */ - private final T gns[][]; + private final T[][] gns; /** The derivatives of the coefficients Gn,s by a. */ - private final T dgnsda[][]; + private final T[][] dgnsda; /** The derivatives of the coefficients Gn,s by γ. */ - private final T dgnsdgamma[][]; + private final T[][] dgnsdgamma; /** Standard constructor. * @@ -1960,7 +1960,7 @@ private void generateCoefficients(final FieldDSSTThirdBodyDynamicContext cont // compute the coefficients only if (n - s) % 2 == 0 if ( (n - s) % 2 == 0 ) { // Kronecker symbol (2 - delta(0,s)) - final T delta0s = (s == 0) ? zero.add(1.) : zero.add(2.); + final T delta0s = (s == 0) ? zero.newInstance(1.) : zero.newInstance(2.); final double vns = Vns.get(new NSKey(n, s)); final T coef0 = aoR3Pow[n].multiply(vns).multiply(context.getMuoR3()).multiply(delta0s); final T coef1 = coef0.multiply(qns[n][s]); @@ -2036,19 +2036,19 @@ private static class CjSjAlphaBetaKH { /** The coeficient sign(j-s) * Cs(α, β) * S|j-s|(k, h) + Ss(α, β) * C|j-s|(k, h) * and its derivative by k, h, α and β. */ - private final double coefAandDeriv[]; + private final double[] coefAandDeriv; /** The coeficient Cs(α, β) * Sj+s(k, h) - Ss(α, β) * Cj+s(k, h) * and its derivative by k, h, α and β. */ - private final double coefBandDeriv[]; + private final double[] coefBandDeriv; /** The coeficient Cs(α, β) * C|j-s|(k, h) - sign(j-s) * Ss(α, β) * S|j-s|(k, h) * and its derivative by k, h, α and β. */ - private final double coefDandDeriv[]; + private final double[] coefDandDeriv; /** The coeficient Cs(α, β) * Cj+s(k, h) + Ss(α, β) * Sj+s(k, h) * and its derivative by k, h, α and β. */ - private final double coefEandDeriv[]; + private final double[] coefEandDeriv; /** * Standard constructor. @@ -2294,19 +2294,19 @@ private static class FieldCjSjAlphaBetaKH > { /** The coeficient sign(j-s) * Cs(α, β) * S|j-s|(k, h) + Ss(α, β) * C|j-s|(k, h) * and its derivative by k, h, α and β. */ - private final T coefAandDeriv[]; + private final T[] coefAandDeriv; /** The coeficient Cs(α, β) * Sj+s(k, h) - Ss(α, β) * Cj+s(k, h) * and its derivative by k, h, α and β. */ - private final T coefBandDeriv[]; + private final T[] coefBandDeriv; /** The coeficient Cs(α, β) * C|j-s|(k, h) - sign(j-s) * Ss(α, β) * S|j-s|(k, h) * and its derivative by k, h, α and β. */ - private final T coefDandDeriv[]; + private final T[] coefDandDeriv; /** The coeficient Cs(α, β) * Cj+s(k, h) + Ss(α, β) * Sj+s(k, h) * and its derivative by k, h, α and β. */ - private final T coefEandDeriv[]; + private final T[] coefEandDeriv; /** * Standard constructor. @@ -3255,7 +3255,7 @@ public Map getCoefficients(final AbsoluteDate date, final Set< // select the coefficients slot final Slot slot = slots.get(date); - final Map coefficients = new HashMap(2 * maxFreqF + 1); + final Map coefficients = new HashMap<>(2 * maxFreqF + 1); storeIfSelected(coefficients, selected, slot.cij[0].value(date), "c", 0); for (int j = 1; j <= maxFreqF; j++) { storeIfSelected(coefficients, selected, slot.cij[j].value(date), "c", j); @@ -3360,15 +3360,15 @@ public T[] value(final FieldOrbit meanOrbit) { final T F = meanOrbit.getLE(); //initialize the short periodic contribution with the corresponding C⁰ coeficient - final T[] shortPeriodic = (T[]) slot.cij[0].value(meanOrbit.getDate()); + final T[] shortPeriodic = slot.cij[0].value(meanOrbit.getDate()); // Add the cos and sin dependent terms for (int j = 1; j <= maxFreqF; j++) { //compute cos and sin final FieldSinCos scjF = FastMath.sinCos(F.multiply(j)); - final T[] c = (T[]) slot.cij[j].value(meanOrbit.getDate()); - final T[] s = (T[]) slot.sij[j].value(meanOrbit.getDate()); + final T[] c = slot.cij[j].value(meanOrbit.getDate()); + final T[] s = slot.sij[j].value(meanOrbit.getDate()); for (int i = 0; i < 6; i++) { shortPeriodic[i] = shortPeriodic[i].add(c[i].multiply(scjF.cos()).add(s[i].multiply(scjF.sin()))); } @@ -3398,7 +3398,7 @@ public Map getCoefficients(final FieldAbsoluteDate date, final S // select the coefficients slot final FieldSlot slot = slots.get(date); - final Map coefficients = new HashMap(2 * maxFreqF + 1); + final Map coefficients = new HashMap<>(2 * maxFreqF + 1); storeIfSelected(coefficients, selected, slot.cij[0].value(date), "c", 0); for (int j = 1; j <= maxFreqF; j++) { storeIfSelected(coefficients, selected, slot.cij[j].value(date), "c", j); @@ -3782,14 +3782,14 @@ private class FieldUAnddU > { } // Kronecker symbol (2 - delta(0,s)) - final T delta0s = zero.add((s == 0) ? 1. : 2.); + final T delta0s = zero.newInstance((s == 0) ? 1. : 2.); for (int n = FastMath.max(2, s); n <= staticContext.getMaxAR3Pow(); n++) { // (n - s) must be even if ((n - s) % 2 == 0) { // Extract data from previous computation : - final T kns = (T) hansen.getHansenObjects()[s].getValue(n, auxiliaryElements.getB()); - final T dkns = (T) hansen.getHansenObjects()[s].getDerivative(n, auxiliaryElements.getB()); + final T kns = hansen.getHansenObjects()[s].getValue(n, auxiliaryElements.getB()); + final T dkns = hansen.getHansenObjects()[s].getDerivative(n, auxiliaryElements.getB()); final double vns = Vns.get(new NSKey(n, s)); final T coef0 = delta0s.multiply(vns).multiply(aoR3Pow[n]); 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 bce96738be..d888f628a3 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,16 +16,6 @@ */ 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; @@ -65,6 +55,16 @@ 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 @@ -254,11 +254,10 @@ public List initializeShortPeriodTerms(final AuxiliaryElements hansen = new HansenObjects(); - final List list = new ArrayList(); + final List list = new ArrayList<>(); zonalSPCoefs = new ZonalShortPeriodicCoefficients(maxFrequencyShortPeriodics, INTERPOLATION_POINTS, - new TimeSpanMap(new Slot(maxFrequencyShortPeriodics, - INTERPOLATION_POINTS))); + new TimeSpanMap<>(new Slot(maxFrequencyShortPeriodics, INTERPOLATION_POINTS))); list.add(zonalSPCoefs); return list; @@ -432,14 +431,14 @@ private > void computeMeanElementsTruncations( final T ax2or = auxiliaryElements.getSma().multiply(2.).divide(provider.getAe()); T xmuran = parameters[0].divide(auxiliaryElements.getSma()); // Set a lower bound for eccentricity - final T eo2 = FastMath.max(zero.add(0.0025), auxiliaryElements.getEcc().divide(2.)); + final T eo2 = FastMath.max(zero.newInstance(0.0025), auxiliaryElements.getEcc().divide(2.)); final T x2o2 = context.getXX().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.add(1.); + eccPwr[0] = zero.newInstance(1.); chiPwr[0] = context.getX(); - hafPwr[0] = zero.add(1.); + hafPwr[0] = zero.newInstance(1.); for (int i = 1; i <= maxDegree; i++) { eccPwr[i] = eccPwr[i - 1].multiply(eo2); chiPwr[i] = chiPwr[i - 1].multiply(x2o2); @@ -457,8 +456,8 @@ private > void computeMeanElementsTruncations( // Loop over m do { // Compute magnitude of current spherical harmonic coefficient. - final T cnm = zero.add(harmonics.getUnnormalizedCnm(maxDeg, m)); - final T snm = zero.add(harmonics.getUnnormalizedSnm(maxDeg, m)); + final T cnm = zero.newInstance(harmonics.getUnnormalizedCnm(maxDeg, m)); + final T snm = zero.newInstance(harmonics.getUnnormalizedSnm(maxDeg, m)); final T csnm = FastMath.hypot(cnm, snm); if (csnm.getReal() == 0.) { break; @@ -552,7 +551,7 @@ public double[] getMeanElementRate(final SpacecraftState spacecraftState, // Access to potential U derivatives final UAnddU udu = new UAnddU(spacecraftState.getDate(), context, auxiliaryElements, hansen); - return computeMeanElementRates(spacecraftState.getDate(), context, udu); + return computeMeanElementRates(context, udu); } @@ -578,13 +577,11 @@ public > T[] getMeanElementRate(final FieldSpa } /** Compute the mean element rates. - * @param date current date * @param context container for attributes * @param udu derivatives of the gravitational potential U * @return the mean element rates */ - private double[] computeMeanElementRates(final AbsoluteDate date, - final DSSTZonalContext context, + private double[] computeMeanElementRates(final DSSTZonalContext context, final UAnddU udu) { // Auxiliary elements related to the current orbit @@ -689,7 +686,7 @@ public void updateShortPeriodTerms(final double[] parameters, final SpacecraftSt final UAnddU udu = new UAnddU(meanState.getDate(), context, auxiliaryElements, hansen); // Compute rhoj and sigmaj - final double[][] rhoSigma = computeRhoSigmaCoefficients(meanState.getDate(), slot, auxiliaryElements); + final double[][] rhoSigma = computeRhoSigmaCoefficients(slot, auxiliaryElements); // Compute Di computeDiCoefficients(meanState.getDate(), slot, context, udu); @@ -734,7 +731,7 @@ public > void updateShortPeriodTerms(final T[] final FieldUAnddU udu = new FieldUAnddU<>(meanState.getDate(), context, auxiliaryElements, fho); // Compute rhoj and sigmaj - final T[][] rhoSigma = computeRhoSigmaCoefficients(meanState.getDate(), slot, auxiliaryElements, field); + final T[][] rhoSigma = computeRhoSigmaCoefficients(slot, auxiliaryElements, field); // Compute Di computeDiCoefficients(meanState.getDate(), slot, context, field, udu); @@ -768,7 +765,7 @@ private void computeDiCoefficients(final AbsoluteDate date, final DSSTZonalContext context, final UAnddU udu) { - final double[] meanElementRates = computeMeanElementRates(date, context, udu); + final double[] meanElementRates = computeMeanElementRates(context, udu); final double[] currentDi = new double[6]; @@ -904,7 +901,7 @@ private void computeCijSijCoefficients(final AbsoluteDate date, final Slot slot, currentSij[0] -= coef2; //Coefficients for k - currentCij[1] += -coef4; + currentCij[1] -= coef4; currentSij[1] -= coef3; //Coefficients for h @@ -978,7 +975,7 @@ private void computeCijSijCoefficients(final AbsoluteDate date, final Slot slot, //Coefficients for k currentCij[1] += coef4; - currentSij[1] += -coef3; + currentSij[1] -= coef3; //Coefficients for h currentCij[2] += coef3; @@ -1336,13 +1333,11 @@ private > void computeCijSijCoefficients(final * ρj = (1+jB)(-b)jCj(k, h)
          * σj = (1+jB)(-b)jSj(k, h)
          *

          - * @param date target date * @param slot slot to which the coefficients belong * @param auxiliaryElements auxiliary elements related to the current orbit * @return array containing ρj and σj */ - private double[][] computeRhoSigmaCoefficients(final AbsoluteDate date, - final Slot slot, + private double[][] computeRhoSigmaCoefficients(final Slot slot, final AuxiliaryElements auxiliaryElements) { final CjSjCoefficient cjsjKH = new CjSjCoefficient(auxiliaryElements.getK(), auxiliaryElements.getH()); @@ -1377,23 +1372,21 @@ private double[][] computeRhoSigmaCoefficients(final AbsoluteDate date, * σj = (1+jB)(-b)jSj(k, h)
          *

          * @param type of the elements - * @param date target date * @param slot slot to which the coefficients belong * @param auxiliaryElements auxiliary elements related to the current orbit * @param field field used by default * @return array containing ρj and σj */ - private > T[][] computeRhoSigmaCoefficients(final FieldAbsoluteDate date, - final FieldSlot slot, - final FieldAuxiliaryElements auxiliaryElements, - final Field field) { + private > T[][] computeRhoSigmaCoefficients(final FieldSlot slot, + final FieldAuxiliaryElements auxiliaryElements, + final Field field) { final T zero = field.getZero(); final FieldCjSjCoefficient cjsjKH = new FieldCjSjCoefficient<>(auxiliaryElements.getK(), auxiliaryElements.getH(), field); final T b = auxiliaryElements.getB().add(1.).reciprocal(); // (-b)j - T mbtj = zero.add(1.); + T mbtj = zero.newInstance(1.); final T[][] rhoSigma = MathArrays.buildArray(field, slot.cij.length, 2); for (int j = 1; j < rhoSigma.length; j++) { @@ -1528,7 +1521,7 @@ public Map getCoefficients(final AbsoluteDate date, final Set< // select the coefficients slot final Slot slot = slots.get(date); - final Map coefficients = new HashMap(2 * maxFrequencyShortPeriodics + 2); + final Map coefficients = new HashMap<>(2 * maxFrequencyShortPeriodics + 2); storeIfSelected(coefficients, selected, slot.cij[0].value(date), "d", 0); storeIfSelected(coefficients, selected, slot.di.value(date), "d", 1); for (int j = 1; j <= maxFrequencyShortPeriodics; j++) { @@ -1674,7 +1667,7 @@ public Map getCoefficients(final FieldAbsoluteDate date, final S // select the coefficients slot final FieldSlot slot = slots.get(date); - final Map coefficients = new HashMap(2 * maxFrequencyShortPeriodics + 2); + final Map coefficients = new HashMap<>(2 * maxFrequencyShortPeriodics + 2); storeIfSelected(coefficients, selected, slot.cij[0].value(date), "d", 0); storeIfSelected(coefficients, selected, slot.di.value(date), "d", 1); for (int j = 1; j <= maxFrequencyShortPeriodics; j++) { @@ -2671,8 +2664,8 @@ private void generateCoefficients(final FieldAbsoluteDate date, final T jn = zero.subtract(harmonics.getUnnormalizedCnm(n, 0)); // K₀-n-1,s - final T kns = (T) hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX()); - final T dkns = (T) hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX()); + final T kns = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX()); + final T dkns = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX()); final T coef0 = jn.multiply(d0smj); final T coef1 = coef0.multiply(lns); @@ -2730,8 +2723,8 @@ private void generateCoefficients(final FieldAbsoluteDate date, final T jn = zero.subtract(harmonics.getUnnormalizedCnm(n, 0)); // K₀-n-1,s - final T kns = (T) hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX()); - final T dkns = (T) hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX()); + final T kns = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX()); + final T dkns = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX()); final T coef0 = jn.multiply(d0spj); final T coef1 = coef0.multiply(lns); @@ -2790,8 +2783,8 @@ private void generateCoefficients(final FieldAbsoluteDate date, final T jn = zero.subtract(harmonics.getUnnormalizedCnm(n, 0)); // K₀-n-1,s - final T kns = (T) hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX()); - final T dkns = (T) hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX()); + final T kns = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX()); + final T dkns = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX()); final T coef0 = jn.multiply(d0smj); final T coef1 = coef0.multiply(lns); @@ -2825,8 +2818,8 @@ private void generateCoefficients(final FieldAbsoluteDate date, //add first term // Jj final T jj = zero.subtract(harmonics.getUnnormalizedCnm(j, 0)); - T kns = (T) hansenObjects.getHansenObjects()[0].getValue(-j - 1, context.getX()); - T dkns = (T) hansenObjects.getHansenObjects()[0].getDerivative(-j - 1, context.getX()); + T kns = hansenObjects.getHansenObjects()[0].getValue(-j - 1, context.getX()); + T dkns = hansenObjects.getHansenObjects()[0].getDerivative(-j - 1, context.getX()); T lns = lnsCoef.getLns(j, j); //dlns is 0 because n == s == j @@ -2878,8 +2871,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 = (T) hansenObjects.getHansenObjects()[s].getValue(-j - 1, context.getX()); - dkns = (T) hansenObjects.getHansenObjects()[s].getDerivative(-j - 1, context.getX()); + kns = hansenObjects.getHansenObjects()[s].getValue(-j - 1, context.getX()); + dkns = hansenObjects.getHansenObjects()[s].getDerivative(-j - 1, context.getX()); lns = lnsCoef.getLns(j, jms); final T dlns = lnsCoef.getdLnsdGamma(j, jms); @@ -2961,8 +2954,8 @@ private void generateCoefficients(final FieldAbsoluteDate date, final T jn = zero.subtract(harmonics.getUnnormalizedCnm(n, 0)); // K₀-n-1,s - final T kns = (T) hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX()); - final T dkns = (T) hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX()); + final T kns = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX()); + final T dkns = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX()); final T coef0 = jn.multiply(d0smj); final T coef1 = coef0.multiply(lns); @@ -3021,8 +3014,8 @@ private void generateCoefficients(final FieldAbsoluteDate date, final T jn = zero.subtract(harmonics.getUnnormalizedCnm(n, 0)); // K₀-n-1,s - final T kns = (T) hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX()); - final T dkns = (T) hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX()); + final T kns = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX()); + final T dkns = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX()); final T coef0 = jn.multiply(d0smj); final T coef1 = coef0.multiply(lns); @@ -3085,8 +3078,8 @@ private void generateCoefficients(final FieldAbsoluteDate date, // K₀-n-1,s - final T kns = (T) hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX()); - final T dkns = (T) hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX()); + final T kns = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX()); + final T dkns = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX()); final T coef0 = jn.multiply(d0smj); final T coef1 = coef0.multiply(lns); @@ -3147,8 +3140,8 @@ private void generateCoefficients(final FieldAbsoluteDate date, final T jn = zero.subtract(harmonics.getUnnormalizedCnm(n, 0)); // K₀-n-1,s - final T kns = (T) hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX()); - final T dkns = (T) hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX()); + final T kns = hansenObjects.getHansenObjects()[s].getValue(-n - 1, context.getX()); + final T dkns = hansenObjects.getHansenObjects()[s].getDerivative(-n - 1, context.getX()); final T coef0 = jn.multiply(d0smj); final T coef1 = coef0.multiply(lns); @@ -3711,7 +3704,7 @@ private class FieldUAnddU > { final T[][] Qns = CoefficientsFactory.computeQns(auxiliaryElements.getGamma(), maxDegree, maxEccPowMeanElements); final T[] roaPow = MathArrays.buildArray(field, maxDegree + 1); - roaPow[0] = zero.add(1.); + roaPow[0] = zero.newInstance(1.); for (int i = 1; i <= maxDegree; i++) { roaPow[i] = roaPow[i - 1].multiply(context.getRoa()); } @@ -3748,15 +3741,15 @@ private class FieldUAnddU > { } // Kronecker symbol (2 - delta(0,s)) - final T d0s = zero.add((s == 0) ? 1 : 2); + final T d0s = zero.newInstance((s == 0) ? 1 : 2); for (int n = s + 2; n <= maxDegree; n++) { // (n - s) must be even if ((n - s) % 2 == 0) { //Extract data from previous computation : - final T kns = (T) hansen.getHansenObjects()[s].getValue(-n - 1, context.getX()); - final T dkns = (T) hansen.getHansenObjects()[s].getDerivative(-n - 1, context.getX()); + final T kns = hansen.getHansenObjects()[s].getValue(-n - 1, context.getX()); + final T dkns = hansen.getHansenObjects()[s].getDerivative(-n - 1, context.getX()); final double vns = Vns.get(new NSKey(n, s)); final T coef0 = d0s.multiply(roaPow[n]).multiply(vns).multiply(-harmonics.getUnnormalizedCnm(n, 0)); diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTJ2SquaredClosedFormContext.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTJ2SquaredClosedFormContext.java index 18c2f8685d..c7493fc290 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTJ2SquaredClosedFormContext.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTJ2SquaredClosedFormContext.java @@ -73,7 +73,7 @@ public FieldDSSTJ2SquaredClosedFormContext(final FieldAuxiliaryElements auxil this.eta = FastMath.sqrt(auxiliaryElements.getEcc().multiply(auxiliaryElements.getEcc()).negate().add(1.0)); final T a2 = auxiliaryElements.getSma().multiply(auxiliaryElements.getSma()); - this.a4 = a2.multiply(a2); + this.a4 = a2.square(); } /** 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 c376b13e1f..a3c47504a2 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 @@ -133,7 +133,7 @@ public class FieldDSSTTesseralContext> extends // Keplerian period final T a = auxiliaryElements.getSma(); - period = (a.getReal() < 0) ? zero.add(Double.POSITIVE_INFINITY) : a.multiply(a.getPi().multiply(2.0)).multiply(a.divide(mu).sqrt()); + 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())); diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTThirdBodyDynamicContext.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTThirdBodyDynamicContext.java index f7d39bfd39..ea92773b5e 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTThirdBodyDynamicContext.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTThirdBodyDynamicContext.java @@ -136,7 +136,7 @@ public FieldDSSTThirdBodyDynamicContext(final FieldAuxiliaryElements aux, // Χ X = aux.getB().reciprocal(); - XX = X.multiply(X); + XX = X.square(); XXX = X.multiply(XX); // -2 * a / A m2aoA = aux.getSma().multiply(-2.).divide(A); 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 8f2efc067f..368cecd6b3 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 @@ -109,7 +109,7 @@ public class FieldDSSTZonalContext> extends Fi // Χ = 1 / B X = auxiliaryElements.getB().reciprocal(); - XX = X.multiply(X); + XX = X.square(); XXX = X.multiply(XX); // 1 / AB diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/CjSjCoefficient.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/CjSjCoefficient.java index 91284bb7cb..2c43ca1bba 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/CjSjCoefficient.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/CjSjCoefficient.java @@ -16,11 +16,11 @@ */ package org.orekit.propagation.semianalytical.dsst.utilities; +import org.hipparchus.complex.Complex; + import java.util.ArrayList; import java.util.List; -import org.hipparchus.complex.Complex; - /** Compute the Sj(k, h) and the Cj(k, h) series * and their partial derivatives with respect to k and h. *

          @@ -50,7 +50,7 @@ public class CjSjCoefficient { */ public CjSjCoefficient(final double k, final double h) { kih = new Complex(k, h); - cjsj = new ArrayList(); + cjsj = new ArrayList<>(); cjsj.add(new Complex(1, 0)); cjsj.add(kih); jLast = 1; diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/CoefficientsFactory.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/CoefficientsFactory.java index bcaa33f04b..b58abaa9b2 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/CoefficientsFactory.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/CoefficientsFactory.java @@ -16,9 +16,6 @@ */ package org.orekit.propagation.semianalytical.dsst.utilities; -import java.util.SortedMap; -import java.util.concurrent.ConcurrentSkipListMap; - import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; import org.hipparchus.util.CombinatoricsUtils; @@ -27,6 +24,9 @@ import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; +import java.util.SortedMap; +import java.util.concurrent.ConcurrentSkipListMap; + /** * This class is designed to provide coefficient from the DSST theory. * @@ -35,7 +35,7 @@ public class CoefficientsFactory { /** Internal storage of the polynomial values. Reused for further computation. */ - private static SortedMap VNS = new ConcurrentSkipListMap(); + private static SortedMap VNS = new ConcurrentSkipListMap<>(); /** Last computed order for Vns coefficients. */ private static int LAST_VNS_ORDER = 2; @@ -190,7 +190,7 @@ public static > T[][] computeGsHs(final T k, f final T kaphb = k.multiply(alpha).add(h.multiply(beta)); // Initialization final T[][] GsHs = MathArrays.buildArray(field, 2, order + 1); - GsHs[0][0] = zero.add(1.); + GsHs[0][0] = zero.newInstance(1.); GsHs[1][0] = zero; for (int s = 1; s <= order; s++) { @@ -213,7 +213,7 @@ public static SortedMap computeVns(final int order) { if (order > LAST_VNS_ORDER) { // Compute coefficient // Need previous computation as recurrence relation is done at s + 1 and n + 2 - final int min = (LAST_VNS_ORDER - 2 < 0) ? 0 : (LAST_VNS_ORDER - 2); + final int min = FastMath.max(LAST_VNS_ORDER - 2, 0); for (int n = min; n < order; n++) { for (int s = 0; s < n + 1; s++) { if ((n - s) % 2 != 0) { diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldCjSjCoefficient.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldCjSjCoefficient.java index dcafe69e08..e1a7f738dd 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldCjSjCoefficient.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldCjSjCoefficient.java @@ -16,14 +16,14 @@ */ package org.orekit.propagation.semianalytical.dsst.utilities; -import java.util.ArrayList; -import java.util.List; - -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.complex.Complex; import org.hipparchus.exception.NullArgumentException; +import java.util.ArrayList; +import java.util.List; + /** Compute the Sj(k, h) and the Cj(k, h) series * and their partial derivatives with respect to k and h. *

          @@ -59,8 +59,8 @@ public class FieldCjSjCoefficient > { public FieldCjSjCoefficient(final T k, final T h, final Field field) { zero = field.getZero(); kih = new FieldComplex<>(k, h); - cjsj = new ArrayList>(); - cjsj.add(new FieldComplex<>(zero.add(1.), zero)); + cjsj = new ArrayList<>(); + cjsj.add(new FieldComplex<>(zero.newInstance(1.), zero)); cjsj.add(kih); jLast = 1; } @@ -178,7 +178,6 @@ public T getImaginary() { * @param imaginaryPart Imaginary part. * @return a new complex number instance. * - * @see #valueOf(double, double) */ protected FieldComplex createComplex(final T realPart, final T imaginaryPart) { return new FieldComplex<>(realPart, imaginaryPart); diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldGHmsjPolynomials.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldGHmsjPolynomials.java index ba7fa1045d..782aa82a76 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldGHmsjPolynomials.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldGHmsjPolynomials.java @@ -16,8 +16,8 @@ */ package org.orekit.propagation.semianalytical.dsst.utilities; -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.util.FastMath; /** Compute the Gmsj and the Hmsj @@ -46,9 +46,6 @@ public class FieldGHmsjPolynomials > { */ private int I; - /** Zero for initialization. */ - private final T zero; - /** Create a set of Gmsj and Hmsj polynomials. * @param k X component of the eccentricity vector * @param h Y component of the eccentricity vector @@ -61,7 +58,6 @@ public FieldGHmsjPolynomials(final T k, final T h, final T alpha, final T beta, final int retroFactor, final Field field) { - zero = field.getZero(); this.cjsjKH = new FieldCjSjCoefficient<>(k, h, field); this.cjsjAB = new FieldCjSjCoefficient<>(alpha, beta, field); this.I = retroFactor; @@ -75,7 +71,7 @@ public FieldGHmsjPolynomials(final T k, final T h, */ public T getGmsj(final int m, final int s, final int j) { final int sMj = FastMath.abs(s - j); - T gms = zero; + final T gms; if (FastMath.abs(s) <= m) { final int mMis = m - I * s; gms = cjsjKH.getCj(sMj).multiply(cjsjAB.getCj(mMis)). @@ -96,7 +92,7 @@ public T getGmsj(final int m, final int s, final int j) { */ public T getHmsj(final int m, final int s, final int j) { final int sMj = FastMath.abs(s - j); - T hms = zero; + final T hms; if (FastMath.abs(s) <= m) { final int mMis = m - I * s; hms = cjsjKH.getCj(sMj).multiply(cjsjAB.getSj(mMis)).multiply(I). @@ -117,7 +113,7 @@ public T getHmsj(final int m, final int s, final int j) { */ public T getdGmsdk(final int m, final int s, final int j) { final int sMj = FastMath.abs(s - j); - T dGmsdk = zero; + final T dGmsdk; if (FastMath.abs(s) <= m) { final int mMis = m - I * s; dGmsdk = cjsjKH.getDcjDk(sMj).multiply(cjsjAB.getCj(mMis)). @@ -138,7 +134,7 @@ public T getdGmsdk(final int m, final int s, final int j) { */ public T getdGmsdh(final int m, final int s, final int j) { final int sMj = FastMath.abs(s - j); - T dGmsdh = zero; + final T dGmsdh; if (FastMath.abs(s) <= m) { final int mMis = m - I * s; dGmsdh = cjsjKH.getDcjDh(sMj).multiply(cjsjAB.getCj(mMis)). @@ -159,7 +155,7 @@ public T getdGmsdh(final int m, final int s, final int j) { */ public T getdGmsdAlpha(final int m, final int s, final int j) { final int sMj = FastMath.abs(s - j); - T dGmsdAl = zero; + final T dGmsdAl; if (FastMath.abs(s) <= m) { final int mMis = m - I * s; dGmsdAl = cjsjKH.getCj(sMj).multiply(cjsjAB.getDcjDk(mMis)). @@ -180,7 +176,7 @@ public T getdGmsdAlpha(final int m, final int s, final int j) { */ public T getdGmsdBeta(final int m, final int s, final int j) { final int sMj = FastMath.abs(s - j); - T dGmsdBe = zero; + final T dGmsdBe; if (FastMath.abs(s) <= m) { final int mMis = m - I * s; dGmsdBe = cjsjKH.getCj(sMj).multiply(cjsjAB.getDcjDh(mMis)). @@ -201,7 +197,7 @@ public T getdGmsdBeta(final int m, final int s, final int j) { */ public T getdHmsdk(final int m, final int s, final int j) { final int sMj = FastMath.abs(s - j); - T dHmsdk = zero; + final T dHmsdk; if (FastMath.abs(s) <= m) { final int mMis = m - I * s; dHmsdk = cjsjKH.getDcjDk(sMj).multiply(cjsjAB.getSj(mMis)).multiply(I). @@ -222,7 +218,7 @@ public T getdHmsdk(final int m, final int s, final int j) { */ public T getdHmsdh(final int m, final int s, final int j) { final int sMj = FastMath.abs(s - j); - T dHmsdh = zero; + final T dHmsdh; if (FastMath.abs(s) <= m) { final int mMis = m - I * s; dHmsdh = cjsjKH.getDcjDh(sMj).multiply(cjsjAB.getSj(mMis)).multiply(I). @@ -243,7 +239,7 @@ public T getdHmsdh(final int m, final int s, final int j) { */ public T getdHmsdAlpha(final int m, final int s, final int j) { final int sMj = FastMath.abs(s - j); - T dHmsdAl = zero; + final T dHmsdAl; if (FastMath.abs(s) <= m) { final int mMis = m - I * s; dHmsdAl = cjsjKH.getCj(sMj).multiply(cjsjAB.getDsjDk(mMis)).multiply(I). @@ -264,7 +260,7 @@ public T getdHmsdAlpha(final int m, final int s, final int j) { */ public T getdHmsdBeta(final int m, final int s, final int j) { final int sMj = FastMath.abs(s - j); - T dHmsdBe = zero; + final T dHmsdBe; if (FastMath.abs(s) <= m) { final int mMis = m - I * s; dHmsdBe = cjsjKH.getCj(sMj).multiply(cjsjAB.getDsjDh(mMis)).multiply(I). diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldGammaMnsFunction.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldGammaMnsFunction.java index 386caf2b53..f4fbf8b39a 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldGammaMnsFunction.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldGammaMnsFunction.java @@ -16,14 +16,14 @@ */ package org.orekit.propagation.semianalytical.dsst.utilities; -import java.util.Arrays; - -import org.hipparchus.Field; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; import org.hipparchus.fraction.BigFraction; import org.hipparchus.util.FastMath; import org.hipparchus.util.MathArrays; +import java.util.Arrays; + /** Compute the Γmn,s(γ) function from equation 2.7.1-(13). * @param type of the field elements */ @@ -32,9 +32,6 @@ public class FieldGammaMnsFunction > { /** Factorial ratios. */ private static double[] PRECOMPUTED_RATIOS; - /** Field element. */ - private final Field field; - /** Factorial ratios. */ private final double[] ratios; @@ -54,12 +51,10 @@ public class FieldGammaMnsFunction > { * @param field field element */ public FieldGammaMnsFunction(final int nMax, final T gamma, final int I, final Field field) { - this.field = field; - final T zero = field.getZero(); final int size = (nMax + 1) * (nMax + 2) * (4 * nMax + 3) / 6; this.values = MathArrays.buildArray(field, size); this.ratios = getRatios(nMax, size); - Arrays.fill(values, zero.add(Double.NaN)); + Arrays.fill(values, field.getZero().add(Double.NaN)); this.opIg = gamma.multiply(I).add(1.); this.I = I; } @@ -82,7 +77,7 @@ private static int index(final int m, final int n, final int s) { * @return factorial ratios */ private static double[] getRatios(final int nMax, final int size) { - synchronized (GammaMnsFunction.class) { + synchronized (FieldGammaMnsFunction.class) { if (PRECOMPUTED_RATIOS == null || PRECOMPUTED_RATIOS.length < size) { // we need to compute a larger reference array @@ -143,16 +138,13 @@ public T getValue(final int m, final int n, final int s) { * @return dΓmn,s(γ)/dγ */ public T getDerivative(final int m, final int n, final int s) { - final T zero = field.getZero(); - T res = zero; if (s <= -m) { - res = getValue(m, n, s).multiply(I).multiply(-m).divide(opIg); + return getValue(m, n, s).multiply(I).multiply(-m).divide(opIg); } else if (s >= m) { - res = getValue(m, n, s).multiply(I).multiply(m).divide(opIg);; + return getValue(m, n, s).multiply(I).multiply(m).divide(opIg); } else { - res = getValue(m, n, s).multiply(I).multiply(s).divide(opIg);; + return getValue(m, n, s).multiply(I).multiply(s).divide(opIg); } - return res; } } diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldLnsCoefficients.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldLnsCoefficients.java index c21d279baa..ea74f41203 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldLnsCoefficients.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/FieldLnsCoefficients.java @@ -58,7 +58,7 @@ public FieldLnsCoefficients(final int nMax, final int sMax, this.dlns = MathArrays.buildArray(field, rows, columns); final T[] roaPow = MathArrays.buildArray(field, rows); - roaPow[0] = zero.add(1.); + roaPow[0] = zero.newInstance(1.); for (int i = 1; i <= nMax; i++) { roaPow[i] = roa.multiply(roaPow[i - 1]); } diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/GammaMnsFunction.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/GammaMnsFunction.java index c24879ddd0..5c437c65d8 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/GammaMnsFunction.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/GammaMnsFunction.java @@ -16,11 +16,11 @@ */ package org.orekit.propagation.semianalytical.dsst.utilities; -import java.util.Arrays; - import org.hipparchus.fraction.BigFraction; import org.hipparchus.util.FastMath; +import java.util.Arrays; + /** Compute the Γmn,s(γ) function from equation 2.7.1-(13). * * @author Romain Di Costanzo @@ -135,15 +135,13 @@ public double getValue(final int m, final int n, final int s) { * @return dΓmn,s(γ)/dγ */ public double getDerivative(final int m, final int n, final int s) { - double res = 0.; if (s <= -m) { - res = -m * I * getValue(m, n, s) / opIg; + return -m * I * getValue(m, n, s) / opIg; } else if (s >= m) { - res = m * I * getValue(m, n, s) / opIg; + return m * I * getValue(m, n, s) / opIg; } else { - res = s * I * getValue(m, n, s) / opIg; + return s * I * getValue(m, n, s) / opIg; } - return res; } } diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/JacobiPolynomials.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/JacobiPolynomials.java index 2fa5d1686f..f8f7026e74 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/JacobiPolynomials.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/JacobiPolynomials.java @@ -24,6 +24,7 @@ import org.hipparchus.CalculusFieldElement; import org.hipparchus.analysis.differentiation.FieldGradient; import org.hipparchus.analysis.differentiation.Gradient; +import org.hipparchus.analysis.polynomials.JacobiKey; import org.hipparchus.analysis.polynomials.PolynomialFunction; import org.hipparchus.analysis.polynomials.PolynomialsUtils; import org.orekit.propagation.semianalytical.dsst.forces.DSSTThirdBody; @@ -41,8 +42,7 @@ public class JacobiPolynomials { /** Storage map. */ - private static final Map> MAP = - new HashMap>(); + private static final Map> MAP = new HashMap<>(); /** Private constructor as class is a utility. */ private JacobiPolynomials() { @@ -150,7 +150,7 @@ private static PolynomialFunction computePolynomial(final int l, final int v, fi // Check the existence of the corresponding key in the map. if (!MAP.containsKey(key)) { - MAP.put(key, new ArrayList()); + MAP.put(key, new ArrayList<>()); } polyList = MAP.get(key); @@ -169,55 +169,4 @@ private static PolynomialFunction computePolynomial(final int l, final int v, fi return polynomial; } - - /** Inner class for Jacobi polynomials keys. - *

          - * Please note that this class is not original content but is a copy from the - * Hipparchus library. This library is published under the - * Apache License, version 2.0. - *

          - * - * @see org.hipparchus.analysis.polynomials.PolynomialsUtils - */ - private static class JacobiKey { - - /** First exponent. */ - private final int v; - - /** Second exponent. */ - private final int w; - - /** Simple constructor. - * @param v first exponent - * @param w second exponent - */ - JacobiKey(final int v, final int w) { - this.v = v; - this.w = w; - } - - /** Get hash code. - * @return hash code - */ - @Override - public int hashCode() { - return (v << 16) ^ w; - } - - /** Check if the instance represent the same key as another instance. - * @param key other key - * @return true if the instance and the other key refer to the same polynomial - */ - @Override - public boolean equals(final Object key) { - - if (!(key instanceof JacobiKey)) { - return false; - } - - final JacobiKey otherK = (JacobiKey) key; - return v == otherK.v && w == otherK.w; - - } - } } diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/NewcombOperators.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/NewcombOperators.java index 84d0f7684d..90644c52ce 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/NewcombOperators.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/NewcombOperators.java @@ -16,15 +16,15 @@ */ package org.orekit.propagation.semianalytical.dsst.utilities; +import org.hipparchus.analysis.polynomials.PolynomialFunction; +import org.hipparchus.util.FastMath; + import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; -import org.hipparchus.analysis.polynomials.PolynomialFunction; -import org.hipparchus.util.FastMath; - /** * Implementation of the Modified Newcomb Operators. * @@ -59,13 +59,7 @@ public class NewcombOperators { if (k1.n == k2.n) { if (k1.s == k2.s) { if (k1.rho == k2.rho) { - if (k1.sigma < k2.sigma) { - return -1; - } else if (k1.sigma == k2.sigma) { - return 0; - } else { - return 1; - } + return Integer.compare(k1.sigma, k2.sigma); } else if (k1.rho < k2.rho) { return -1; } else { @@ -163,10 +157,10 @@ private static List getPolynomials(final int rho, final int if (POLYNOMIALS.isEmpty()) { // Initialize lists - final List l00 = new ArrayList(); - final List l01 = new ArrayList(); - final List l10 = new ArrayList(); - final List l11 = new ArrayList(); + final List l00 = new ArrayList<>(); + final List l01 = new ArrayList<>(); + final List l10 = new ArrayList<>(); + final List l11 = new ArrayList<>(); // Y(rho = 0, sigma = 0) = 1 l00.add(new PolynomialFunction(new double[] { @@ -225,7 +219,7 @@ private static List getPolynomials(final int rho, final int private static void computeFor(final int rho, final int sigma) { // Initialize result : - List result = new ArrayList(); + List result = new ArrayList<>(); // Get the coefficient from the recurrence relation final Map> map = generateRecurrenceCoefficients(rho, sigma); @@ -287,7 +281,7 @@ private static void computeFor(final int rho, final int sigma) { private static List multiplyPolynomialList(final List poly1, final List poly2) { // Initialize the result list of polynomial function - final List result = new ArrayList(); + final List result = new ArrayList<>(); initializeListOfPolynomials(poly1.size() + poly2.size() - 1, result); int i = 0; @@ -318,7 +312,7 @@ private static List sumPolynomialList(final List result = new ArrayList(); + final List result = new ArrayList<>(); initializeListOfPolynomials(highLength, result); for (int i = 0; i < lowLength; i++) { @@ -355,7 +349,7 @@ private static void initializeListOfPolynomials(final int i, */ private static List shiftList(final List polynomialList, final int shift) { - final List shiftedList = new ArrayList(); + final List shiftedList = new ArrayList<>(); for (PolynomialFunction function : polynomialList) { shiftedList.add(new PolynomialFunction(shift(function.getCoefficients(), shift))); } @@ -403,8 +397,8 @@ public static double[] shift(final double[] coefficients, // First polynomial coefficient. double shiftI = 1; - for (int i = 0; i < dp1; i++) { - newCoefficients[0] += coefficients[i] * shiftI; + for (double coefficient : coefficients) { + newCoefficients[0] += coefficient * shiftI; shiftI *= shift; } @@ -432,12 +426,12 @@ private static Map> generateRecurrenceCoeffici final double denx2 = 2. * den; final double denx4 = 4. * den; // Initialization : - final Map> list = new TreeMap>(); - final List poly0 = new ArrayList(); - final List poly1 = new ArrayList(); - final List poly2 = new ArrayList(); - final List poly3 = new ArrayList(); - final List poly4 = new ArrayList(); + final Map> list = new TreeMap<>(); + final List poly0 = new ArrayList<>(); + final List poly1 = new ArrayList<>(); + final List poly2 = new ArrayList<>(); + final List poly3 = new ArrayList<>(); + final List poly4 = new ArrayList<>(); // (s - n) poly0.add(new PolynomialFunction(new double[] {0., den})); poly0.add(new PolynomialFunction(new double[] {-den})); diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/ShortPeriodicsInterpolatedCoefficient.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/ShortPeriodicsInterpolatedCoefficient.java index 8a89dfad7a..3c339c0cd7 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/ShortPeriodicsInterpolatedCoefficient.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/ShortPeriodicsInterpolatedCoefficient.java @@ -16,12 +16,12 @@ */ package org.orekit.propagation.semianalytical.dsst.utilities; -import java.util.ArrayList; - import org.hipparchus.analysis.interpolation.HermiteInterpolator; import org.hipparchus.util.FastMath; import org.orekit.time.AbsoluteDate; +import java.util.ArrayList; + /** Interpolated short periodics coefficients. *

          * Representation of a coefficient that need to be interpolated over time. @@ -53,8 +53,8 @@ public class ShortPeriodicsInterpolatedCoefficient { */ public ShortPeriodicsInterpolatedCoefficient(final int interpolationPoints) { this.interpolationPoints = interpolationPoints; - this.abscissae = new ArrayList(); - this.values = new ArrayList(); + this.abscissae = new ArrayList<>(); + this.values = new ArrayList<>(); this.latestClosestNeighbor = 0; } @@ -133,7 +133,7 @@ private int getClosestNeighbor(final AbsoluteDate date) { //with an input date evolving often continuously in time, there is a high //probability that the result will be the same as for last call of //this method. - int closestNeighbor = latestClosestNeighbor; + final int closestNeighbor; //case where the date is before the available points if (date.compareTo(abscissae.get(0)) <= 0) { diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/UpperBounds.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/UpperBounds.java index e3a30fcaad..d949334f63 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/UpperBounds.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/UpperBounds.java @@ -172,9 +172,9 @@ public static > T getRnml(final T gamma, final T zero = gamma.getField().getZero(); // Initialization final int mei = m * eps * irf; - final T sinisq = gamma.multiply(gamma).negate().add(1.); + final T sinisq = gamma.square().negate().add(1.); // Set a lower bound for inclination - final T sininc = FastMath.max(zero.add(0.03), FastMath.sqrt(sinisq)); + final T sininc = FastMath.max(zero.newInstance(0.03), FastMath.sqrt(sinisq)); final T onepig = gamma.multiply(irf).add(1.); final T sinincPowLmMEI = FastMath.pow(sininc, l - mei); final T onepigPowLmMEI = FastMath.pow(onepig, mei); @@ -186,7 +186,7 @@ public static > T getRnml(final T gamma, if (n > l) { final int lp1 = l + 1; - T dpnml = zero.add(lp1 * eps); + T dpnml = zero.newInstance(lp1 * eps); T pnml = gamma.multiply(dpnml).subtract(m); // If index > 1 @@ -195,9 +195,9 @@ public static > T getRnml(final T gamma, final int ml = m * l; final int mm = m * m; - T pn1ml = zero.add(1.); + T pn1ml = zero.newInstance(1.); T dpn1ml = zero; - T pn2ml = zero.add(1.); + T pn2ml = zero.newInstance(1.); T dpn2ml = zero; for (int in = l + 2; in <= n; in++) { final int nm1 = in - 1; 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 41126cfab5..b1365fd255 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 @@ -54,34 +54,28 @@ public class FieldHansenTesseralLinear > { private PolynomialFunction[][] mpvecDeriv; /** The Hansen coefficients used as roots. */ - private T[][] hansenRoot; + private final T[][] hansenRoot; /** The derivatives of the Hansen coefficients used as roots. */ - private T[][] hansenDerivRoot; + private final T[][] hansenDerivRoot; /** The minimum value for the order. */ - private int Nmin; + private final int Nmin; /** The index of the initial condition, Petre's paper. */ - private int N0; - - /** The s coefficient. */ - private int s; - - /** The j coefficient. */ - private int j; + private final int N0; /** The number of slices needed to compute the coefficients. */ - private int numSlices; + private final int numSlices; /** * The offset used to identify the polynomial that corresponds to a negative. * value of n in the internal array that starts at 0 */ - private int offset; + private final int offset; /** The objects used to calculate initial data by means of Newcomb operators. */ - private FieldHansenCoefficientsBySeries[] hansenInit; + private final FieldHansenCoefficientsBySeries[] hansenInit; /** * Constructor. @@ -100,8 +94,6 @@ public FieldHansenTesseralLinear(final int nMax, final int s, final int j, final this.offset = nMax + 1; this.Nmin = -nMax - 1; this.N0 = -n0 - 4; - this.s = s; - this.j = j; final int maxRoots = FastMath.min(4, N0 - Nmin + 4); //Ensure that only the needed terms are computed @@ -120,205 +112,12 @@ public FieldHansenTesseralLinear(final int nMax, final int s, final int j, final mpvecDeriv = new PolynomialFunction[size][]; // Prepare the database of the associated polynomials - generatePolynomials(); + HansenUtilities.generateTesseralPolynomials(N0, Nmin, offset, SLICE, j, s, + mpvec, mpvecDeriv); } } - /** - * Compute polynomial coefficient a. - * - *

          - * It is used to generate the coefficient for Kj-n, s when computing Kj-n-1, s - * and the coefficient for dKj-n, s / de² when computing dKj-n-1, s / de² - *

          - * - *

          - * See Danielson 2.7.3-(9) and Collins 4-236 and 4-240 - *

          - * - * @param mnm1 -n-1 - * @return the polynomial - */ - private PolynomialFunction a(final int mnm1) { - // Collins 4-236, Danielson 2.7.3-(9) - final double r1 = (mnm1 + 2.) * (2. * mnm1 + 5.); - final double r2 = (2. + mnm1 + s) * (2. + mnm1 - s); - return new PolynomialFunction(new double[] { - 0.0, 0.0, r1 / r2 - }); - } - - /** - * Compute polynomial coefficient b. - * - *

          - * It is used to generate the coefficient for Kj-n+1, s when computing Kj-n-1, s - * and the coefficient for dKj-n+1, s / de² when computing dKj-n-1, s / de² - *

          - * - *

          - * See Danielson 2.7.3-(9) and Collins 4-236 and 4-240 - *

          - * - * @param mnm1 -n-1 - * @return the polynomial - */ - private PolynomialFunction b(final int mnm1) { - // Collins 4-236, Danielson 2.7.3-(9) - final double r2 = (2. + mnm1 + s) * (2. + mnm1 - s); - final double d1 = (mnm1 + 3.) * 2. * j * s / (r2 * (mnm1 + 4.)); - final double d2 = (mnm1 + 3.) * (mnm1 + 2.) / r2; - return new PolynomialFunction(new double[] { - 0.0, -d1, -d2 - }); - } - - /** - * Compute polynomial coefficient c. - * - *

          - * It is used to generate the coefficient for Kj-n+3, s when computing Kj-n-1, s - * and the coefficient for dKj-n+3, s / de² when computing dKj-n-1, s / de² - *

          - * - *

          - * See Danielson 2.7.3-(9) and Collins 4-236 and 4-240 - *

          - * - * @param mnm1 -n-1 - * @return the polynomial - */ - private PolynomialFunction c(final int mnm1) { - // Collins 4-236, Danielson 2.7.3-(9) - final double r1 = j * j * (mnm1 + 2.); - final double r2 = (mnm1 + 4.) * (2. + mnm1 + s) * (2. + mnm1 - s); - - return new PolynomialFunction(new double[] { - 0.0, 0.0, r1 / r2 - }); - } - - /** - * Compute polynomial coefficient d. - * - *

          - * It is used to generate the coefficient for Kj-n-1, s / dχ when computing dKj-n-1, s / de² - *

          - * - *

          - * See Danielson 2.7.3-(9) and Collins 4-236 and 4-240 - *

          - * - * @param mnm1 -n-1 - * @return the polynomial - */ - private PolynomialFunction d(final int mnm1) { - // Collins 4-236, Danielson 2.7.3-(9) - return new PolynomialFunction(new double[] { - 0.0, 0.0, 1.0 - }); - } - - /** - * Compute polynomial coefficient f. - * - *

          - * It is used to generate the coefficient for Kj-n+1, s / dχ when computing dKj-n-1, s / de² - *

          - * - *

          - * See Danielson 2.7.3-(9) and Collins 4-236 and 4-240 - *

          - * - * @param n index - * @return the polynomial - */ - private PolynomialFunction f(final int n) { - // Collins 4-236, Danielson 2.7.3-(9) - final double r1 = (n + 3.0) * j * s; - final double r2 = (n + 4.0) * (2.0 + n + s) * (2.0 + n - s); - return new PolynomialFunction(new double[] { - 0.0, 0.0, 0.0, r1 / r2 - }); - } - - /** - * Generate the polynomials needed in the linear transformation. - * - *

          - * See Petre's paper - *

          - */ - private void generatePolynomials() { - - - // Initialization of the matrices for linear transformations - // The final configuration of these matrices are obtained by composition - // of linear transformations - - // The matrix of polynomials associated to Hansen coefficients, Petre's - // paper - PolynomialFunctionMatrix A = HansenUtilities.buildIdentityMatrix4(); - - // The matrix of polynomials associated to derivatives, Petre's paper - final PolynomialFunctionMatrix B = HansenUtilities.buildZeroMatrix4(); - PolynomialFunctionMatrix D = HansenUtilities.buildZeroMatrix4(); - final PolynomialFunctionMatrix a = HansenUtilities.buildZeroMatrix4(); - - // The matrix of the current linear transformation - a.setMatrixLine(0, new PolynomialFunction[] { - HansenUtilities.ZERO, HansenUtilities.ONE, HansenUtilities.ZERO, HansenUtilities.ZERO - }); - a.setMatrixLine(1, new PolynomialFunction[] { - HansenUtilities.ZERO, HansenUtilities.ZERO, HansenUtilities.ONE, HansenUtilities.ZERO - }); - a.setMatrixLine(2, new PolynomialFunction[] { - HansenUtilities.ZERO, HansenUtilities.ZERO, HansenUtilities.ZERO, HansenUtilities.ONE - }); - // The generation process - int index; - int sliceCounter = 0; - for (int i = N0 - 1; i > Nmin - 1; i--) { - index = i + this.offset; - // The matrix of the current linear transformation is updated - // Petre's paper - a.setMatrixLine(3, new PolynomialFunction[] { - c(i), HansenUtilities.ZERO, b(i), a(i) - }); - - // composition of the linear transformations to calculate - // the polynomials associated to Hansen coefficients - // Petre's paper - A = A.multiply(a); - // store the polynomials for Hansen coefficients - mpvec[index] = A.getMatrixLine(3); - // composition of the linear transformations to calculate - // the polynomials associated to derivatives - // Petre's paper - D = D.multiply(a); - - //Update the B matrix - B.setMatrixLine(3, new PolynomialFunction[] { - HansenUtilities.ZERO, f(i), - HansenUtilities.ZERO, d(i) - }); - D = D.add(A.multiply(B)); - - // store the polynomials for Hansen coefficients from the - // expressions of derivatives - mpvecDeriv[index] = D.getMatrixLine(3); - - if (++sliceCounter % SLICE == 0) { - // Re-Initialisation of matrix for linear transformmations - // The final configuration of these matrix are obtained by composition - // of linear transformations - A = HansenUtilities.buildIdentityMatrix4(); - D = HansenUtilities.buildZeroMatrix4(); - } - } - } - /** * Compute the values for the first four coefficients and their derivatives by means of series. * @@ -464,7 +263,7 @@ private static class FieldHansenCoefficientsBySeries 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/propagation/semianalytical/dsst/utilities/hansen/FieldHansenThirdBodyLinear.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/FieldHansenThirdBodyLinear.java index 48cc213152..4ab6a1fbed 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/FieldHansenThirdBodyLinear.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/FieldHansenThirdBodyLinear.java @@ -44,40 +44,34 @@ public class FieldHansenThirdBodyLinear > { * The first vector of polynomials associated to Hansen coefficients and * derivatives. */ - private PolynomialFunction[][] mpvec; + private final PolynomialFunction[][] mpvec; /** The second vector of polynomials associated only to derivatives. */ - private PolynomialFunction[][] mpvecDeriv; + private final PolynomialFunction[][] mpvecDeriv; /** The Hansen coefficients used as roots. */ - private T[][] hansenRoot; + private final T[][] hansenRoot; /** The derivatives of the Hansen coefficients used as roots. */ - private T[][] hansenDerivRoot; + private final T[][] hansenDerivRoot; /** The number of slices needed to compute the coefficients. */ - private int numSlices; - - /** The maximum order of n indexes. */ - private int nMax; - - /** The index of the initial condition, Petre's paper. */ - private int N0; + private final int numSlices; /** The s index. */ - private int s; + private final int s; /** (-1)s * (2*s + 1)!! / (s+1)! */ private double twosp1dfosp1f; /** (-1)s * (2*s + 1)!! / (s+2)! */ - private double twosp1dfosp2f; + private final double twosp1dfosp2f; /** (-1)s * 2 * (2*s + 1)!! / (s+2)! */ - private double two2sp1dfosp2f; + private final double two2sp1dfosp2f; /** (2*s + 3). */ - private double twosp3; + private final double twosp3; /** * Constructor. @@ -88,14 +82,8 @@ public class FieldHansenThirdBodyLinear > { */ public FieldHansenThirdBodyLinear(final int nMax, final int s, final Field field) { // initialise fields - this.nMax = nMax; - N0 = s; this.s = s; - // initialization of structures for stored data - mpvec = new PolynomialFunction[this.nMax + 1][]; - mpvecDeriv = new PolynomialFunction[this.nMax + 1][]; - //Compute the fields that will be used to determine the initial values for the coefficients this.twosp1dfosp1f = (s % 2 == 0) ? 1.0 : -1.0; for (int i = s; i >= 1; i--) { @@ -107,8 +95,8 @@ public FieldHansenThirdBodyLinear(final int nMax, final int s, final Field fi this.two2sp1dfosp2f = 2 * this.twosp1dfosp2f; // initialization of structures for stored data - mpvec = new PolynomialFunction[this.nMax + 1][]; - mpvecDeriv = new PolynomialFunction[this.nMax + 1][]; + mpvec = new PolynomialFunction[nMax + 1][]; + mpvecDeriv = new PolynomialFunction[nMax + 1][]; this.numSlices = FastMath.max(1, (nMax - s + SLICE - 2) / SLICE); @@ -116,153 +104,9 @@ public FieldHansenThirdBodyLinear(final int nMax, final int s, final Field fi hansenDerivRoot = MathArrays.buildArray(field, numSlices, 2); // Prepare the database of the associated polynomials - generatePolynomials(); - - } - - /** - * Compute polynomial coefficient a. - * - *

          - * It is used to generate the coefficient for K₀n-1, s when computing K₀n, s - * and the coefficient for dK₀n-1, s / dΧ when computing dK₀n, s / dΧ - *

          - * - *

          - * See Danielson 2.7.3-(7c) and Collins 4-254 and 4-257 - *

          - * - * @param n n value - * @return the polynomial - */ - private PolynomialFunction a(final int n) { - // from recurrence Danielson 2.7.3-(7c), Collins 4-254 - final double r1 = 2 * n + 1; - final double r2 = n + 1; - - return new PolynomialFunction(new double[] { - r1 / r2 - }); - } + HansenUtilities.generateThirdBodyPolynomials(s, nMax, SLICE, s, + mpvec, mpvecDeriv); - /** - * Compute polynomial coefficient b. - * - *

          - * It is used to generate the coefficient for K₀n-2, s when computing K₀n, s - * and the coefficient for dK₀n-2, s / dΧ when computing dK₀n, s / dΧ - *

          - * - *

          - * See Danielson 2.7.3-(7c) and Collins 4-254 and 4-257 - *

          - * - * @param n n value - * @return the polynomial - */ - private PolynomialFunction b(final int n) { - // from recurrence Danielson 2.7.3-(7c), Collins 4-254 - final double r1 = (n + s) * (n - s); - final double r2 = n * (n + 1); - - return new PolynomialFunction(new double[] { - 0.0, 0.0, -r1 / r2 - }); - } - - /** - * Compute polynomial coefficient d. - * - *

          - * It is used to generate the coefficient for K₀n-2, s when computing dK₀n, s / dΧ - *

          - * - *

          - * See Danielson 2.7.3-(7c) and Collins 4-254 and 4-257 - *

          - * - * @param n n value - * @return the polynomial - */ - private PolynomialFunction d(final int n) { - // from Danielson 3.2-(3b) - final double r1 = 2 * (n + s) * (n - s); - final double r2 = n * (n + 1); - - return new PolynomialFunction(new double[] { - 0.0, 0.0, 0.0, r1 / r2 - }); - } - - /** - * Generate the polynomials needed in the linear transformation. - * - *

          - * See Petre's paper - *

          - */ - private void generatePolynomials() { - - int sliceCounter = 0; - - // Initialization of the matrices for linear transformations - // The final configuration of these matrices are obtained by composition - // of linear transformations - - // the matrix A for the polynomials associated - // to Hansen coefficients, Petre's pater - PolynomialFunctionMatrix A = HansenUtilities.buildIdentityMatrix2(); - - // the matrix D for the polynomials associated - // to derivatives, Petre's paper - final PolynomialFunctionMatrix B = HansenUtilities.buildZeroMatrix2(); - PolynomialFunctionMatrix D = HansenUtilities.buildZeroMatrix2(); - PolynomialFunctionMatrix E = HansenUtilities.buildIdentityMatrix2(); - - // The matrix that contains the coefficients at each step - final PolynomialFunctionMatrix a = HansenUtilities.buildZeroMatrix2(); - a.setElem(0, 1, HansenUtilities.ONE); - - // The generation process - for (int i = N0 + 2; i <= nMax; i++) { - // Collins 4-254 or Danielson 2.7.3-(7) - // Petre's paper - // The matrix of the current linear transformation is actualized - a.setMatrixLine(1, new PolynomialFunction[] { - b(i), a(i) - }); - - // composition of the linear transformations to calculate - // the polynomials associated to Hansen coefficients - A = A.multiply(a); - // store the polynomials associated to Hansen coefficients - this.mpvec[i] = A.getMatrixLine(1); - // composition of the linear transformations to calculate - // the polynomials associated to derivatives - // Danielson 3.2-(3b) and Petre's paper - D = D.multiply(a); - if (sliceCounter % SLICE != 0) { - a.setMatrixLine(1, new PolynomialFunction[] { - b(i - 1), a(i - 1) - }); - E = E.multiply(a); - } - - B.setElem(1, 0, d(i)); - // F = E.prod(B); - D = D.add(E.multiply(B)); - // store the polynomials associated to the derivatives - this.mpvecDeriv[i] = D.getMatrixLine(1); - - if (++sliceCounter % SLICE == 0) { - // Re-Initialization of the matrices for linear transformations - // The final configuration of these matrices are obtained by composition - // of linear transformations - A = HansenUtilities.buildIdentityMatrix2(); - D = HansenUtilities.buildZeroMatrix2(); - E = HansenUtilities.buildIdentityMatrix2(); - } - } } /** @@ -285,7 +129,7 @@ private void generatePolynomials() { public void computeInitValues(final T chitm1, final T chitm2, final T chitm3) { final Field field = chitm2.getField(); final T zero = field.getZero(); - this.hansenRoot[0][0] = zero.add(twosp1dfosp1f); + this.hansenRoot[0][0] = zero.newInstance(twosp1dfosp1f); this.hansenRoot[0][1] = (chitm2.negate().add(this.twosp3)).multiply(this.twosp1dfosp2f); this.hansenDerivRoot[0][0] = zero; this.hansenDerivRoot[0][1] = chitm3.multiply(two2sp1dfosp2f); diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/FieldHansenZonalLinear.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/FieldHansenZonalLinear.java index 85ff585318..15e65e7512 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/FieldHansenZonalLinear.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/FieldHansenZonalLinear.java @@ -44,47 +44,40 @@ public class FieldHansenZonalLinear > { * The first vector of polynomials associated to Hansen coefficients and * derivatives. */ - private PolynomialFunction[][] mpvec; + private final PolynomialFunction[][] mpvec; /** The second vector of polynomials associated only to derivatives. */ - private PolynomialFunction[][] mpvecDeriv; + private final PolynomialFunction[][] mpvecDeriv; /** The Hansen coefficients used as roots. */ - private T[][] hansenRoot; + private final T[][] hansenRoot; /** The derivatives of the Hansen coefficients used as roots. */ - private T[][] hansenDerivRoot; - - /** The minimum value for the order. */ - private int Nmin; - - - /** The index of the initial condition, Petre's paper. */ - private int N0; + private final T[][] hansenDerivRoot; /** The s coefficient. */ - private int s; + private final int s; /** * The offset used to identify the polynomial that corresponds to a negative * value of n in the internal array that starts at 0. */ - private int offset; + private final int offset; /** The number of slices needed to compute the coefficients. */ - private int numSlices; + private final int numSlices; /** 2s. */ - private double twots; + private final double twots; /** 2*s+1. */ - private int twosp1; + private final int twosp1; /** 2*s. */ - private int twos; + private final int twos; /** (2*s+1) / 2s. */ - private double twosp1otwots; + private final double twosp1otwots; /** * Constructor. @@ -95,9 +88,9 @@ public class FieldHansenZonalLinear > { */ public FieldHansenZonalLinear(final int nMax, final int s, final Field field) { //Initialize fields + final int Nmin = -nMax - 1; + final int N0 = -(s + 2); this.offset = nMax + 1; - this.Nmin = -nMax - 1; - N0 = -(s + 2); this.s = s; this.twots = FastMath.pow(2., s); this.twos = 2 * s; @@ -114,122 +107,11 @@ public FieldHansenZonalLinear(final int nMax, final int s, final Field field) hansenDerivRoot = MathArrays.buildArray(field, numSlices, 2); // Prepare the data base of associated polynomials - generatePolynomials(); + HansenUtilities.generateZonalPolynomials(N0, Nmin, offset, SLICE, s, + mpvec, mpvecDeriv); } - /** - * Compute polynomial coefficient a. - * - *

          - * It is used to generate the coefficient for K₀-n, s when computing K₀-n-1, s - * and the coefficient for dK₀-n, s / de² when computing dK₀-n-1, s / de² - *

          - * - *

          - * See Danielson 2.7.3-(6) and Collins 4-242 and 4-245 - *

          - * - * @param mnm1 -n-1 value - * @return the polynomial - */ - private PolynomialFunction a(final int mnm1) { - // from recurence Collins 4-242 - final double d1 = (mnm1 + 2) * (2 * mnm1 + 5); - final double d2 = (mnm1 + 2 - s) * (mnm1 + 2 + s); - return new PolynomialFunction(new double[] { - 0.0, 0.0, d1 / d2 - }); - } - - /** - * Compute polynomial coefficient b. - * - *

          - * It is used to generate the coefficient for K₀-n+1, s when computing K₀-n-1, s - * and the coefficient for dK₀-n+1, s / de² when computing dK₀-n-1, s / de² - *

          - * - *

          - * See Danielson 2.7.3-(6) and Collins 4-242 and 4-245 - *

          - * - * @param mnm1 -n-1 value - * @return the polynomial - */ - private PolynomialFunction b(final int mnm1) { - // from recurence Collins 4-242 - final double d1 = (mnm1 + 2) * (mnm1 + 3); - final double d2 = (mnm1 + 2 - s) * (mnm1 + 2 + s); - return new PolynomialFunction(new double[] { - 0.0, 0.0, -d1 / d2 - }); - } - - /** - * Generate the polynomials needed in the linear transformation. - * - *

          - * See Petre's paper - *

          - */ - private void generatePolynomials() { - - int sliceCounter = 0; - int index; - - // Initialisation of matrix for linear transformmations - // The final configuration of these matrix are obtained by composition - // of linear transformations - PolynomialFunctionMatrix A = HansenUtilities.buildIdentityMatrix2(); - PolynomialFunctionMatrix D = HansenUtilities.buildZeroMatrix2(); - PolynomialFunctionMatrix E = HansenUtilities.buildIdentityMatrix2(); - - // generation of polynomials associated to Hansen coefficients and to - // their derivatives - final PolynomialFunctionMatrix a = HansenUtilities.buildZeroMatrix2(); - a.setElem(0, 1, HansenUtilities.ONE); - - //The B matrix is constant. - final PolynomialFunctionMatrix B = HansenUtilities.buildZeroMatrix2(); - // from Collins 4-245 and Petre's paper - B.setElem(1, 1, new PolynomialFunction(new double[] { - 2.0 - })); - - for (int i = N0 - 1; i > Nmin - 1; i--) { - index = i + offset; - // Matrix of the current linear transformation - // Petre's paper - a.setMatrixLine(1, new PolynomialFunction[] { - b(i), a(i) - }); - // composition of linear transformations - // see Petre's paper - A = A.multiply(a); - // store the polynomials for Hansen coefficients - mpvec[index] = A.getMatrixLine(1); - - D = D.multiply(a); - E = E.multiply(a); - D = D.add(E.multiply(B)); - - // store the polynomials for Hansen coefficients from the expressions - // of derivatives - mpvecDeriv[index] = D.getMatrixLine(1); - - if (++sliceCounter % SLICE == 0) { - // Re-Initialisation of matrix for linear transformmations - // The final configuration of these matrix are obtained by composition - // of linear transformations - A = HansenUtilities.buildIdentityMatrix2(); - D = HansenUtilities.buildZeroMatrix2(); - E = HansenUtilities.buildIdentityMatrix2(); - } - - } - } - /** * Compute the roots for the Hansen coefficients and their derivatives. * diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/HansenTesseralLinear.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/HansenTesseralLinear.java index 7c964ee8b8..a6082473e5 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/HansenTesseralLinear.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/HansenTesseralLinear.java @@ -47,34 +47,28 @@ public class HansenTesseralLinear { private PolynomialFunction[][] mpvecDeriv; /** The Hansen coefficients used as roots. */ - private double[][] hansenRoot; + private final double[][] hansenRoot; /** The derivatives of the Hansen coefficients used as roots. */ - private double[][] hansenDerivRoot; + private final double[][] hansenDerivRoot; /** The minimum value for the order. */ - private int Nmin; + private final int Nmin; /** The index of the initial condition, Petre's paper. */ - private int N0; - - /** The s coefficient. */ - private int s; - - /** The j coefficient. */ - private int j; + private final int N0; /** The number of slices needed to compute the coefficients. */ - private int numSlices; + private final int numSlices; /** * The offset used to identify the polynomial that corresponds to a negative. * value of n in the internal array that starts at 0 */ - private int offset; + private final int offset; /** The objects used to calculate initial data by means of Newcomb operators. */ - private HansenCoefficientsBySeries[] hansenInit; + private final HansenCoefficientsBySeries[] hansenInit; /** * Constructor. @@ -90,8 +84,6 @@ public HansenTesseralLinear(final int nMax, final int s, final int j, final int this.offset = nMax + 1; this.Nmin = -nMax - 1; this.N0 = -n0 - 4; - this.s = s; - this.j = j; //Ensure that only the needed terms are computed final int maxRoots = FastMath.min(4, N0 - Nmin + 4); @@ -110,205 +102,12 @@ public HansenTesseralLinear(final int nMax, final int s, final int j, final int mpvecDeriv = new PolynomialFunction[size][]; // Prepare the database of the associated polynomials - generatePolynomials(); + HansenUtilities.generateTesseralPolynomials(N0, Nmin, offset, SLICE, j, s, + mpvec, mpvecDeriv); } } - /** - * Compute polynomial coefficient a. - * - *

          - * It is used to generate the coefficient for Kj-n, s when computing Kj-n-1, s - * and the coefficient for dKj-n, s / de² when computing dKj-n-1, s / de² - *

          - * - *

          - * See Danielson 2.7.3-(9) and Collins 4-236 and 4-240 - *

          - * - * @param mnm1 -n-1 - * @return the polynomial - */ - private PolynomialFunction a(final int mnm1) { - // Collins 4-236, Danielson 2.7.3-(9) - final double r1 = (mnm1 + 2.) * (2. * mnm1 + 5.); - final double r2 = (2. + mnm1 + s) * (2. + mnm1 - s); - return new PolynomialFunction(new double[] { - 0.0, 0.0, r1 / r2 - }); - } - - /** - * Compute polynomial coefficient b. - * - *

          - * It is used to generate the coefficient for Kj-n+1, s when computing Kj-n-1, s - * and the coefficient for dKj-n+1, s / de² when computing dKj-n-1, s / de² - *

          - * - *

          - * See Danielson 2.7.3-(9) and Collins 4-236 and 4-240 - *

          - * - * @param mnm1 -n-1 - * @return the polynomial - */ - private PolynomialFunction b(final int mnm1) { - // Collins 4-236, Danielson 2.7.3-(9) - final double r2 = (2. + mnm1 + s) * (2. + mnm1 - s); - final double d1 = (mnm1 + 3.) * 2. * j * s / (r2 * (mnm1 + 4.)); - final double d2 = (mnm1 + 3.) * (mnm1 + 2.) / r2; - return new PolynomialFunction(new double[] { - 0.0, -d1, -d2 - }); - } - - /** - * Compute polynomial coefficient c. - * - *

          - * It is used to generate the coefficient for Kj-n+3, s when computing Kj-n-1, s - * and the coefficient for dKj-n+3, s / de² when computing dKj-n-1, s / de² - *

          - * - *

          - * See Danielson 2.7.3-(9) and Collins 4-236 and 4-240 - *

          - * - * @param mnm1 -n-1 - * @return the polynomial - */ - private PolynomialFunction c(final int mnm1) { - // Collins 4-236, Danielson 2.7.3-(9) - final double r1 = j * j * (mnm1 + 2.); - final double r2 = (mnm1 + 4.) * (2. + mnm1 + s) * (2. + mnm1 - s); - - return new PolynomialFunction(new double[] { - 0.0, 0.0, r1 / r2 - }); - } - - /** - * Compute polynomial coefficient d. - * - *

          - * It is used to generate the coefficient for Kj-n-1, s / dχ when computing dKj-n-1, s / de² - *

          - * - *

          - * See Danielson 2.7.3-(9) and Collins 4-236 and 4-240 - *

          - * - * @param mnm1 -n-1 - * @return the polynomial - */ - private PolynomialFunction d(final int mnm1) { - // Collins 4-236, Danielson 2.7.3-(9) - return new PolynomialFunction(new double[] { - 0.0, 0.0, 1.0 - }); - } - - /** - * Compute polynomial coefficient f. - * - *

          - * It is used to generate the coefficient for Kj-n+1, s / dχ when computing dKj-n-1, s / de² - *

          - * - *

          - * See Danielson 2.7.3-(9) and Collins 4-236 and 4-240 - *

          - * - * @param n index - * @return the polynomial - */ - private PolynomialFunction f(final int n) { - // Collins 4-236, Danielson 2.7.3-(9) - final double r1 = (n + 3.0) * j * s; - final double r2 = (n + 4.0) * (2.0 + n + s) * (2.0 + n - s); - return new PolynomialFunction(new double[] { - 0.0, 0.0, 0.0, r1 / r2 - }); - } - - /** - * Generate the polynomials needed in the linear transformation. - * - *

          - * See Petre's paper - *

          - */ - private void generatePolynomials() { - - - // Initialization of the matrices for linear transformations - // The final configuration of these matrices are obtained by composition - // of linear transformations - - // The matrix of polynomials associated to Hansen coefficients, Petre's - // paper - PolynomialFunctionMatrix A = HansenUtilities.buildIdentityMatrix4(); - - // The matrix of polynomials associated to derivatives, Petre's paper - final PolynomialFunctionMatrix B = HansenUtilities.buildZeroMatrix4(); - PolynomialFunctionMatrix D = HansenUtilities.buildZeroMatrix4(); - final PolynomialFunctionMatrix a = HansenUtilities.buildZeroMatrix4(); - - // The matrix of the current linear transformation - a.setMatrixLine(0, new PolynomialFunction[] { - HansenUtilities.ZERO, HansenUtilities.ONE, HansenUtilities.ZERO, HansenUtilities.ZERO - }); - a.setMatrixLine(1, new PolynomialFunction[] { - HansenUtilities.ZERO, HansenUtilities.ZERO, HansenUtilities.ONE, HansenUtilities.ZERO - }); - a.setMatrixLine(2, new PolynomialFunction[] { - HansenUtilities.ZERO, HansenUtilities.ZERO, HansenUtilities.ZERO, HansenUtilities.ONE - }); - // The generation process - int index; - int sliceCounter = 0; - for (int i = N0 - 1; i > Nmin - 1; i--) { - index = i + this.offset; - // The matrix of the current linear transformation is updated - // Petre's paper - a.setMatrixLine(3, new PolynomialFunction[] { - c(i), HansenUtilities.ZERO, b(i), a(i) - }); - - // composition of the linear transformations to calculate - // the polynomials associated to Hansen coefficients - // Petre's paper - A = A.multiply(a); - // store the polynomials for Hansen coefficients - mpvec[index] = A.getMatrixLine(3); - // composition of the linear transformations to calculate - // the polynomials associated to derivatives - // Petre's paper - D = D.multiply(a); - - //Update the B matrix - B.setMatrixLine(3, new PolynomialFunction[] { - HansenUtilities.ZERO, f(i), - HansenUtilities.ZERO, d(i) - }); - D = D.add(A.multiply(B)); - - // store the polynomials for Hansen coefficients from the - // expressions of derivatives - mpvecDeriv[index] = D.getMatrixLine(3); - - if (++sliceCounter % SLICE == 0) { - // Re-Initialisation of matrix for linear transformmations - // The final configuration of these matrix are obtained by composition - // of linear transformations - A = HansenUtilities.buildIdentityMatrix4(); - D = HansenUtilities.buildZeroMatrix4(); - } - } - } - /** * Compute the values for the first four coefficients and their derivatives by means of series. * @@ -454,7 +253,7 @@ private static class HansenCoefficientsBySeries { private final int maxNewcomb; /** Polynomial representing the serie. */ - private PolynomialFunction polynomial; + private final PolynomialFunction polynomial; /** * Class constructor. diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/HansenThirdBodyLinear.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/HansenThirdBodyLinear.java index 00fb1f23ae..9e88e8232b 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/HansenThirdBodyLinear.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/HansenThirdBodyLinear.java @@ -39,40 +39,34 @@ public class HansenThirdBodyLinear { * The first vector of polynomials associated to Hansen coefficients and * derivatives. */ - private PolynomialFunction[][] mpvec; + private final PolynomialFunction[][] mpvec; /** The second vector of polynomials associated only to derivatives. */ - private PolynomialFunction[][] mpvecDeriv; + private final PolynomialFunction[][] mpvecDeriv; /** The Hansen coefficients used as roots. */ - private double[][] hansenRoot; + private final double[][] hansenRoot; /** The derivatives of the Hansen coefficients used as roots. */ - private double[][] hansenDerivRoot; + private final double[][] hansenDerivRoot; /** The number of slices needed to compute the coefficients. */ - private int numSlices; - - /** The maximum order of n indexes. */ - private int nMax; - - /** The index of the initial condition, Petre's paper. */ - private int N0; + private final int numSlices; /** The s index. */ - private int s; + private final int s; /** (-1)s * (2*s + 1)!! / (s+1)! */ private double twosp1dfosp1f; /** (-1)s * (2*s + 1)!! / (s+2)! */ - private double twosp1dfosp2f; + private final double twosp1dfosp2f; /** (-1)s * 2 * (2*s + 1)!! / (s+2)! */ - private double two2sp1dfosp2f; + private final double two2sp1dfosp2f; /** (2*s + 3). */ - private double twosp3; + private final double twosp3; /** * Constructor. @@ -83,14 +77,8 @@ public class HansenThirdBodyLinear { public HansenThirdBodyLinear(final int nMax, final int s) { // initialise fields - this.nMax = nMax; - N0 = s; this.s = s; - // initialization of structures for stored data - mpvec = new PolynomialFunction[this.nMax + 1][]; - mpvecDeriv = new PolynomialFunction[this.nMax + 1][]; - //Compute the fields that will be used to determine the initial values for the coefficients this.twosp1dfosp1f = (s % 2 == 0) ? 1.0 : -1.0; for (int i = s; i >= 1; i--) { @@ -102,161 +90,17 @@ public HansenThirdBodyLinear(final int nMax, final int s) { this.two2sp1dfosp2f = 2 * this.twosp1dfosp2f; // initialization of structures for stored data - mpvec = new PolynomialFunction[this.nMax + 1][]; - mpvecDeriv = new PolynomialFunction[this.nMax + 1][]; + mpvec = new PolynomialFunction[nMax + 1][]; + mpvecDeriv = new PolynomialFunction[nMax + 1][]; this.numSlices = FastMath.max(1, (nMax - s + SLICE - 2) / SLICE); hansenRoot = new double[numSlices][2]; hansenDerivRoot = new double[numSlices][2]; // Prepare the database of the associated polynomials - generatePolynomials(); - - } - - /** - * Compute polynomial coefficient a. - * - *

          - * It is used to generate the coefficient for K₀n-1, s when computing K₀n, s - * and the coefficient for dK₀n-1, s / dΧ when computing dK₀n, s / dΧ - *

          - * - *

          - * See Danielson 2.7.3-(7c) and Collins 4-254 and 4-257 - *

          - * - * @param n n value - * @return the polynomial - */ - private PolynomialFunction a(final int n) { - // from recurrence Danielson 2.7.3-(7c), Collins 4-254 - final double r1 = 2 * n + 1; - final double r2 = n + 1; - - return new PolynomialFunction(new double[] { - r1 / r2 - }); - } + HansenUtilities.generateThirdBodyPolynomials(s, nMax, SLICE, s, + mpvec, mpvecDeriv); - /** - * Compute polynomial coefficient b. - * - *

          - * It is used to generate the coefficient for K₀n-2, s when computing K₀n, s - * and the coefficient for dK₀n-2, s / dΧ when computing dK₀n, s / dΧ - *

          - * - *

          - * See Danielson 2.7.3-(7c) and Collins 4-254 and 4-257 - *

          - * - * @param n n value - * @return the polynomial - */ - private PolynomialFunction b(final int n) { - // from recurrence Danielson 2.7.3-(7c), Collins 4-254 - final double r1 = (n + s) * (n - s); - final double r2 = n * (n + 1); - - return new PolynomialFunction(new double[] { - 0.0, 0.0, -r1 / r2 - }); - } - - /** - * Compute polynomial coefficient d. - * - *

          - * It is used to generate the coefficient for K₀n-2, s when computing dK₀n, s / dΧ - *

          - * - *

          - * See Danielson 2.7.3-(7c) and Collins 4-254 and 4-257 - *

          - * - * @param n n value - * @return the polynomial - */ - private PolynomialFunction d(final int n) { - // from Danielson 3.2-(3b) - final double r1 = 2 * (n + s) * (n - s); - final double r2 = n * (n + 1); - - return new PolynomialFunction(new double[] { - 0.0, 0.0, 0.0, r1 / r2 - }); - } - - /** - * Generate the polynomials needed in the linear transformation. - * - *

          - * See Petre's paper - *

          - */ - private void generatePolynomials() { - - int sliceCounter = 0; - - // Initialization of the matrices for linear transformations - // The final configuration of these matrices are obtained by composition - // of linear transformations - - // the matrix A for the polynomials associated - // to Hansen coefficients, Petre's pater - PolynomialFunctionMatrix A = HansenUtilities.buildIdentityMatrix2(); - - // the matrix D for the polynomials associated - // to derivatives, Petre's paper - final PolynomialFunctionMatrix B = HansenUtilities.buildZeroMatrix2(); - PolynomialFunctionMatrix D = HansenUtilities.buildZeroMatrix2(); - PolynomialFunctionMatrix E = HansenUtilities.buildIdentityMatrix2(); - - // The matrix that contains the coefficients at each step - final PolynomialFunctionMatrix a = HansenUtilities.buildZeroMatrix2(); - a.setElem(0, 1, HansenUtilities.ONE); - - // The generation process - for (int i = N0 + 2; i <= nMax; i++) { - // Collins 4-254 or Danielson 2.7.3-(7) - // Petre's paper - // The matrix of the current linear transformation is actualized - a.setMatrixLine(1, new PolynomialFunction[] { - b(i), a(i) - }); - - // composition of the linear transformations to calculate - // the polynomials associated to Hansen coefficients - A = A.multiply(a); - // store the polynomials associated to Hansen coefficients - this.mpvec[i] = A.getMatrixLine(1); - // composition of the linear transformations to calculate - // the polynomials associated to derivatives - // Danielson 3.2-(3b) and Petre's paper - D = D.multiply(a); - if (sliceCounter % SLICE != 0) { - a.setMatrixLine(1, new PolynomialFunction[] { - b(i - 1), a(i - 1) - }); - E = E.multiply(a); - } - - B.setElem(1, 0, d(i)); - // F = E.prod(B); - D = D.add(E.multiply(B)); - // store the polynomials associated to the derivatives - this.mpvecDeriv[i] = D.getMatrixLine(1); - - if (++sliceCounter % SLICE == 0) { - // Re-Initialization of the matrices for linear transformations - // The final configuration of these matrices are obtained by composition - // of linear transformations - A = HansenUtilities.buildIdentityMatrix2(); - D = HansenUtilities.buildZeroMatrix2(); - E = HansenUtilities.buildIdentityMatrix2(); - } - } } /** diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/HansenUtilities.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/HansenUtilities.java index 9eb9cc167e..6457713696 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/HansenUtilities.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/HansenUtilities.java @@ -51,7 +51,7 @@ private HansenUtilities() { * * @return the identity matrix of order 2 */ - public static final PolynomialFunctionMatrix buildIdentityMatrix2() { + public static PolynomialFunctionMatrix buildIdentityMatrix2() { final PolynomialFunctionMatrix matrix = new PolynomialFunctionMatrix(2); matrix.setMatrix(new PolynomialFunction[][] { { @@ -75,7 +75,7 @@ public static final PolynomialFunctionMatrix buildIdentityMatrix2() { * * @return the identity matrix of order 2 */ - public static final PolynomialFunctionMatrix buildZeroMatrix2() { + public static PolynomialFunctionMatrix buildZeroMatrix2() { final PolynomialFunctionMatrix matrix = new PolynomialFunctionMatrix(2); matrix.setMatrix(new PolynomialFunction[][] { { @@ -104,7 +104,7 @@ public static final PolynomialFunctionMatrix buildZeroMatrix2() { * * @return the identity matrix of order 4 */ - public static final PolynomialFunctionMatrix buildIdentityMatrix4() { + public static PolynomialFunctionMatrix buildIdentityMatrix4() { final PolynomialFunctionMatrix matrix = new PolynomialFunctionMatrix(4); matrix.setMatrix(new PolynomialFunction[][] { { @@ -138,7 +138,7 @@ public static final PolynomialFunctionMatrix buildIdentityMatrix4() { * * @return the identity matrix of order 4 */ - public static final PolynomialFunctionMatrix buildZeroMatrix4() { + public static PolynomialFunctionMatrix buildZeroMatrix4() { final PolynomialFunctionMatrix matrix = new PolynomialFunctionMatrix(4); matrix.setMatrix(new PolynomialFunction[][] { { @@ -157,4 +157,506 @@ public static final PolynomialFunctionMatrix buildZeroMatrix4() { return matrix; } + /** + * Compute polynomial coefficient a. + * + *

          + * It is used to generate the coefficient for K₀-n, s when computing K₀-n-1, s + * and the coefficient for dK₀-n, s / de² when computing dK₀-n-1, s / de² + *

          + * + *

          + * See Danielson 2.7.3-(6) and Collins 4-242 and 4-245 + *

          + * + * @param s the s coefficient + * @param mnm1 -n-1 value + * @return the polynomial + */ + private static PolynomialFunction aZonal(final int s, final int mnm1) { + // from recurrence Collins 4-242 + final double d1 = (mnm1 + 2) * (2 * mnm1 + 5); + final double d2 = (mnm1 + 2 - s) * (mnm1 + 2 + s); + return new PolynomialFunction(new double[] { + 0.0, 0.0, d1 / d2 + }); + } + + /** + * Compute polynomial coefficient b. + * + *

          + * It is used to generate the coefficient for K₀-n+1, s when computing K₀-n-1, s + * and the coefficient for dK₀-n+1, s / de² when computing dK₀-n-1, s / de² + *

          + * + *

          + * See Danielson 2.7.3-(6) and Collins 4-242 and 4-245 + *

          + * + * @param s the s coefficient + * @param mnm1 -n-1 value + * @return the polynomial + */ + private static PolynomialFunction bZonal(final int s, final int mnm1) { + // from recurence Collins 4-242 + final double d1 = (mnm1 + 2) * (mnm1 + 3); + final double d2 = (mnm1 + 2 - s) * (mnm1 + 2 + s); + return new PolynomialFunction(new double[] { + 0.0, 0.0, -d1 / d2 + }); + } + + /** + * Generate the polynomials needed in the linear transformation. + * + * @param n0 the index of the initial condition, Petre's paper + * @param nMin rhe minimum value for the order + * @param offset offset used to identify the polynomial that corresponds + * to a negative value of n in the internal array that + * starts at 0 + * @param slice number of coefficients that will be computed with a set + * of roots + * @param s the s coefficient + * @param mpvec array to store the first vector of polynomials + * associated to Hansen coefficients and derivatives. + * @param mpvecDeriv array to store the second vector of polynomials + * associated only to derivatives. + *

          + * See Petre's paper + *

          + */ + public static void generateZonalPolynomials(final int n0, final int nMin, + final int offset, final int slice, + final int s, + final PolynomialFunction[][] mpvec, + final PolynomialFunction[][] mpvecDeriv) { + + int sliceCounter = 0; + int index; + + // Initialisation of matrix for linear transformmations + // The final configuration of these matrix are obtained by composition + // of linear transformations + PolynomialFunctionMatrix A = HansenUtilities.buildIdentityMatrix2(); + PolynomialFunctionMatrix D = HansenUtilities.buildZeroMatrix2(); + PolynomialFunctionMatrix E = HansenUtilities.buildIdentityMatrix2(); + + // generation of polynomials associated to Hansen coefficients and to + // their derivatives + final PolynomialFunctionMatrix a = HansenUtilities.buildZeroMatrix2(); + a.setElem(0, 1, HansenUtilities.ONE); + + //The B matrix is constant. + final PolynomialFunctionMatrix B = HansenUtilities.buildZeroMatrix2(); + // from Collins 4-245 and Petre's paper + B.setElem(1, 1, new PolynomialFunction(new double[] { + 2.0 + })); + + for (int i = n0 - 1; i > nMin - 1; i--) { + index = i + offset; + // Matrix of the current linear transformation + // Petre's paper + a.setMatrixLine(1, new PolynomialFunction[] { + bZonal(s, i), aZonal(s, i) + }); + // composition of linear transformations + // see Petre's paper + A = A.multiply(a); + // store the polynomials for Hansen coefficients + mpvec[index] = A.getMatrixLine(1); + + D = D.multiply(a); + E = E.multiply(a); + D = D.add(E.multiply(B)); + + // store the polynomials for Hansen coefficients from the expressions + // of derivatives + mpvecDeriv[index] = D.getMatrixLine(1); + + if (++sliceCounter % slice == 0) { + // Re-Initialisation of matrix for linear transformmations + // The final configuration of these matrix are obtained by composition + // of linear transformations + A = HansenUtilities.buildIdentityMatrix2(); + D = HansenUtilities.buildZeroMatrix2(); + E = HansenUtilities.buildIdentityMatrix2(); + } + + } + } + + /** + * Compute polynomial coefficient a. + * + *

          + * It is used to generate the coefficient for Kj-n, s when computing Kj-n-1, s + * and the coefficient for dKj-n, s / de² when computing dKj-n-1, s / de² + *

          + * + *

          + * See Danielson 2.7.3-(9) and Collins 4-236 and 4-240 + *

          + * + * @param s the s coefficient + * @param mnm1 -n-1 + * @return the polynomial + */ + private static PolynomialFunction aTesseral(final int s, final int mnm1) { + // Collins 4-236, Danielson 2.7.3-(9) + final double r1 = (mnm1 + 2.) * (2. * mnm1 + 5.); + final double r2 = (2. + mnm1 + s) * (2. + mnm1 - s); + return new PolynomialFunction(new double[] { + 0.0, 0.0, r1 / r2 + }); + } + + /** + * Compute polynomial coefficient b. + * + *

          + * It is used to generate the coefficient for Kj-n+1, s when computing Kj-n-1, s + * and the coefficient for dKj-n+1, s / de² when computing dKj-n-1, s / de² + *

          + * + *

          + * See Danielson 2.7.3-(9) and Collins 4-236 and 4-240 + *

          + * + * @param j the j coefficient + * @param s the s coefficient + * @param mnm1 -n-1 + * @return the polynomial + */ + private static PolynomialFunction bTesseral(final int j, final int s, final int mnm1) { + // Collins 4-236, Danielson 2.7.3-(9) + final double r2 = (2. + mnm1 + s) * (2. + mnm1 - s); + final double d1 = (mnm1 + 3.) * 2. * j * s / (r2 * (mnm1 + 4.)); + final double d2 = (mnm1 + 3.) * (mnm1 + 2.) / r2; + return new PolynomialFunction(new double[] { + 0.0, -d1, -d2 + }); + } + + /** + * Compute polynomial coefficient c. + * + *

          + * It is used to generate the coefficient for Kj-n+3, s when computing Kj-n-1, s + * and the coefficient for dKj-n+3, s / de² when computing dKj-n-1, s / de² + *

          + * + *

          + * See Danielson 2.7.3-(9) and Collins 4-236 and 4-240 + *

          + * + * @param j the j coefficient + * @param s the s coefficient + * @param mnm1 -n-1 + * @return the polynomial + */ + private static PolynomialFunction cTesseral(final int j, final int s, final int mnm1) { + // Collins 4-236, Danielson 2.7.3-(9) + final double r1 = j * j * (mnm1 + 2.); + final double r2 = (mnm1 + 4.) * (2. + mnm1 + s) * (2. + mnm1 - s); + + return new PolynomialFunction(new double[] { + 0.0, 0.0, r1 / r2 + }); + } + + /** + * Compute polynomial coefficient d. + *

          + * It is used to generate the coefficient for Kj-n-1, + * s / dχ when computing dKj-n-1, s / de² + *

          + *

          + * See Danielson 2.7.3-(9) and Collins 4-236 and 4-240 + *

          + * + * @return the polynomial + */ + private static PolynomialFunction dTesseral() { + // Collins 4-236, Danielson 2.7.3-(9) + return new PolynomialFunction(new double[] { + 0.0, 0.0, 1.0 + }); + } + + /** + * Compute polynomial coefficient f. + * + *

          + * It is used to generate the coefficient for Kj-n+1, s / dχ when computing dKj-n-1, s / de² + *

          + * + *

          + * See Danielson 2.7.3-(9) and Collins 4-236 and 4-240 + *

          + * + * @param j the j coefficient + * @param s the s coefficient + * @param n index + * @return the polynomial + */ + private static PolynomialFunction fTesseral(final int j, final int s, final int n) { + // Collins 4-236, Danielson 2.7.3-(9) + final double r1 = (n + 3.0) * j * s; + final double r2 = (n + 4.0) * (2.0 + n + s) * (2.0 + n - s); + return new PolynomialFunction(new double[] { + 0.0, 0.0, 0.0, r1 / r2 + }); + } + + /** + * Generate the polynomials needed in the linear transformation. + * + * @param n0 the index of the initial condition, Petre's paper + * @param nMin rhe minimum value for the order + * @param offset offset used to identify the polynomial that corresponds + * to a negative value of n in the internal array that + * starts at 0 + * @param slice number of coefficients that will be computed with a set + * of roots + * @param j the j coefficient + * @param s the s coefficient + * @param mpvec array to store the first vector of polynomials + * associated to Hansen coefficients and derivatives. + * @param mpvecDeriv array to store the second vector of polynomials + * associated only to derivatives. + */ + public static void generateTesseralPolynomials(final int n0, final int nMin, + final int offset, final int slice, + final int j, final int s, + final PolynomialFunction[][] mpvec, + final PolynomialFunction[][] mpvecDeriv) { + + + // Initialization of the matrices for linear transformations + // The final configuration of these matrices are obtained by composition + // of linear transformations + + // The matrix of polynomials associated to Hansen coefficients, Petre's + // paper + PolynomialFunctionMatrix A = HansenUtilities.buildIdentityMatrix4(); + + // The matrix of polynomials associated to derivatives, Petre's paper + final PolynomialFunctionMatrix B = HansenUtilities.buildZeroMatrix4(); + PolynomialFunctionMatrix D = HansenUtilities.buildZeroMatrix4(); + final PolynomialFunctionMatrix a = HansenUtilities.buildZeroMatrix4(); + + // The matrix of the current linear transformation + a.setMatrixLine(0, new PolynomialFunction[] { + HansenUtilities.ZERO, HansenUtilities.ONE, HansenUtilities.ZERO, HansenUtilities.ZERO + }); + a.setMatrixLine(1, new PolynomialFunction[] { + HansenUtilities.ZERO, HansenUtilities.ZERO, HansenUtilities.ONE, HansenUtilities.ZERO + }); + a.setMatrixLine(2, new PolynomialFunction[] { + HansenUtilities.ZERO, HansenUtilities.ZERO, HansenUtilities.ZERO, HansenUtilities.ONE + }); + // The generation process + int index; + int sliceCounter = 0; + for (int i = n0 - 1; i > nMin - 1; i--) { + index = i + offset; + // The matrix of the current linear transformation is updated + // Petre's paper + a.setMatrixLine(3, new PolynomialFunction[] { + cTesseral(j, s, i), HansenUtilities.ZERO, bTesseral(j, s, i), aTesseral(s, i) + }); + + // composition of the linear transformations to calculate + // the polynomials associated to Hansen coefficients + // Petre's paper + A = A.multiply(a); + // store the polynomials for Hansen coefficients + mpvec[index] = A.getMatrixLine(3); + // composition of the linear transformations to calculate + // the polynomials associated to derivatives + // Petre's paper + D = D.multiply(a); + + //Update the B matrix + B.setMatrixLine(3, new PolynomialFunction[] { + HansenUtilities.ZERO, fTesseral(j, s, i), + HansenUtilities.ZERO, dTesseral() + }); + D = D.add(A.multiply(B)); + + // store the polynomials for Hansen coefficients from the + // expressions of derivatives + mpvecDeriv[index] = D.getMatrixLine(3); + + if (++sliceCounter % slice == 0) { + // Re-Initialisation of matrix for linear transformmations + // The final configuration of these matrix are obtained by composition + // of linear transformations + A = HansenUtilities.buildIdentityMatrix4(); + D = HansenUtilities.buildZeroMatrix4(); + } + } + } + + /** + * Compute polynomial coefficient a. + * + *

          + * It is used to generate the coefficient for K₀n-1, s when computing K₀n, s + * and the coefficient for dK₀n-1, s / dΧ when computing dK₀n, s / dΧ + *

          + * + *

          + * See Danielson 2.7.3-(7c) and Collins 4-254 and 4-257 + *

          + * + * @param n n value + * @return the polynomial + */ + private static PolynomialFunction aThirdBody(final int n) { + // from recurrence Danielson 2.7.3-(7c), Collins 4-254 + final double r1 = 2 * n + 1; + final double r2 = n + 1; + + return new PolynomialFunction(new double[] { + r1 / r2 + }); + } + + /** + * Compute polynomial coefficient b. + * + *

          + * It is used to generate the coefficient for K₀n-2, s when computing K₀n, s + * and the coefficient for dK₀n-2, s / dΧ when computing dK₀n, s / dΧ + *

          + * + *

          + * See Danielson 2.7.3-(7c) and Collins 4-254 and 4-257 + *

          + * + * @param s the s coefficient + * @param n n value + * @return the polynomial + */ + private static PolynomialFunction bThirdBody(final int s, final int n) { + // from recurrence Danielson 2.7.3-(7c), Collins 4-254 + final double r1 = (n + s) * (n - s); + final double r2 = n * (n + 1); + + return new PolynomialFunction(new double[] { + 0.0, 0.0, -r1 / r2 + }); + } + + /** + * Compute polynomial coefficient d. + * + *

          + * It is used to generate the coefficient for K₀n-2, s when computing dK₀n, s / dΧ + *

          + * + *

          + * See Danielson 2.7.3-(7c) and Collins 4-254 and 4-257 + *

          + * + * @param s the s coefficient + * @param n n value + * @return the polynomial + */ + private static PolynomialFunction dThirdBody(final int s, final int n) { + // from Danielson 3.2-(3b) + final double r1 = 2 * (n + s) * (n - s); + final double r2 = n * (n + 1); + + return new PolynomialFunction(new double[] { + 0.0, 0.0, 0.0, r1 / r2 + }); + } + + /** + * Generate the polynomials needed in the linear transformation. + * + * @param n0 the index of the initial condition, Petre's paper + * @param nMax the maximum order of n indexes + * @param slice number of coefficients that will be computed with a set of roots + * @param s the s coefficient + * @param mpvec array to store the first vector of polynomials + * associated to Hansen coefficients and derivatives. + * @param mpvecDeriv array to store the second vector of polynomials + * associated only to derivatives. + *

          + * See Petre's paper + *

          + */ + public static void generateThirdBodyPolynomials(final int n0, final int nMax, + final int slice, + final int s, + final PolynomialFunction[][] mpvec, + final PolynomialFunction[][] mpvecDeriv) { + + int sliceCounter = 0; + + // Initialization of the matrices for linear transformations + // The final configuration of these matrices are obtained by composition + // of linear transformations + + // the matrix A for the polynomials associated + // to Hansen coefficients, Petre's pater + PolynomialFunctionMatrix A = HansenUtilities.buildIdentityMatrix2(); + + // the matrix D for the polynomials associated + // to derivatives, Petre's paper + final PolynomialFunctionMatrix B = HansenUtilities.buildZeroMatrix2(); + PolynomialFunctionMatrix D = HansenUtilities.buildZeroMatrix2(); + PolynomialFunctionMatrix E = HansenUtilities.buildIdentityMatrix2(); + + // The matrix that contains the coefficients at each step + final PolynomialFunctionMatrix a = HansenUtilities.buildZeroMatrix2(); + a.setElem(0, 1, HansenUtilities.ONE); + + // The generation process + for (int i = n0 + 2; i <= nMax; i++) { + // Collins 4-254 or Danielson 2.7.3-(7) + // Petre's paper + // The matrix of the current linear transformation is actualized + a.setMatrixLine(1, new PolynomialFunction[] { + bThirdBody(s, i), aThirdBody(i) + }); + + // composition of the linear transformations to calculate + // the polynomials associated to Hansen coefficients + A = A.multiply(a); + // store the polynomials associated to Hansen coefficients + mpvec[i] = A.getMatrixLine(1); + // composition of the linear transformations to calculate + // the polynomials associated to derivatives + // Danielson 3.2-(3b) and Petre's paper + D = D.multiply(a); + if (sliceCounter % slice != 0) { + a.setMatrixLine(1, new PolynomialFunction[] { + bThirdBody(s, i - 1), aThirdBody(i - 1) + }); + E = E.multiply(a); + } + + B.setElem(1, 0, dThirdBody(s, i)); + // F = E.prod(B); + D = D.add(E.multiply(B)); + // store the polynomials associated to the derivatives + mpvecDeriv[i] = D.getMatrixLine(1); + + if (++sliceCounter % slice == 0) { + // Re-Initialization of the matrices for linear transformations + // The final configuration of these matrices are obtained by composition + // of linear transformations + A = HansenUtilities.buildIdentityMatrix2(); + D = HansenUtilities.buildZeroMatrix2(); + E = HansenUtilities.buildIdentityMatrix2(); + } + } + } + } diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/HansenZonalLinear.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/HansenZonalLinear.java index 5d95727a67..dd1d3fcce5 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/HansenZonalLinear.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/HansenZonalLinear.java @@ -39,47 +39,40 @@ public class HansenZonalLinear { * The first vector of polynomials associated to Hansen coefficients and * derivatives. */ - private PolynomialFunction[][] mpvec; + private final PolynomialFunction[][] mpvec; /** The second vector of polynomials associated only to derivatives. */ - private PolynomialFunction[][] mpvecDeriv; + private final PolynomialFunction[][] mpvecDeriv; /** The Hansen coefficients used as roots. */ - private double[][] hansenRoot; + private final double[][] hansenRoot; /** The derivatives of the Hansen coefficients used as roots. */ - private double[][] hansenDerivRoot; - - /** The minimum value for the order. */ - private int Nmin; - - - /** The index of the initial condition, Petre's paper. */ - private int N0; + private final double[][] hansenDerivRoot; /** The s coefficient. */ - private int s; + private final int s; /** * The offset used to identify the polynomial that corresponds to a negative * value of n in the internal array that starts at 0. */ - private int offset; + private final int offset; /** The number of slices needed to compute the coefficients. */ - private int numSlices; + private final int numSlices; /** 2s. */ - private double twots; + private final double twots; /** 2*s+1. */ - private int twosp1; + private final int twosp1; /** 2*s. */ - private int twos; + private final int twos; /** (2*s+1) / 2s. */ - private double twosp1otwots; + private final double twosp1otwots; /** * Constructor. @@ -90,9 +83,9 @@ public class HansenZonalLinear { public HansenZonalLinear(final int nMax, final int s) { //Initialize fields + final int Nmin = -nMax - 1; + final int N0 = -(s + 2); this.offset = nMax + 1; - this.Nmin = -nMax - 1; - N0 = -(s + 2); this.s = s; this.twots = FastMath.pow(2., s); this.twos = 2 * s; @@ -109,122 +102,11 @@ public HansenZonalLinear(final int nMax, final int s) { hansenDerivRoot = new double[numSlices][2]; // Prepare the data base of associated polynomials - generatePolynomials(); + HansenUtilities.generateZonalPolynomials(N0, Nmin, offset, SLICE, s, + mpvec, mpvecDeriv); } - /** - * Compute polynomial coefficient a. - * - *

          - * It is used to generate the coefficient for K₀-n, s when computing K₀-n-1, s - * and the coefficient for dK₀-n, s / de² when computing dK₀-n-1, s / de² - *

          - * - *

          - * See Danielson 2.7.3-(6) and Collins 4-242 and 4-245 - *

          - * - * @param mnm1 -n-1 value - * @return the polynomial - */ - private PolynomialFunction a(final int mnm1) { - // from recurence Collins 4-242 - final double d1 = (mnm1 + 2) * (2 * mnm1 + 5); - final double d2 = (mnm1 + 2 - s) * (mnm1 + 2 + s); - return new PolynomialFunction(new double[] { - 0.0, 0.0, d1 / d2 - }); - } - - /** - * Compute polynomial coefficient b. - * - *

          - * It is used to generate the coefficient for K₀-n+1, s when computing K₀-n-1, s - * and the coefficient for dK₀-n+1, s / de² when computing dK₀-n-1, s / de² - *

          - * - *

          - * See Danielson 2.7.3-(6) and Collins 4-242 and 4-245 - *

          - * - * @param mnm1 -n-1 value - * @return the polynomial - */ - private PolynomialFunction b(final int mnm1) { - // from recurence Collins 4-242 - final double d1 = (mnm1 + 2) * (mnm1 + 3); - final double d2 = (mnm1 + 2 - s) * (mnm1 + 2 + s); - return new PolynomialFunction(new double[] { - 0.0, 0.0, -d1 / d2 - }); - } - - /** - * Generate the polynomials needed in the linear transformation. - * - *

          - * See Petre's paper - *

          - */ - private void generatePolynomials() { - - int sliceCounter = 0; - int index; - - // Initialisation of matrix for linear transformmations - // The final configuration of these matrix are obtained by composition - // of linear transformations - PolynomialFunctionMatrix A = HansenUtilities.buildIdentityMatrix2(); - PolynomialFunctionMatrix D = HansenUtilities.buildZeroMatrix2(); - PolynomialFunctionMatrix E = HansenUtilities.buildIdentityMatrix2(); - - // generation of polynomials associated to Hansen coefficients and to - // their derivatives - final PolynomialFunctionMatrix a = HansenUtilities.buildZeroMatrix2(); - a.setElem(0, 1, HansenUtilities.ONE); - - //The B matrix is constant. - final PolynomialFunctionMatrix B = HansenUtilities.buildZeroMatrix2(); - // from Collins 4-245 and Petre's paper - B.setElem(1, 1, new PolynomialFunction(new double[] { - 2.0 - })); - - for (int i = N0 - 1; i > Nmin - 1; i--) { - index = i + offset; - // Matrix of the current linear transformation - // Petre's paper - a.setMatrixLine(1, new PolynomialFunction[] { - b(i), a(i) - }); - // composition of linear transformations - // see Petre's paper - A = A.multiply(a); - // store the polynomials for Hansen coefficients - mpvec[index] = A.getMatrixLine(1); - - D = D.multiply(a); - E = E.multiply(a); - D = D.add(E.multiply(B)); - - // store the polynomials for Hansen coefficients from the expressions - // of derivatives - mpvecDeriv[index] = D.getMatrixLine(1); - - if (++sliceCounter % SLICE == 0) { - // Re-Initialisation of matrix for linear transformmations - // The final configuration of these matrix are obtained by composition - // of linear transformations - A = HansenUtilities.buildIdentityMatrix2(); - D = HansenUtilities.buildZeroMatrix2(); - E = HansenUtilities.buildIdentityMatrix2(); - } - - } - } - /** * Compute the roots for the Hansen coefficients and their derivatives. * diff --git a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/PolynomialFunctionMatrix.java b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/PolynomialFunctionMatrix.java index 94575702a3..13eacfeb31 100644 --- a/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/PolynomialFunctionMatrix.java +++ b/src/main/java/org/orekit/propagation/semianalytical/dsst/utilities/hansen/PolynomialFunctionMatrix.java @@ -30,7 +30,7 @@ public class PolynomialFunctionMatrix { /** The order of the matrix. */ private int order; /** The elements of the matrix. */ - private PolynomialFunction elements[][]; + private PolynomialFunction[][] elements; /** * Create a matrix. diff --git a/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/AbstractShortTermEncounter1DNumerical2DPOCMethod.java b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/AbstractShortTermEncounter1DNumerical2DPOCMethod.java index 1825ba93a6..20e8a29a5e 100644 --- a/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/AbstractShortTermEncounter1DNumerical2DPOCMethod.java +++ b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/AbstractShortTermEncounter1DNumerical2DPOCMethod.java @@ -138,16 +138,18 @@ public > FieldProbabilityOfCollision comput final DataContext cdmDataContext = cdm.getDataContext(); // Extract primary data - final FieldOrbit primaryOrbit = - Fieldifier.fieldify(field, getObjectOrbitFromCdm(cdmRelativeMetadata, primaryData, - primaryMetadata, cdmDataContext)); + final Orbit primaryOrbitFromCdm = getObjectOrbitFromCdm(cdmRelativeMetadata, primaryData, + primaryMetadata, cdmDataContext); + final FieldOrbit primaryOrbit = primaryOrbitFromCdm.getType().convertToFieldOrbit(field, + primaryOrbitFromCdm); final FieldStateCovariance primaryCovariance = Fieldifier.fieldify(field, getObjectStateCovarianceFromCdm(cdmRelativeMetadata, primaryData)); // Extract secondary data - final FieldOrbit secondaryOrbit = - Fieldifier.fieldify(field, getObjectOrbitFromCdm(cdmRelativeMetadata, secondaryData, - secondaryMetadata, cdmDataContext)); + final Orbit secondaryOrbitFromCdm = getObjectOrbitFromCdm(cdmRelativeMetadata, secondaryData, + secondaryMetadata, cdmDataContext); + final FieldOrbit secondaryOrbit = secondaryOrbitFromCdm.getType().convertToFieldOrbit(field, + secondaryOrbitFromCdm); final FieldStateCovariance secondaryCovariance = Fieldifier.fieldify(field, getObjectStateCovarianceFromCdm(cdmRelativeMetadata, secondaryData)); diff --git a/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/AbstractShortTermEncounter2DPOCMethod.java b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/AbstractShortTermEncounter2DPOCMethod.java index d21eb5f9ce..85f2ac1e9c 100644 --- a/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/AbstractShortTermEncounter2DPOCMethod.java +++ b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/AbstractShortTermEncounter2DPOCMethod.java @@ -119,16 +119,18 @@ public > FieldProbabilityOfCollision comput final DataContext cdmDataContext = cdm.getDataContext(); // Extract primary data - final FieldOrbit primaryOrbit = - Fieldifier.fieldify(field, getObjectOrbitFromCdm(cdmRelativeMetadata, primaryData, - primaryMetadata, cdmDataContext)); + final Orbit primaryOrbitFromCdm = getObjectOrbitFromCdm(cdmRelativeMetadata, primaryData, + primaryMetadata, cdmDataContext); + final FieldOrbit primaryOrbit = primaryOrbitFromCdm.getType().convertToFieldOrbit(field, + primaryOrbitFromCdm); final FieldStateCovariance primaryCovariance = Fieldifier.fieldify(field, getObjectStateCovarianceFromCdm(cdmRelativeMetadata, primaryData)); // Extract secondary data - final FieldOrbit secondaryOrbit = - Fieldifier.fieldify(field, getObjectOrbitFromCdm(cdmRelativeMetadata, secondaryData, - secondaryMetadata, cdmDataContext)); + final Orbit secondaryOrbitFromCdm = getObjectOrbitFromCdm(cdmRelativeMetadata, secondaryData, + secondaryMetadata, cdmDataContext); + final FieldOrbit secondaryOrbit = secondaryOrbitFromCdm.getType().convertToFieldOrbit(field, + secondaryOrbitFromCdm); final FieldStateCovariance secondaryCovariance = Fieldifier.fieldify(field, getObjectStateCovarianceFromCdm(cdmRelativeMetadata, secondaryData)); @@ -198,14 +200,6 @@ public > FieldProbabilityOfCollision comput encounter.getCombinedRadius()); } - /** {@inheritDoc} */ - public abstract ProbabilityOfCollision compute(double xm, double ym, double sigmaX, double sigmaY, double radius); - - /** {@inheritDoc} */ - public abstract > FieldProbabilityOfCollision compute(T xm, T ym, - T sigmaX, T sigmaY, - T radius); - /** {@inheritDoc} */ @Override public String getName() { diff --git a/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Alfano2005.java b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Alfano2005.java index 7b70e30da4..5d11823e10 100644 --- a/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Alfano2005.java +++ b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Alfano2005.java @@ -120,7 +120,7 @@ public > FieldProbabilityOfCollision comput final T rootTwoSigmaY = sigmaY.multiply(FastMath.sqrt(2)); - final T otherTerm = xm.multiply(xm).divide(sigmaX.multiply(sigmaX).multiply(-2.)).exp() + final T otherTerm = xm.square().divide(sigmaX.multiply(sigmaX).multiply(-2.)).exp() .multiply(Erf.erf(radius.subtract(ym).divide(rootTwoSigmaY)) .subtract(Erf.erf(radius.add(ym).negate().divide(rootTwoSigmaY)))); @@ -248,16 +248,16 @@ private double getRecurrentPart(final double x, final double xm, final double ym */ private > T getRecurrentPart(final T x, final T xm, final T ym, final T sigmaX, final T sigmaY, final T radius) { - final T minusTwoSigmaXSquared = sigmaX.multiply(sigmaX).multiply(-2.); - final T radiusSquaredMinusXSquaredSQRT = radius.multiply(radius).subtract(x.multiply(x)).sqrt(); + final T minusTwoSigmaXSquared = sigmaX.square().multiply(-2.); + final T radiusSquaredMinusXSquaredSQRT = radius.square().subtract(x.square()).sqrt(); final T rootTwoSigmaY = sigmaY.multiply(FastMath.sqrt(2)); final T xMinusXm = x.subtract(xm); final T xPlusXm = x.add(xm); return Erf.erf(radiusSquaredMinusXSquaredSQRT.subtract(ym).divide(rootTwoSigmaY)).subtract( Erf.erf(radiusSquaredMinusXSquaredSQRT.add(ym).negate().divide(rootTwoSigmaY))) - .multiply(xMinusXm.multiply(xMinusXm).divide(minusTwoSigmaXSquared).exp() - .add(xPlusXm.multiply(xPlusXm).divide(minusTwoSigmaXSquared).exp())); + .multiply(xMinusXm.square().divide(minusTwoSigmaXSquared).exp() + .add(xPlusXm.square().divide(minusTwoSigmaXSquared).exp())); } /** {@inheritDoc} */ diff --git a/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Alfriend1999.java b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Alfriend1999.java index 7d2195bc0e..11efdea40c 100644 --- a/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Alfriend1999.java +++ b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Alfriend1999.java @@ -72,7 +72,7 @@ public ShortTermEncounter2DPOCMethodType getType() { @Override > T computeValue(final T radius, final T squaredMahalanobisDistance, final T covarianceMatrixDeterminant) { - return squaredMahalanobisDistance.multiply(-0.5).exp().multiply(radius).multiply(radius) + return squaredMahalanobisDistance.multiply(-0.5).exp().multiply(radius.square()) .divide(covarianceMatrixDeterminant.sqrt().multiply(2.)); } diff --git a/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/FieldShortTermEncounter2DDefinition.java b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/FieldShortTermEncounter2DDefinition.java index 8ec3f02e01..b61710d05c 100644 --- a/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/FieldShortTermEncounter2DDefinition.java +++ b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/FieldShortTermEncounter2DDefinition.java @@ -30,6 +30,8 @@ import org.hipparchus.util.MathUtils; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; +import org.orekit.frames.FieldKinematicTransform; +import org.orekit.frames.FieldStaticTransform; import org.orekit.frames.FieldTransform; import org.orekit.frames.Frame; import org.orekit.frames.LOF; @@ -277,7 +279,10 @@ public FieldPVCoordinates computeOtherRelativeToReferencePVInReferenceInertia // Get PVCoordinates in the same frame final FieldPVCoordinates referencePV = referenceAtTCA.getPVCoordinates(); - final FieldPVCoordinates otherPVInReferenceInertial = otherAtTCA.getPVCoordinates(referenceInertial); + final FieldKinematicTransform kinematicTransform = otherAtTCA.getFrame() + .getKinematicTransformTo(referenceInertial, referenceAtTCA.getDate()); + final FieldPVCoordinates otherPVInReferenceInertial = kinematicTransform + .transformOnlyPV(otherAtTCA.getPVCoordinates()); // Create relative pv expressed in the reference inertial frame final FieldVector3D relativePosition = otherPVInReferenceInertial.getPosition() @@ -300,8 +305,8 @@ public FieldPVCoordinates computeOtherRelativeToReferencePVInReferenceInertia public FieldMatrix computeReferenceInertialToCollisionPlaneProjectionMatrix() { // Create transform from reference inertial frame to encounter local orbital frame - final FieldTransform referenceInertialToEncounterFrameTransform = - new FieldTransform<>(tca, + final FieldStaticTransform referenceInertialToEncounterFrameTransform = + FieldStaticTransform.compose(tca, computeReferenceInertialToReferenceTNWTransform(), computeReferenceTNWToEncounterFrameTransform()); @@ -334,7 +339,7 @@ public FieldMatrix computeProjectedAndDiagonalizedCombinedPositionalCovarianc final T crossTerm = covarianceMatrixToDiagonalize.getEntry(0, 1); final T recurrentTerm = sigmaXSquared.subtract(sigmaYSquared).multiply(0.5).pow(2) - .add(crossTerm.multiply(crossTerm)).sqrt(); + .add(crossTerm.square()).sqrt(); final T eigenValueX = sigmaXSquared.add(sigmaYSquared).multiply(0.5).subtract(recurrentTerm); final T eigenValueY = sigmaXSquared.add(sigmaYSquared).multiply(0.5).add(recurrentTerm); @@ -381,18 +386,17 @@ public FieldStateCovariance computeCombinedCovarianceInEncounterFrame() { public FieldVector2D computeOtherPositionInCollisionPlane() { // Express other in reference inertial - final FieldPVCoordinates otherInReferenceInertial = otherAtTCA.getPVCoordinates(referenceAtTCA.getFrame()); + final FieldVector3D otherInReferenceInertial = otherAtTCA.getPosition(referenceAtTCA.getFrame()); // Express other in reference TNW local orbital frame - final FieldPVCoordinates otherPVInReferenceTNW = - computeReferenceInertialToReferenceTNWTransform().transformPVCoordinates(otherInReferenceInertial); + final FieldVector3D otherPositionInReferenceTNW = + computeReferenceInertialToReferenceTNWTransform().transformPosition(otherInReferenceInertial); // Express other in encounter local orbital frame - final FieldPVCoordinates otherPVInEncounterFrame = - computeReferenceTNWToEncounterFrameTransform().transformPVCoordinates( - otherPVInReferenceTNW); + final FieldVector3D otherPositionInEncounterFrame = + computeReferenceTNWToEncounterFrameTransform().transformPosition(otherPositionInReferenceTNW); - return encounterFrame.projectOntoCollisionPlane(otherPVInEncounterFrame.getPosition()); + return encounterFrame.projectOntoCollisionPlane(otherPositionInEncounterFrame); } @@ -455,7 +459,7 @@ public FieldVector2D computeOtherPositionInRotatedCollisionPlane(final double public T computeCoppolaEncounterDuration() { // Default value for γ = 1e-16 - final T DEFAULT_ALPHA_C = instanceField.getOne().multiply(5.864); + final T DEFAULT_ALPHA_C = instanceField.getZero().newInstance(5.864); final FieldMatrix combinedPositionalCovarianceMatrix = computeCombinedCovarianceInEncounterFrame() .getMatrix().getSubMatrix(0, 2, 0, 2); @@ -670,7 +674,7 @@ private FieldMatrix computeEncounterPlaneRotationMatrix(final double zeroThre else { // Rotation in order to have sigmaXSquared < sigmaYSquared if (sigmaXSquared.subtract(sigmaYSquared).getReal() > 0) { - theta = tca.getField().getOne().multiply(MathUtils.SEMI_PI); + theta = tca.getField().getZero().newInstance(MathUtils.SEMI_PI); } // Else, there is no need for a rotation else { diff --git a/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Laas2015.java b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Laas2015.java index 3c47347488..c42ae66230 100644 --- a/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Laas2015.java +++ b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Laas2015.java @@ -280,16 +280,16 @@ public final > FieldProbabilityOfCollision final T zero = field.getZero(); final T one = field.getOne(); - final T xmSquared = xm.multiply(xm); - final T ymSquared = ym.multiply(ym); - final T sigmaXSquared = sigmaX.multiply(sigmaX); - final T sigmaYSquared = sigmaY.multiply(sigmaY); + final T xmSquared = xm.square(); + final T ymSquared = ym.square(); + final T sigmaXSquared = sigmaX.square(); + final T sigmaYSquared = sigmaY.square(); final T twoSigmaXY = sigmaX.multiply(sigmaY).multiply(2); - final T radiusSquared = radius.multiply(radius); - final T radiusFourth = radiusSquared.multiply(radiusSquared); + final T radiusSquared = radius.square(); + final T radiusFourth = radiusSquared.square(); final T radiusSixth = radiusFourth.multiply(radiusSquared); - final T p = sigmaX.multiply(sigmaX).reciprocal().multiply(0.5); + final T p = sigmaX.square().reciprocal().multiply(0.5); final T pTimesRadiusSquared = p.multiply(radiusSquared); final T phiY = sigmaXSquared.divide(sigmaYSquared).negate().add(1.); final T omegaX = xm.divide(sigmaXSquared.multiply(2.)).pow(2.); @@ -298,10 +298,10 @@ public final > FieldProbabilityOfCollision final T bigOmega = phiY.multiply(0.5).add(omegaX.add(omegaY).divide(p)); final T minusP = p.negate(); - final T pSquared = p.multiply(p); + final T pSquared = p.square(); final T pCubed = p.multiply(pSquared); final T pPhiY = p.multiply(phiY); - final T phiYSquared = phiY.multiply(phiY); + final T phiYSquared = phiY.square(); final T pRadiusSquaredBigOmega = p.multiply(radiusSquared).multiply(bigOmega); final T bigOmegaPlusOne = bigOmega.add(1.); final T pSqTimesHalfPhiYSqPlusOne = pSquared.multiply(phiYSquared.multiply(0.5).add(1.)); @@ -349,11 +349,11 @@ public final > FieldProbabilityOfCollision final T auxiliaryTerm4 = pPhiY.multiply(recurrentTerm0).multiply(2.); final T auxiliaryTerm5 = p.multiply(phiY.multiply(2.).add(1.)); - T kPlus2 = one.multiply(2.); - T kPlus3 = one.multiply(3.); - T kPlus4 = one.multiply(4.); - T kPlus5 = one.multiply(5.); - T halfY = one.multiply(2.5); + T kPlus2 = one.newInstance(2.); + T kPlus3 = one.newInstance(3.); + T kPlus4 = one.newInstance(4.); + T kPlus5 = one.newInstance(5.); + T halfY = one.newInstance(2.5); // Initialize recurrence T c0 = alpha0.multiply(radiusSquared); diff --git a/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Patera2005.java b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Patera2005.java index e94f2cc5de..6b6ef91a24 100644 --- a/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Patera2005.java +++ b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Patera2005.java @@ -122,10 +122,10 @@ public > FieldProbabilityOfCollision comput final Field field = xm.getField(); final T zero = field.getZero(); final T one = field.getOne(); - final T twoPiField = one.multiply(MathUtils.TWO_PI); + final T twoPiField = one.newInstance(MathUtils.TWO_PI); final T value; - final double missDistance = xm.multiply(xm).add(ym.multiply(ym)).sqrt().getReal(); + final double missDistance = xm.square().add(ym.square()).sqrt().getReal(); final double radiusReal = radius.getReal(); // Reference outside the hardbody area, first part of eq(11) is equal to 0 @@ -399,10 +399,10 @@ public T value(final T xm, final T ym, final T scaleFactor, final T sigma, final T yPrime = getYPrime(sinTheta); final T rSquared = getRSquared(xPrime, yPrime); - return rSquared.divide(sigma).divide(sigma).multiply(-0.5).exp() + return rSquared.divide(sigma.square()).multiply(-0.5).exp() .multiply(radius.multiply(scaleFactor).multiply(xm.multiply(cosTheta) .add(ym.multiply(sinTheta))) - .add(scaleFactor.multiply(radius).multiply(radius))).divide(rSquared); + .add(scaleFactor.multiply(radius.square()))).divide(rSquared); } } @@ -443,7 +443,7 @@ public T value(final T xm, final T ym, final T scaleFactor, final T sigma, final T xPrime = scaleFactor.multiply(xm).add(scaleFactor.multiply(radius).multiply(cosTheta)); final T yPrime = ym.add(radius.multiply(sinTheta)); - final T rSquared = xPrime.multiply(xPrime).add(yPrime.multiply(yPrime)); + final T rSquared = xPrime.square().add(yPrime.square()); final T sigmaSquared = sigma.multiply(sigma); final T oneOverTwoSigmaSq = sigmaSquared.multiply(2.).reciprocal(); final T rSqOverTwoSigmaSq = rSquared.multiply(oneOverTwoSigmaSq); diff --git a/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/ShortTermEncounter2DDefinition.java b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/ShortTermEncounter2DDefinition.java index 6b79bdcc24..6cb80ce5c3 100644 --- a/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/ShortTermEncounter2DDefinition.java +++ b/src/main/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/ShortTermEncounter2DDefinition.java @@ -29,6 +29,8 @@ import org.orekit.frames.Frame; import org.orekit.frames.LOF; import org.orekit.frames.LOFType; +import org.orekit.frames.KinematicTransform; +import org.orekit.frames.StaticTransform; import org.orekit.frames.Transform; import org.orekit.frames.encounter.EncounterLOF; import org.orekit.frames.encounter.EncounterLOFType; @@ -250,7 +252,9 @@ public PVCoordinates computeOtherRelativeToReferencePVInReferenceInertial() { // Get PVCoordinates in the same frame final PVCoordinates referencePV = referenceAtTCA.getPVCoordinates(); - final PVCoordinates otherPVInReferenceInertial = otherAtTCA.getPVCoordinates(referenceInertial); + final KinematicTransform kinematicTransform = otherAtTCA.getFrame().getKinematicTransformTo(referenceInertial, + otherAtTCA.getDate()); + final PVCoordinates otherPVInReferenceInertial = kinematicTransform.transformOnlyPV(otherAtTCA.getPVCoordinates()); // Create relative pv expressed in the reference inertial frame final Vector3D relativePosition = otherPVInReferenceInertial.getPosition().subtract(referencePV.getPosition()); @@ -271,9 +275,8 @@ public PVCoordinates computeOtherRelativeToReferencePVInReferenceInertial() { public RealMatrix computeReferenceInertialToCollisionPlaneProjectionMatrix() { // Create transform from reference inertial frame to encounter local orbital frame - final Transform referenceInertialToEncounterFrameTransform = - new Transform(tca, - computeReferenceInertialToReferenceTNWTransform(), + final StaticTransform referenceInertialToEncounterFrameTransform = + StaticTransform.compose(tca, computeReferenceInertialToReferenceTNWTransform(), computeReferenceTNWToEncounterFrameTransform()); // Create rotation matrix from reference inertial frame to encounter local orbital frame @@ -333,18 +336,17 @@ public StateCovariance computeCombinedCovarianceInEncounterFrame() { public Vector2D computeOtherPositionInCollisionPlane() { // Express other in reference inertial - final PVCoordinates otherInReferenceInertial = otherAtTCA.getPVCoordinates(referenceAtTCA.getFrame()); + final Vector3D otherInReferenceInertial = otherAtTCA.getPosition(referenceAtTCA.getFrame()); // Express other in reference TNW local orbital frame - final PVCoordinates otherPVInReferenceTNW = - computeReferenceInertialToReferenceTNWTransform().transformPVCoordinates(otherInReferenceInertial); + final Vector3D otherPositionInReferenceTNW = + computeReferenceInertialToReferenceTNWTransform().transformPosition(otherInReferenceInertial); // Express other in encounter local orbital frame - final PVCoordinates otherPVInEncounterFrame = - computeReferenceTNWToEncounterFrameTransform().transformPVCoordinates( - otherPVInReferenceTNW); + final Vector3D otherPositionInEncounterFrame = + computeReferenceTNWToEncounterFrameTransform().transformPosition(otherPositionInReferenceTNW); - return encounterFrame.projectOntoCollisionPlane(otherPVInEncounterFrame.getPosition()); + return encounterFrame.projectOntoCollisionPlane(otherPositionInEncounterFrame); } diff --git a/src/main/java/org/orekit/time/AbsoluteDate.java b/src/main/java/org/orekit/time/AbsoluteDate.java index 70fa87828b..e31a2e2c1c 100644 --- a/src/main/java/org/orekit/time/AbsoluteDate.java +++ b/src/main/java/org/orekit/time/AbsoluteDate.java @@ -18,17 +18,19 @@ import java.io.Serializable; import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; import java.util.Date; import java.util.TimeZone; +import java.util.concurrent.TimeUnit; import org.hipparchus.util.FastMath; import org.hipparchus.util.MathUtils; import org.hipparchus.util.MathUtils.SumAndResidual; import org.orekit.annotation.DefaultDataContext; import org.orekit.data.DataContext; -import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitIllegalArgumentException; -import org.orekit.errors.OrekitMessages; import org.orekit.utils.Constants; @@ -320,15 +322,15 @@ public AbsoluteDate(final DateComponents date, final TimeComponents time, if (regularOffset >= 0) { // regular case, the offset is between 0.0 and 1.0 offset = regularOffset; - epoch = 60l * ((date.getJ2000Day() * 24l + time.getHour()) * 60l + - time.getMinute() - time.getMinutesFromUTC() - 720l) + dl; + epoch = 60L * ((date.getJ2000Day() * 24L + time.getHour()) * 60L + + time.getMinute() - time.getMinutesFromUTC() - 720L) + dl; } else { // very rare case, the offset is just before a whole second // we will loose some bits of accuracy when adding 1 second // but this will ensure the offset remains in the [0.0; 1.0] interval offset = 1.0 + regularOffset; - epoch = 60l * ((date.getJ2000Day() * 24l + time.getHour()) * 60l + - time.getMinute() - time.getMinutesFromUTC() - 720l) + dl - 1; + epoch = 60L * ((date.getJ2000Day() * 24L + time.getHour()) * 60L + + time.getMinute() - time.getMinutesFromUTC() - 720L) + dl - 1; } } @@ -413,23 +415,47 @@ public AbsoluteDate(final int year, final Month month, final int day, */ public AbsoluteDate(final Date location, final TimeScale timeScale) { this(new DateComponents(DateComponents.JAVA_EPOCH, - (int) (location.getTime() / 86400000l)), - millisToTimeComponents((int) (location.getTime() % 86400000l)), + (int) (location.getTime() / 86400000L)), + millisToTimeComponents((int) (location.getTime() % 86400000L)), timeScale); } /** Build an instance from an {@link Instant instant} in a {@link TimeScale time scale}. + * + * @deprecated Use {@link AbsoluteDate#AbsoluteDate(Instant, UTCScale)} or {@link AbsoluteDate#AbsoluteDate(Instant)} instead * @param instant instant in the time scale * @param timeScale time scale * @since 12.0 */ + @Deprecated public AbsoluteDate(final Instant instant, final TimeScale timeScale) { this(new DateComponents(DateComponents.JAVA_EPOCH, - (int) (instant.getEpochSecond() / 86400l)), + (int) (instant.getEpochSecond() / 86400L)), instantToTimeComponents(instant), timeScale); } + /** Build an instance from an {@link Instant instant} in utc time scale. + * @param instant instant in the time scale + * @since 12.1 + */ + @DefaultDataContext + public AbsoluteDate(final Instant instant) { + this(instant, TimeScalesFactory.getUTC()); + } + + /** Build an instance from an {@link Instant instant} in the {@link UTCScale time scale}. + * @param instant instant in the time scale + * @param utcScale utc time scale + * @since 12.1 + */ + public AbsoluteDate(final Instant instant, final UTCScale utcScale) { + this(new DateComponents(DateComponents.JAVA_EPOCH, + (int) (instant.getEpochSecond() / 86400l)), + instantToTimeComponents(instant), + utcScale); + } + /** Build an instance from an elapsed duration since to another instant. *

          It is important to note that the elapsed duration is not * the difference between two readings on a time scale. As an example, @@ -468,6 +494,41 @@ public AbsoluteDate(final AbsoluteDate since, final double elapsedDuration) { } } + /** Build an instance from an elapsed duration since to another instant. + *

          It is important to note that the elapsed duration is not + * the difference between two readings on a time scale. As an example, + * the duration between the two instants leading to the readings + * 2005-12-31T23:59:59 and 2006-01-01T00:00:00 in the {@link UTCScale UTC} + * time scale is not 1 second, but a stop watch would have measured + * an elapsed duration of 2 seconds between these two instances because a leap + * second was introduced at the end of 2005 in this time scale.

          + *

          This constructor is the reverse of the {@link #durationFrom(AbsoluteDate, TimeUnit)} + * method.

          + * @param since start instant of the measured duration + * @param elapsedDuration physically elapsed duration from the since + * instant, as measured in a regular time scale + * @param timeUnit {@link TimeUnit} of the elapsedDuration + * @see #durationFrom(AbsoluteDate, TimeUnit) + * @since 12.1 + */ + public AbsoluteDate(final AbsoluteDate since, final long elapsedDuration, final TimeUnit timeUnit) { + final long elapsedDurationNanoseconds = TimeUnit.NANOSECONDS.convert(elapsedDuration, timeUnit); + final long deltaEpoch = elapsedDurationNanoseconds / TimeUnit.SECONDS.toNanos(1); + final double deltaOffset = (elapsedDurationNanoseconds - (deltaEpoch * TimeUnit.SECONDS.toNanos(1))) / (double) TimeUnit.SECONDS.toNanos(1); + final double newOffset = since.offset + deltaOffset; + if (newOffset >= 1.0) { + // newOffset is in [1.0, 2.0] + epoch = since.epoch + deltaEpoch + 1L; + offset = newOffset - 1.0; + } else if (newOffset < 0) { + epoch = since.epoch + deltaEpoch - 1L; + offset = 1.0 + newOffset; + } else { + epoch = since.epoch + deltaEpoch; + offset = newOffset; + } + } + /** Build an instance from an apparent clock offset with respect to another * instant in the perspective of a specific {@link TimeScale time scale}. *

          It is important to note that the apparent clock offset is the @@ -519,7 +580,7 @@ private static TimeComponents millisToTimeComponents(final int millisInDay) { * @return time components */ private static TimeComponents instantToTimeComponents(final Instant instant) { - final int secInDay = (int) (instant.getEpochSecond() % 86400l); + final int secInDay = (int) (instant.getEpochSecond() % 86400L); return new TimeComponents(secInDay, 1.0e-9 * instant.getNano()); } @@ -622,51 +683,11 @@ public static AbsoluteDate parseCCSDSUnsegmentedTimeCode( final byte[] timeField, final AbsoluteDate agencyDefinedEpoch, final AbsoluteDate ccsdsEpoch) { - - // time code identification and reference epoch - final AbsoluteDate epoch; - switch (preambleField1 & 0x70) { - case 0x10: - // the reference epoch is CCSDS epoch 1958-01-01T00:00:00 TAI - epoch = ccsdsEpoch; - break; - case 0x20: - // the reference epoch is agency defined - if (agencyDefinedEpoch == null) { - throw new OrekitException(OrekitMessages.CCSDS_DATE_MISSING_AGENCY_EPOCH); - } - epoch = agencyDefinedEpoch; - break; - default : - throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD, - formatByte(preambleField1)); - } - - // time field lengths - int coarseTimeLength = 1 + ((preambleField1 & 0x0C) >>> 2); - int fineTimeLength = preambleField1 & 0x03; - - if ((preambleField1 & 0x80) != 0x0) { - // there is an additional octet in preamble field - coarseTimeLength += (preambleField2 & 0x60) >>> 5; - fineTimeLength += (preambleField2 & 0x1C) >>> 2; - } - - if (timeField.length != coarseTimeLength + fineTimeLength) { - throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_LENGTH_TIME_FIELD, - timeField.length, coarseTimeLength + fineTimeLength); - } - - double seconds = 0; - for (int i = 0; i < coarseTimeLength; ++i) { - seconds = seconds * 256 + toUnsigned(timeField[i]); - } - double subseconds = 0; - for (int i = timeField.length - 1; i >= coarseTimeLength; --i) { - subseconds = (subseconds + toUnsigned(timeField[i])) / 256; - } - - return new AbsoluteDate(epoch, seconds).shiftedBy(subseconds); + final CcsdsUnsegmentedTimeCode timeCode = + new CcsdsUnsegmentedTimeCode<>(preambleField1, preambleField2, timeField, + agencyDefinedEpoch, ccsdsEpoch); + return new AbsoluteDate(timeCode.getEpoch(), timeCode.getSeconds()). + shiftedBy(timeCode.getSubSecond()); } @@ -709,68 +730,15 @@ public static AbsoluteDate parseCCSDSDaySegmentedTimeCode(final byte preambleFie * @return an instance corresponding to the specified date * @since 10.1 */ - public static AbsoluteDate parseCCSDSDaySegmentedTimeCode( - final byte preambleField, - final byte[] timeField, - final DateComponents agencyDefinedEpoch, - final TimeScale utc) { - - // time code identification - if ((preambleField & 0xF0) != 0x40) { - throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD, - formatByte(preambleField)); - } - - // reference epoch - final DateComponents epoch; - if ((preambleField & 0x08) == 0x00) { - // the reference epoch is CCSDS epoch 1958-01-01T00:00:00 TAI - epoch = DateComponents.CCSDS_EPOCH; - } else { - // the reference epoch is agency defined - if (agencyDefinedEpoch == null) { - throw new OrekitException(OrekitMessages.CCSDS_DATE_MISSING_AGENCY_EPOCH); - } - epoch = agencyDefinedEpoch; - } - - // time field lengths - final int daySegmentLength = ((preambleField & 0x04) == 0x0) ? 2 : 3; - final int subMillisecondLength = (preambleField & 0x03) << 1; - if (subMillisecondLength == 6) { - throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD, - formatByte(preambleField)); - } - if (timeField.length != daySegmentLength + 4 + subMillisecondLength) { - throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_LENGTH_TIME_FIELD, - timeField.length, daySegmentLength + 4 + subMillisecondLength); - } - - - int i = 0; - int day = 0; - while (i < daySegmentLength) { - day = day * 256 + toUnsigned(timeField[i++]); - } - - long milliInDay = 0l; - while (i < daySegmentLength + 4) { - milliInDay = milliInDay * 256 + toUnsigned(timeField[i++]); - } - final int milli = (int) (milliInDay % 1000l); - final int seconds = (int) ((milliInDay - milli) / 1000l); - - double subMilli = 0; - double divisor = 1; - while (i < timeField.length) { - subMilli = subMilli * 256 + toUnsigned(timeField[i++]); - divisor *= 1000; - } - - final DateComponents date = new DateComponents(epoch, day); - final TimeComponents time = new TimeComponents(seconds); - return new AbsoluteDate(date, time, utc).shiftedBy(milli * 1.0e-3 + subMilli / divisor); + public static AbsoluteDate parseCCSDSDaySegmentedTimeCode(final byte preambleField, + final byte[] timeField, + final DateComponents agencyDefinedEpoch, + final TimeScale utc) { + final CcsdsSegmentedTimeCode timeCode = new CcsdsSegmentedTimeCode(preambleField, timeField, + agencyDefinedEpoch); + return new AbsoluteDate(timeCode.getDate(), timeCode.getTime(), utc). + shiftedBy(timeCode.getSubSecond()); } /** Build an instance from a CCSDS Calendar Segmented Time Code (CCS). @@ -805,84 +773,54 @@ public static AbsoluteDate parseCCSDSCalendarSegmentedTimeCode(final byte preamb * @return an instance corresponding to the specified date * @since 10.1 */ - public static AbsoluteDate parseCCSDSCalendarSegmentedTimeCode( - final byte preambleField, - final byte[] timeField, - final TimeScale utc) { - - // time code identification - if ((preambleField & 0xF0) != 0x50) { - throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD, - formatByte(preambleField)); - } - - // time field length - final int length = 7 + (preambleField & 0x07); - if (length == 14) { - throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD, - formatByte(preambleField)); - } - if (timeField.length != length) { - throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_LENGTH_TIME_FIELD, - timeField.length, length); - } - - // date part in the first four bytes - final DateComponents date; - if ((preambleField & 0x08) == 0x00) { - // month of year and day of month variation - date = new DateComponents(toUnsigned(timeField[0]) * 256 + toUnsigned(timeField[1]), - toUnsigned(timeField[2]), - toUnsigned(timeField[3])); - } else { - // day of year variation - date = new DateComponents(toUnsigned(timeField[0]) * 256 + toUnsigned(timeField[1]), - toUnsigned(timeField[2]) * 256 + toUnsigned(timeField[3])); - } - - // time part from bytes 5 to last (between 7 and 13 depending on precision) - final TimeComponents time = new TimeComponents(toUnsigned(timeField[4]), - toUnsigned(timeField[5]), - toUnsigned(timeField[6])); - double subSecond = 0; - double divisor = 1; - for (int i = 7; i < length; ++i) { - subSecond = subSecond * 100 + toUnsigned(timeField[i]); - divisor *= 100; - } - - return new AbsoluteDate(date, time, utc).shiftedBy(subSecond / divisor); - - } - - /** Decode a signed byte as an unsigned int value. - * @param b byte to decode - * @return an unsigned int value - */ - private static int toUnsigned(final byte b) { - final int i = (int) b; - return (i < 0) ? 256 + i : i; + public static AbsoluteDate parseCCSDSCalendarSegmentedTimeCode(final byte preambleField, + final byte[] timeField, + final TimeScale utc) { + final CcsdsSegmentedTimeCode timeCode = new CcsdsSegmentedTimeCode(preambleField, timeField); + return new AbsoluteDate(timeCode.getDate(), timeCode.getTime(), utc). + shiftedBy(timeCode.getSubSecond()); } - /** Format a byte as an hex string for error messages. - * @param data byte to format - * @return a formatted string + /** Build an instance corresponding to a Julian Day date. + * @param jd Julian day + * @param secondsSinceNoon seconds in the Julian day + * (BEWARE, Julian days start at noon, so 0.0 is noon) + * @param timeScale time scale in which the seconds in day are defined + * @return a new instant */ - private static String formatByte(final byte data) { - return "0x" + Integer.toHexString(data).toUpperCase(); + public static AbsoluteDate createJDDate(final int jd, final double secondsSinceNoon, + final TimeScale timeScale) { + return new AbsoluteDate(new DateComponents(DateComponents.JULIAN_EPOCH, jd), + TimeComponents.H12, timeScale).shiftedBy(secondsSinceNoon); } /** Build an instance corresponding to a Julian Day date. + *

          + * This function should be preferred to {@link #createMJDDate(int, double, TimeScale)} when the target time scale + * has a non-constant offset with respect to TAI. + *

          + * The idea is to introduce a pivot time scale that is close to the target time scale but has a constant bias with TAI. + *

          + * For example, to get a date from an MJD in TDB time scale, it's advised to use the TT time scale + * as a pivot scale. TT is very close to TDB and has constant offset to TAI. * @param jd Julian day * @param secondsSinceNoon seconds in the Julian day * (BEWARE, Julian days start at noon, so 0.0 is noon) - * @param timeScale time scale in which the seconds in day are defined + * @param timeScale timescale in which the seconds in day are defined + * @param pivotTimeScale pivot timescale used as intermediate timescale * @return a new instant */ public static AbsoluteDate createJDDate(final int jd, final double secondsSinceNoon, - final TimeScale timeScale) { - return new AbsoluteDate(new DateComponents(DateComponents.JULIAN_EPOCH, jd), - TimeComponents.H12, timeScale).shiftedBy(secondsSinceNoon); + final TimeScale timeScale, + final TimeScale pivotTimeScale) { + // Get the date in pivot timescale + final AbsoluteDate dateInPivotTimeScale = createJDDate(jd, secondsSinceNoon, pivotTimeScale); + + // Compare offsets to TAI of the two time scales + final double offsetFromTAI = timeScale.offsetFromTAI(dateInPivotTimeScale) - pivotTimeScale.offsetFromTAI(dateInPivotTimeScale); + + // Return date in desired timescale + return dateInPivotTimeScale.shiftedBy(-offsetFromTAI); } /** Build an instance corresponding to a Modified Julian Day date. @@ -920,7 +858,6 @@ public static AbsoluteDate createMJDDate(final int mjd, final double secondsInDa } - /** Build an instance corresponding to a Julian Epoch (JE). *

          According to Lieske paper: @@ -986,6 +923,19 @@ public AbsoluteDate shiftedBy(final double dt) { return new AbsoluteDate(this, dt); } + /** Get a time-shifted date. + *

          + * Calling this method is equivalent to call new AbsoluteDate(this, shift, timeUnit). + *

          + * @param dt time shift in time units + * @param timeUnit {@link TimeUnit} of the shift + * @return a new date, shifted with respect to instance (which is immutable) + * @since 12.1 + */ + public AbsoluteDate shiftedBy(final long dt, final TimeUnit timeUnit) { + return new AbsoluteDate(this, dt, timeUnit); + } + /** Compute the physically elapsed duration between two instants. *

          The returned duration is the number of seconds physically * elapsed between the two instants, measured in a regular time @@ -1009,6 +959,31 @@ public double durationFrom(final AbsoluteDate instant) { return (epoch - instant.epoch) + (offset - instant.offset); } + /** Compute the physically elapsed duration between two instants. + *

          The returned duration is the duration physically + * elapsed between the two instants, using the given time unit and rounded to the nearest integer, measured in a regular time + * scale with respect to surface of the Earth (i.e either the {@link + * TAIScale TAI scale}, the {@link TTScale TT scale} or the {@link + * GPSScale GPS scale}). It is the only method that gives a + * duration with a physical meaning.

          + *

          This method is the reverse of the {@link #AbsoluteDate(AbsoluteDate, + * long, TimeUnit)} constructor.

          + * @param instant instant to subtract from the instance + * @param timeUnit {@link TimeUnit} precision for the offset + * @return offset in the given timeunit between the two instants (positive + * if the instance is posterior to the argument), rounded to the nearest integer {@link TimeUnit} + * @see #AbsoluteDate(AbsoluteDate, long, TimeUnit) + * @since 12.1 + */ + public long durationFrom(final AbsoluteDate instant, final TimeUnit timeUnit) { + final long deltaEpoch = timeUnit.convert(epoch - instant.epoch, TimeUnit.SECONDS); + + final long multiplier = timeUnit.convert(1, TimeUnit.SECONDS); + final long deltaOffset = FastMath.round((offset - instant.offset) * multiplier); + + return deltaEpoch + deltaOffset; + } + /** Compute the apparent clock offset between two instant in the * perspective of a specific {@link TimeScale time scale}. *

          The offset is the number of seconds counted in the given @@ -1065,6 +1040,36 @@ public Date toDate(final TimeScale timeScale) { return new Date(FastMath.round((time + 10957.5 * 86400.0) * 1000)); } + /** + * Convert the instance to a Java {@link java.time.Instant Instant}. + * Nanosecond precision is preserved during this conversion + * + * @return a {@link java.time.Instant Instant} instance representing the location + * of the instant in the utc time scale + * @since 12.1 + */ + @DefaultDataContext + public Instant toInstant() { + return toInstant(TimeScalesFactory.getTimeScales()); + } + + /** + * Convert the instance to a Java {@link java.time.Instant Instant}. + * Nanosecond precision is preserved during this conversion + * + * @param timeScales the timescales to use + * @return a {@link java.time.Instant Instant} instance representing the location + * of the instant in the utc time scale + * @since 12.1 + */ + public Instant toInstant(final TimeScales timeScales) { + final UTCScale utc = timeScales.getUTC(); + final String stringWithoutUtcOffset = toStringWithoutUtcOffset(utc, 9); + + final LocalDateTime localDateTime = LocalDateTime.parse(stringWithoutUtcOffset, DateTimeFormatter.ISO_LOCAL_DATE_TIME); + return localDateTime.toInstant(ZoneOffset.UTC); + } + /** Split the instance into date/time components. * @param timeScale time scale to use * @return date/time components @@ -1089,16 +1094,16 @@ public DateTimeComponents getComponents(final TimeScale timeScale) { // split date and time final long carry = (long) FastMath.floor(sumAndResidual.getSum()); double offset2000B = (sumAndResidual.getSum() - carry) + sumAndResidual.getResidual(); - long offset2000A = epoch + carry + 43200l; + long offset2000A = epoch + carry + 43200L; if (offset2000B < 0) { offset2000A -= 1; offset2000B += 1; } - long time = offset2000A % 86400l; - if (time < 0l) { - time += 86400l; + long time = offset2000A % 86400L; + if (time < 0L) { + time += 86400L; } - final int date = (int) ((offset2000A - time) / 86400l); + final int date = (int) ((offset2000A - time) / 86400L); // extract calendar elements final DateComponents dateComponents = new DateComponents(DateComponents.J2000_EPOCH, date); diff --git a/src/main/java/org/orekit/time/AbstractCcsdsTimeCode.java b/src/main/java/org/orekit/time/AbstractCcsdsTimeCode.java new file mode 100644 index 0000000000..3a38252220 --- /dev/null +++ b/src/main/java/org/orekit/time/AbstractCcsdsTimeCode.java @@ -0,0 +1,44 @@ +/* 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.time; + +/** Abstract base class for CCSDS segmented and unsegmented time code. + * @author Luc Maisonobe + * @since 12.1 + * @see AbsoluteDate + * @see FieldAbsoluteDate + */ +abstract class AbstractCcsdsTimeCode { + + /** Decode a signed byte as an unsigned int value. + * @param b byte to decode + * @return an unsigned int value + */ + protected int toUnsigned(final byte b) { + final int i = (int) b; + return (i < 0) ? 256 + i : i; + } + + /** Format a byte as an hex string for error messages. + * @param data byte to format + * @return a formatted string + */ + protected String formatByte(final byte data) { + return "0x" + Integer.toHexString(data).toUpperCase(); + } + +} diff --git a/src/main/java/org/orekit/time/AggregatedClockModel.java b/src/main/java/org/orekit/time/AggregatedClockModel.java new file mode 100644 index 0000000000..15dd119045 --- /dev/null +++ b/src/main/java/org/orekit/time/AggregatedClockModel.java @@ -0,0 +1,85 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.time; + +import org.hipparchus.CalculusFieldElement; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.utils.TimeSpanMap; + +/** Offset clock model aggregating several other clock models. + * @author Luc Maisonobe + * @since 12.1 + */ +public class AggregatedClockModel implements ClockModel { + + /** Underlying clock models. */ + private final TimeSpanMap models; + + /** Simple constructor. + * @param models underlying clock models + */ + public AggregatedClockModel(final TimeSpanMap models) { + this.models = models; + } + + /** Get the underlying models. + * @return underlying models + */ + public TimeSpanMap getModels() { + return models; + } + + /** {@inheritDoc} */ + @Override + public AbsoluteDate getValidityStart() { + return models.getFirstNonNullSpan().getStart(); + } + + /** {@inheritDoc} */ + @Override + public AbsoluteDate getValidityEnd() { + return models.getLastNonNullSpan().getEnd(); + } + + /** {@inheritDoc} */ + @Override + public ClockOffset getOffset(final AbsoluteDate date) { + return getModel(date).getOffset(date); + } + + /** {@inheritDoc} */ + @Override + public > FieldClockOffset getOffset(final FieldAbsoluteDate date) { + return getModel(date.toAbsoluteDate()).getOffset(date); + } + + /** Get the model valid at specified date. + * @param date date for which model is requested + * @return clock model valid at date + */ + private ClockModel getModel(final AbsoluteDate date) { + final ClockModel clockModel = models.get(date); + if (clockModel == null) { + // this may happen if map is limited or not contiguous + // typically for models retrieved from SP3Ephemeris + throw new OrekitException(OrekitMessages.NO_DATA_GENERATED, date); + } + return clockModel; + } + +} diff --git a/src/main/java/org/orekit/time/BDTScale.java b/src/main/java/org/orekit/time/BDTScale.java index dd6fa80b66..fc7aee1e77 100644 --- a/src/main/java/org/orekit/time/BDTScale.java +++ b/src/main/java/org/orekit/time/BDTScale.java @@ -16,54 +16,21 @@ */ package org.orekit.time; -import org.hipparchus.CalculusFieldElement; - - /** Beidou system time scale. *

          By convention, BDT = UTC on January 1st 2006.

          *

          This is intended to be accessed thanks to {@link TimeScales}, * so there is no public constructor.

          * @see AbsoluteDate */ -public class BDTScale implements TimeScale { +public class BDTScale extends ConstantOffsetTimeScale { /** Serializable UID. */ - private static final long serialVersionUID = 20180323L; - - /** Offset from TAI. */ - private static final double OFFSET = -33; + private static final long serialVersionUID = 20240321L; /** Package private constructor for the factory. */ BDTScale() { - } - - /** {@inheritDoc} */ - @Override - public double offsetFromTAI(final AbsoluteDate date) { - return OFFSET; - } - - /** {@inheritDoc} */ - @Override - public > T offsetFromTAI(final FieldAbsoluteDate date) { - return date.getField().getZero().add(OFFSET); - } - - /** {@inheritDoc} */ - @Override - public double offsetToTAI(final DateComponents date, final TimeComponents time) { - return -OFFSET; - } - - /** {@inheritDoc} */ - public String getName() { - return "BDT"; - } - - /** {@inheritDoc} */ - public String toString() { - return getName(); + super("BDT", -33); } } diff --git a/src/main/java/org/orekit/time/CcsdsSegmentedTimeCode.java b/src/main/java/org/orekit/time/CcsdsSegmentedTimeCode.java new file mode 100644 index 0000000000..1ca55cd4c7 --- /dev/null +++ b/src/main/java/org/orekit/time/CcsdsSegmentedTimeCode.java @@ -0,0 +1,189 @@ +/* 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.time; + +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; + +/** This class represents a CCSDS segmented time code. + * @author Luc Maisonobe + * @since 12.1 + * @see AbsoluteDate + * @see FieldAbsoluteDate + */ +class CcsdsSegmentedTimeCode extends AbstractCcsdsTimeCode { + + /** Date part. */ + private final DateComponents date; + + /** Time part (down to second only). */ + private final TimeComponents time; + + /** Sub-second part. */ + private final double subSecond; + + /** Create an instance CCSDS Day Segmented Time Code (CDS). + *

          + * CCSDS Day Segmented Time Code is defined in the blue book: + * CCSDS Time Code Format (CCSDS 301.0-B-4) published in November 2010 + *

          + * @param preambleField field specifying the format, often not transmitted in + * data interfaces, as it is constant for a given data interface + * @param timeField byte array containing the time code + * @param agencyDefinedEpoch reference epoch, ignored if the preamble field + * specifies the {@link DateComponents#CCSDS_EPOCH CCSDS reference epoch} is used + * (and hence may be null in this case) + */ + CcsdsSegmentedTimeCode(final byte preambleField, final byte[] timeField, + final DateComponents agencyDefinedEpoch) { + + // time code identification + if ((preambleField & 0xF0) != 0x40) { + throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD, + formatByte(preambleField)); + } + + // reference epoch + final DateComponents epoch; + if ((preambleField & 0x08) == 0x00) { + // the reference epoch is CCSDS epoch 1958-01-01T00:00:00 TAI + epoch = DateComponents.CCSDS_EPOCH; + } else { + // the reference epoch is agency defined + if (agencyDefinedEpoch == null) { + throw new OrekitException(OrekitMessages.CCSDS_DATE_MISSING_AGENCY_EPOCH); + } + epoch = agencyDefinedEpoch; + } + + // time field lengths + final int daySegmentLength = ((preambleField & 0x04) == 0x0) ? 2 : 3; + final int subMillisecondLength = (preambleField & 0x03) << 1; + if (subMillisecondLength == 6) { + throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD, + formatByte(preambleField)); + } + if (timeField.length != daySegmentLength + 4 + subMillisecondLength) { + throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_LENGTH_TIME_FIELD, + timeField.length, daySegmentLength + 4 + subMillisecondLength); + } + + + int i = 0; + int day = 0; + while (i < daySegmentLength) { + day = day * 256 + toUnsigned(timeField[i++]); + } + + long milliInDay = 0L; + while (i < daySegmentLength + 4) { + milliInDay = milliInDay * 256 + toUnsigned(timeField[i++]); + } + final int milli = (int) (milliInDay % 1000L); + final int seconds = (int) ((milliInDay - milli) / 1000L); + + double subMilli = 0; + double divisor = 1; + while (i < timeField.length) { + subMilli = subMilli * 256 + toUnsigned(timeField[i++]); + divisor *= 1000; + } + + this.date = new DateComponents(epoch, day); + this.time = new TimeComponents(seconds); + this.subSecond = milli * 1.0e-3 + subMilli / divisor; + + } + + /** Build an instance from a CCSDS Calendar Segmented Time Code (CCS). + *

          + * CCSDS Calendar Segmented Time Code is defined in the blue book: + * CCSDS Time Code Format (CCSDS 301.0-B-4) published in November 2010 + *

          + * @param preambleField field specifying the format, often not transmitted in + * data interfaces, as it is constant for a given data interface + * @param timeField byte array containing the time code + */ + CcsdsSegmentedTimeCode(final byte preambleField, final byte[] timeField) { + + // time code identification + if ((preambleField & 0xF0) != 0x50) { + throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD, + formatByte(preambleField)); + } + + // time field length + final int length = 7 + (preambleField & 0x07); + if (length == 14) { + throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD, + formatByte(preambleField)); + } + if (timeField.length != length) { + throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_LENGTH_TIME_FIELD, + timeField.length, length); + } + + // date part in the first four bytes + if ((preambleField & 0x08) == 0x00) { + // month of year and day of month variation + this.date = new DateComponents(toUnsigned(timeField[0]) * 256 + toUnsigned(timeField[1]), + toUnsigned(timeField[2]), + toUnsigned(timeField[3])); + } else { + // day of year variation + this.date = new DateComponents(toUnsigned(timeField[0]) * 256 + toUnsigned(timeField[1]), + toUnsigned(timeField[2]) * 256 + toUnsigned(timeField[3])); + } + + // time part from bytes 5 to last (between 7 and 13 depending on precision) + this.time = new TimeComponents(toUnsigned(timeField[4]), + toUnsigned(timeField[5]), + toUnsigned(timeField[6])); + + double sub = 0; + double divisor = 1; + for (int i = 7; i < length; ++i) { + sub = sub * 100 + toUnsigned(timeField[i]); + divisor *= 100; + } + + this.subSecond = sub / divisor; + + } + + /** Get the date part. + * @return date part + */ + public DateComponents getDate() { + return date; + } + + /** Get the time part. + * @return time part + */ + public TimeComponents getTime() { + return time; + } + + /** Get the sub-second part. + * @return sub-second part + */ + public double getSubSecond() { + return subSecond; + } + +} diff --git a/src/main/java/org/orekit/time/CcsdsUnsegmentedTimeCode.java b/src/main/java/org/orekit/time/CcsdsUnsegmentedTimeCode.java new file mode 100644 index 0000000000..1338dff9c0 --- /dev/null +++ b/src/main/java/org/orekit/time/CcsdsUnsegmentedTimeCode.java @@ -0,0 +1,140 @@ +/* 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.time; + +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; + +/** This class represents a CCSDS unsegmented time code. + * @param type of the date + * @author Luc Maisonobe + * @since 12.1 + * @see AbsoluteDate + * @see FieldAbsoluteDate + */ +class CcsdsUnsegmentedTimeCode extends AbstractCcsdsTimeCode { + + /** Epoch part. */ + private final T epoch; + + /** Seconds part. */ + private final long seconds; + + /** Sub-second part. */ + private final double subSecond; + + /** Create an instance CCSDS Day Unegmented Time Code (CDS). + *

          + * CCSDS Unsegmented Time Code is defined in the blue book: CCSDS Time Code Format + * (CCSDS 301.0-B-4) published in November 2010 + *

          + *

          + * If the date to be parsed is formatted using version 3 of the standard (CCSDS + * 301.0-B-3 published in 2002) or if the extension of the preamble field introduced + * in version 4 of the standard is not used, then the {@code preambleField2} parameter + * can be set to 0. + *

          + * @param preambleField1 first byte of the field specifying the format, often not + * transmitted in data interfaces, as it is constant for a + * given data interface + * @param preambleField2 second byte of the field specifying the format (added in + * revision 4 of the CCSDS standard in 2010), often not + * transmitted in data interfaces, as it is constant for a + * given data interface (value ignored if presence not + * signaled in {@code preambleField1}) + * @param timeField byte array containing the time code + * @param agencyDefinedEpoch reference epoch, ignored if the preamble field specifies + * the {@link DateComponents#CCSDS_EPOCH CCSDS reference epoch} is used + * (and hence may be null in this case) + * @param ccsdsEpoch reference epoch, ignored if the preamble field specifies + * the agency epoch is used. + */ + CcsdsUnsegmentedTimeCode(final byte preambleField1, + final byte preambleField2, + final byte[] timeField, + final T agencyDefinedEpoch, + final T ccsdsEpoch) { + + // time code identification and reference epoch + switch (preambleField1 & 0x70) { + case 0x10: + // the reference epoch is CCSDS epoch 1958-01-01T00:00:00 TAI + epoch = ccsdsEpoch; + break; + case 0x20: + // the reference epoch is agency defined + if (agencyDefinedEpoch == null) { + throw new OrekitException(OrekitMessages.CCSDS_DATE_MISSING_AGENCY_EPOCH); + } + epoch = agencyDefinedEpoch; + break; + default : + throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD, + formatByte(preambleField1)); + } + + // time field lengths + int coarseTimeLength = 1 + ((preambleField1 & 0x0C) >>> 2); + int fineTimeLength = preambleField1 & 0x03; + + if ((preambleField1 & 0x80) != 0x0) { + // there is an additional octet in preamble field + coarseTimeLength += (preambleField2 & 0x60) >>> 5; + fineTimeLength += (preambleField2 & 0x1C) >>> 2; + } + + if (timeField.length != coarseTimeLength + fineTimeLength) { + throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_LENGTH_TIME_FIELD, + timeField.length, coarseTimeLength + fineTimeLength); + } + + long s = 0L; + for (int i = 0; i < coarseTimeLength; ++i) { + s = s * 256 + toUnsigned(timeField[i]); + } + seconds = s; + + double sub = 0; + for (int i = timeField.length - 1; i >= coarseTimeLength; --i) { + sub = (sub + toUnsigned(timeField[i])) / 256; + } + subSecond = sub; + + } + + /** Get the epoch part. + * @return epoch part + */ + public T getEpoch() { + return epoch; + } + + /** Get the seconds part. + * @return seconds part + */ + public long getSeconds() { + return seconds; + } + + /** Get the sub-second part. + * @return sub-second part + */ + public double getSubSecond() { + return subSecond; + } + +} diff --git a/src/main/java/org/orekit/time/ClockModel.java b/src/main/java/org/orekit/time/ClockModel.java new file mode 100644 index 0000000000..af59e0b73b --- /dev/null +++ b/src/main/java/org/orekit/time/ClockModel.java @@ -0,0 +1,50 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.time; + +import org.hipparchus.CalculusFieldElement; + +/** Offset clock model. + * @author Luc Maisonobe + * @since 12.1 + */ +public interface ClockModel { + + /** Get validity start. + * @return model validity start + */ + AbsoluteDate getValidityStart(); + + /** Get validity end. + * @return model validity end + */ + AbsoluteDate getValidityEnd(); + + /** Get the clock offset at date. + * @param date date at which offset is requested + * @return clock offset at specified date + */ + ClockOffset getOffset(AbsoluteDate date); + + /** Get the clock offset at date. + * @param type of the field elements + * @param date date at which offset is requested + * @return clock offset at specified date + */ + > FieldClockOffset getOffset(FieldAbsoluteDate date); + +} diff --git a/src/main/java/org/orekit/time/ClockOffset.java b/src/main/java/org/orekit/time/ClockOffset.java new file mode 100644 index 0000000000..2120050a38 --- /dev/null +++ b/src/main/java/org/orekit/time/ClockOffset.java @@ -0,0 +1,78 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.time; + +/** Container for time stamped clock offset. + * @author Luc Maisonobe + * @since 12.1 + */ +public class ClockOffset implements TimeStamped { + + /** Date. */ + private final AbsoluteDate date; + + /** Clock offset. */ + private final double offset; + + /** Clock rate. */ + private final double rate; + + /** Clock acceleration. */ + private final double acceleration; + + /** Simple constructor. + * @param date date + * @param offset clock offset + * @param rate clock rate (can be set to {@code Double.NaN} if unknown) + * @param acceleration clock acceleration (can be set to {@code Double.NaN} if unknown) + */ + public ClockOffset(final AbsoluteDate date, final double offset, + final double rate, final double acceleration) { + this.date = date; + this.offset = offset; + this.rate = rate; + this.acceleration = acceleration; + } + + /** {@inheritDoc} */ + @Override + public AbsoluteDate getDate() { + return date; + } + + /** Get offset. + * @return offset + */ + public double getOffset() { + return offset; + } + + /** Get rate. + * @return rate ({@code Double.NaN} if unknown) + */ + public double getRate() { + return rate; + } + + /** Get acceleration. + * @return acceleration ({@code Double.NaN} if unknown) + */ + public double getAcceleration() { + return acceleration; + } + +} diff --git a/src/main/java/org/orekit/time/ClockOffsetHermiteInterpolator.java b/src/main/java/org/orekit/time/ClockOffsetHermiteInterpolator.java new file mode 100644 index 0000000000..5e0d41d156 --- /dev/null +++ b/src/main/java/org/orekit/time/ClockOffsetHermiteInterpolator.java @@ -0,0 +1,101 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.time; + +import org.hipparchus.analysis.interpolation.HermiteInterpolator; + +import java.util.List; + +/**bHermite interpolator of time stamped clock offsets. + * @author Luc Maisonobe + * @see HermiteInterpolator + * @see TimeInterpolator + * @since 12.1 + */ +public class ClockOffsetHermiteInterpolator extends AbstractTimeInterpolator { + + /** + * Constructor with default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s). + *

          + * 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 the number of interpolation points or derivatives availability is not sufficient, + * the rate and acceleration of interpolated offset will be silently set to 0 (i.e. + * model will be constant or linear only). + *

          + * @param interpolationPoints number of interpolation points + */ + public ClockOffsetHermiteInterpolator(final int interpolationPoints) { + this(interpolationPoints, DEFAULT_EXTRAPOLATION_THRESHOLD_SEC); + } + + /** + * Constructor. + *

          + * 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 the number of interpolation points or derivatives availability is not sufficient, + * the rate and acceleration of interpolated offset will be silently set to 0 (i.e. + * model will be constant or linear only). + *

          + * @param interpolationPoints number of interpolation points + * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail + */ + public ClockOffsetHermiteInterpolator(final int interpolationPoints, final double extrapolationThreshold) { + super(interpolationPoints, extrapolationThreshold); + } + + /** {@inheritDoc} */ + @Override + protected ClockOffset interpolate(final InterpolationData interpolationData) { + final HermiteInterpolator interpolator = new HermiteInterpolator(); + + // Fill interpolator with sample + final AbsoluteDate interpolationDate = interpolationData.getInterpolationDate(); + final List neighborList = interpolationData.getNeighborList(); + for (ClockOffset value : neighborList) { + final double deltaT = value.getDate().durationFrom(interpolationDate); + final double[] offset = new double[] { value.getOffset() }; + if (Double.isNaN(value.getRate())) { + // no clock rate for this entry + interpolator.addSamplePoint(deltaT, offset); + } else { + // clock rate is available + final double[] rate = new double[] { value.getRate() }; + if (Double.isNaN(value.getAcceleration())) { + // no clock acceleration for this entry + interpolator.addSamplePoint(deltaT, offset, rate); + } else { + // clock acceleration is available + final double[] acceleration = new double[] { value.getAcceleration() }; + interpolator.addSamplePoint(deltaT, offset, rate, acceleration); + } + } + } + + final double[][] y = interpolator.derivatives(0, 2); + return new ClockOffset(interpolationDate, y[0][0], y[1][0], y[2][0]); + + } + +} diff --git a/src/main/java/org/orekit/time/ClockTimeScale.java b/src/main/java/org/orekit/time/ClockTimeScale.java new file mode 100644 index 0000000000..06b479f2cf --- /dev/null +++ b/src/main/java/org/orekit/time/ClockTimeScale.java @@ -0,0 +1,70 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.time; + +import org.hipparchus.CalculusFieldElement; + +/** Time scale with clock offset from another time scale. + * @author Luc Maisonobe + * @since 12.1 + */ +public class ClockTimeScale implements TimeScale { + + /** Serializable UID. */ + private static final long serialVersionUID = 20240321L; + + /** Name of the time scale. */ + private final String name; + + /** Reference time scale. */ + private final TimeScale reference; + + /** Clock offset model. */ + private final transient ClockModel clockModel; + + /** Simple constructor. + * @param name name of the time scale + * @param reference reference time scale + * @param clockModel clock offset model + */ + public ClockTimeScale(final String name, + final TimeScale reference, + final ClockModel clockModel) { + this.name = name; + this.reference = reference; + this.clockModel = clockModel; + } + + /** {@inheritDoc} */ + @Override + public double offsetFromTAI(final AbsoluteDate date) { + return reference.offsetFromTAI(date) + clockModel.getOffset(date).getOffset(); + } + + /** {@inheritDoc} */ + @Override + public > T offsetFromTAI(final FieldAbsoluteDate date) { + return reference.offsetFromTAI(date).add(clockModel.getOffset(date).getOffset()); + } + + /** {@inheritDoc} */ + @Override + public String getName() { + return name; + } + +} diff --git a/src/main/java/org/orekit/time/ConstantOffsetTimeScale.java b/src/main/java/org/orekit/time/ConstantOffsetTimeScale.java new file mode 100644 index 0000000000..2a3f1970cd --- /dev/null +++ b/src/main/java/org/orekit/time/ConstantOffsetTimeScale.java @@ -0,0 +1,73 @@ +/* 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.time; + +import org.hipparchus.CalculusFieldElement; + +/** Base class for time scales with constant offset with respecto to TAI. + * @author Luc Maisonobe + * @since 12.1 + */ +public class ConstantOffsetTimeScale implements TimeScale { + + /** Serializable UID. */ + private static final long serialVersionUID = 20240321L; + + /** Name of the time scale. */ + private final String name; + + /** Offset from TAI. */ + private final double offset; + + /** Simple constructor. + * @param name name of the time scale + * @param offset offset from TAI + */ + protected ConstantOffsetTimeScale(final String name, final double offset) { + this.name = name; + this.offset = offset; + } + + /** {@inheritDoc} */ + @Override + public double offsetFromTAI(final AbsoluteDate date) { + return offset; + } + + /** {@inheritDoc} */ + @Override + public > T offsetFromTAI(final FieldAbsoluteDate date) { + return date.getField().getZero().newInstance(offset); + } + + /** {@inheritDoc} */ + @Override + public double offsetToTAI(final DateComponents date, final TimeComponents time) { + return -offset; + } + + /** {@inheritDoc} */ + public String getName() { + return name; + } + + /** {@inheritDoc} */ + public String toString() { + return getName(); + } + +} diff --git a/src/main/java/org/orekit/time/DateTimeComponents.java b/src/main/java/org/orekit/time/DateTimeComponents.java index 830e39475a..9ebdd4868d 100644 --- a/src/main/java/org/orekit/time/DateTimeComponents.java +++ b/src/main/java/org/orekit/time/DateTimeComponents.java @@ -21,6 +21,7 @@ import java.text.DecimalFormatSymbols; import java.util.Locale; +import java.util.concurrent.TimeUnit; import org.hipparchus.util.FastMath; import org.orekit.utils.Constants; @@ -154,6 +155,41 @@ public DateTimeComponents(final DateTimeComponents reference, } + /** Build an instance from a seconds offset with respect to another one. + * @param reference reference date/time + * @param offset offset from the reference + * @param timeUnit the {@link TimeUnit} for the offset + * @see #offsetFrom(DateTimeComponents, TimeUnit) + * @since 12.1 + */ + public DateTimeComponents(final DateTimeComponents reference, + final long offset, final TimeUnit timeUnit) { + + // extract linear data from reference date/time + int day = reference.getDate().getJ2000Day(); + double seconds = reference.getTime().getSecondsInLocalDay(); + + // apply offset + long offsetInNanos = TimeUnit.NANOSECONDS.convert(offset, timeUnit); + final long daysInNanoseconds = TimeUnit.NANOSECONDS.convert((long) Constants.JULIAN_DAY, TimeUnit.SECONDS); + final int nanoDayShift = (int) FastMath.floorDiv(offsetInNanos, daysInNanoseconds); + offsetInNanos -= daysInNanoseconds * nanoDayShift; + + seconds += offsetInNanos / (double) TimeUnit.SECONDS.toNanos(1); + + // fix range + final int dayShift = (int) FastMath.floor(seconds / Constants.JULIAN_DAY); + seconds -= Constants.JULIAN_DAY * dayShift; + day += dayShift + nanoDayShift; + final TimeComponents tmpTime = new TimeComponents(seconds); + + // set up components + this.date = new DateComponents(day); + this.time = new TimeComponents(tmpTime.getHour(), tmpTime.getMinute(), tmpTime.getSecond(), + reference.getTime().getMinutesFromUTC()); + + } + /** Parse a string in ISO-8601 format to build a date/time. *

          The supported formats are all date formats supported by {@link DateComponents#parseDate(String)} * and all time formats supported by {@link TimeComponents#parseTime(String)} separated @@ -189,6 +225,24 @@ public double offsetFrom(final DateTimeComponents dateTime) { return Constants.JULIAN_DAY * dateOffset + timeOffset; } + /** Compute the seconds offset between two instances. + * @param dateTime dateTime to subtract from the instance + * @param timeUnit the desired {@link TimeUnit} + * @return offset in the given timeunit between the two instants (positive + * if the instance is posterior to the argument), rounded to the nearest integer {@link TimeUnit} + * @see #DateTimeComponents(DateTimeComponents, long, TimeUnit) + * @since 12.1 + */ + public long offsetFrom(final DateTimeComponents dateTime, final TimeUnit timeUnit) { + final int dateOffset = date.getJ2000Day() - dateTime.date.getJ2000Day(); + final double timeOffset = time.getSecondsInUTCDay() - dateTime.time.getSecondsInUTCDay(); + + final long multiplier = timeUnit.convert(1, TimeUnit.SECONDS); + + return timeUnit.convert(Math.round(Constants.JULIAN_DAY * dateOffset), TimeUnit.SECONDS) + + FastMath.round(timeOffset * multiplier); + } + /** Get the date component. * @return date component */ diff --git a/src/main/java/org/orekit/time/FieldAbsoluteDate.java b/src/main/java/org/orekit/time/FieldAbsoluteDate.java index b105370a56..e37c796c07 100644 --- a/src/main/java/org/orekit/time/FieldAbsoluteDate.java +++ b/src/main/java/org/orekit/time/FieldAbsoluteDate.java @@ -17,9 +17,13 @@ package org.orekit.time; import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; import java.util.Date; import java.util.TimeZone; +import java.util.concurrent.TimeUnit; import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; import org.hipparchus.FieldElement; @@ -31,8 +35,6 @@ import org.hipparchus.util.MathUtils.SumAndResidual; import org.orekit.annotation.DefaultDataContext; import org.orekit.data.DataContext; -import org.orekit.errors.OrekitException; -import org.orekit.errors.OrekitMessages; import org.orekit.utils.Constants; /** This class represents a specific instant in time. @@ -115,7 +117,7 @@ public class FieldAbsoluteDate> private final T offset; /** Field used by default.*/ - private Field field; + private final Field field; /** Build an instance from an AbsoluteDate. * @param field used by default @@ -227,15 +229,15 @@ public FieldAbsoluteDate(final Field field, final DateComponents date, final if (regularOffset.getReal() >= 0) { // regular case, the offset is between 0.0 and 1.0 offset = regularOffset; - epoch = 60l * ((date.getJ2000Day() * 24l + time.getHour()) * 60l + - time.getMinute() - time.getMinutesFromUTC() - 720l) + dl; + epoch = 60L * ((date.getJ2000Day() * 24L + time.getHour()) * 60L + + time.getMinute() - time.getMinutesFromUTC() - 720L) + dl; } else { // very rare case, the offset is just before a whole second // we will loose some bits of accuracy when adding 1 second // but this will ensure the offset remains in the [0.0; 1.0] interval offset = regularOffset.add(1.0); - epoch = 60l * ((date.getJ2000Day() * 24l + time.getHour()) * 60l + - time.getMinute() - time.getMinutesFromUTC() - 720l) + dl - 1; + epoch = 60L * ((date.getJ2000Day() * 24L + time.getHour()) * 60L + + time.getMinute() - time.getMinutesFromUTC() - 720L) + dl - 1; } this.field = field; @@ -327,8 +329,8 @@ public FieldAbsoluteDate(final Field field, final int year, final Month month */ public FieldAbsoluteDate(final Field field, final Date location, final TimeScale timeScale) { this(field, new DateComponents(DateComponents.JAVA_EPOCH, - (int) (location.getTime() / 86400000l)), - new TimeComponents(0.001 * (location.getTime() % 86400000l)), + (int) (location.getTime() / 86400000L)), + new TimeComponents(0.001 * (location.getTime() % 86400000L)), timeScale); } @@ -340,11 +342,34 @@ public FieldAbsoluteDate(final Field field, final Date location, final TimeSc */ public FieldAbsoluteDate(final Field field, final Instant instant, final TimeScale timeScale) { this(field, new DateComponents(DateComponents.JAVA_EPOCH, - (int) (instant.getEpochSecond() / 86400l)), + (int) (instant.getEpochSecond() / 86400L)), instantToTimeComponents(instant), timeScale); } + /** Build an instance from an {@link Instant instant} in utc time scale. + * @param field field utilized as default + * @param instant instant in the utc timescale + * @since 12.1 + */ + @DefaultDataContext + public FieldAbsoluteDate(final Field field, final Instant instant) { + this(field, instant, TimeScalesFactory.getUTC()); + } + + /** Build an instance from an {@link Instant instant} in the {@link UTCScale time scale}. + * @param field field utilized as default + * @param instant instant in the time scale + * @param utcScale utc time scale + * @since 12.1 + */ + public FieldAbsoluteDate(final Field field, final Instant instant, final UTCScale utcScale) { + this(field, new DateComponents(DateComponents.JAVA_EPOCH, + (int) (instant.getEpochSecond() / 86400l)), + instantToTimeComponents(instant), + utcScale); + } + /** Build an instance from an elapsed duration since to another instant. *

          It is important to note that the elapsed duration is not * the difference between two readings on a time scale. @@ -356,6 +381,19 @@ public FieldAbsoluteDate(final FieldAbsoluteDate since, final double elapsedD this(since.epoch, elapsedDuration, since.offset); } + /** Build an instance from an elapsed duration since to another instant. + *

          It is important to note that the elapsed duration is not + * the difference between two readings on a time scale. + * @param since start instant of the measured duration + * @param elapsedDuration physically elapsed duration from the since + * instant, as measured in a regular time scale + * @param timeUnit {@link TimeUnit} of the elapsed duration + * @since 12.1 + */ + public FieldAbsoluteDate(final FieldAbsoluteDate since, final long elapsedDuration, final TimeUnit timeUnit) { + this(since.epoch, elapsedDuration, timeUnit, since.offset); + } + /** Build an instance from an elapsed duration since to another instant. *

          It is important to note that the elapsed duration is not @@ -368,6 +406,37 @@ public FieldAbsoluteDate(final AbsoluteDate since, final T elapsedDuration) { this(since.getEpoch(), since.getOffset(), elapsedDuration); } + /** Build an instance from an elapsed duration since to another instant. + *

          It is important to note that the elapsed duration is not + * the difference between two readings on a time scale. + * @param since start instant of the measured duration + * @param elapsedDuration physically elapsed duration from the since + * instant, as measured in a regular time scale + * @param timeUnit {@link TimeUnit} of the elapsed duration + * @param field field utilized by default + * @since 12.1 + */ + public FieldAbsoluteDate(final AbsoluteDate since, final long elapsedDuration, final TimeUnit timeUnit, final Field field) { + this.field = field; + + final long elapsedDurationNanoseconds = TimeUnit.NANOSECONDS.convert(elapsedDuration, timeUnit); + final long deltaEpoch = elapsedDurationNanoseconds / TimeUnit.SECONDS.toNanos(1); + final double deltaOffset = (elapsedDurationNanoseconds - (deltaEpoch * TimeUnit.SECONDS.toNanos(1))) / (double) TimeUnit.SECONDS.toNanos(1); + final T newOffset = field.getZero().add(since.getOffset()).add(deltaOffset); + + if (newOffset.getReal() >= 1.0) { + // newOffset is in [1.0, 2.0] + this.epoch = since.getEpoch() + deltaEpoch + 1L; + this.offset = newOffset.subtract(1.0); + } else if (newOffset.getReal() < 0) { + this.epoch = since.getEpoch() + deltaEpoch - 1L; + this.offset = newOffset.add(1.0); + } else { + this.epoch = since.getEpoch() + deltaEpoch; + this.offset = newOffset; + } + } + /** Build an instance from an apparent clock offset with respect to another * instant in the perspective of a specific {@link TimeScale time scale}. *

          It is important to note that the apparent clock offset is the @@ -420,12 +489,40 @@ private FieldAbsoluteDate(final long epoch, final double tA, final T tB) { } } + /** Build an instance from mixed double and field raw components. + * @param epoch reference epoch in seconds from 2000-01-01T12:00:00 TAI + * @param tA numeric part of offset since reference epoch + * @param tATimeUnit {@link TimeUnit} for tA + * @param tB field part of offset since reference epoch + * @since 12.1 + */ + private FieldAbsoluteDate(final long epoch, final long tA, final TimeUnit tATimeUnit, final T tB) { + this.field = tB.getField(); + + final long elapsedDurationNanoseconds = TimeUnit.NANOSECONDS.convert(tA, tATimeUnit); + final long deltaEpoch = elapsedDurationNanoseconds / TimeUnit.SECONDS.toNanos(1); + final double deltaOffset = (elapsedDurationNanoseconds - (deltaEpoch * TimeUnit.SECONDS.toNanos(1))) / (double) TimeUnit.SECONDS.toNanos(1); + final T newOffset = field.getZero().add(tB).add(deltaOffset); + + if (newOffset.getReal() >= 1.0) { + // newOffset is in [1.0, 2.0] + this.epoch = epoch + deltaEpoch + 1L; + offset = newOffset.subtract(1.0); + } else if (newOffset.getReal() < 0) { + this.epoch = epoch + deltaEpoch - 1L; + offset = newOffset.add(1.0); + } else { + this.epoch = epoch + deltaEpoch; + offset = newOffset; + } + } + /** Extract time components from an instant within the day. * @param instant instant to extract the number of seconds within the day * @return time components */ private static TimeComponents instantToTimeComponents(final Instant instant) { - final int secInDay = (int) (instant.getEpochSecond() % 86400l); + final int secInDay = (int) (instant.getEpochSecond() % 86400L); return new TimeComponents(secInDay, 1.0e-9 * instant.getNano()); } @@ -505,58 +602,17 @@ public static > FieldAbsoluteDate parseCCSD * @return an instance corresponding to the specified date * @since 10.1 */ - public static > FieldAbsoluteDate parseCCSDSUnsegmentedTimeCode( - final Field field, + public static > FieldAbsoluteDate parseCCSDSUnsegmentedTimeCode(final Field field, final byte preambleField1, final byte preambleField2, final byte[] timeField, final FieldAbsoluteDate agencyDefinedEpoch, final FieldAbsoluteDate ccsdsEpoch) { - - // time code identification and reference epoch - final FieldAbsoluteDate epochF; - switch (preambleField1 & 0x70) { - case 0x10: - // the reference epoch is CCSDS epoch 1958-01-01T00:00:00 TAI - epochF = ccsdsEpoch; - break; - case 0x20: - // the reference epoch is agency defined - if (agencyDefinedEpoch == null) { - throw new OrekitException(OrekitMessages.CCSDS_DATE_MISSING_AGENCY_EPOCH); - } - epochF = agencyDefinedEpoch; - break; - default : - throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD, - formatByte(preambleField1)); - } - - // time field lengths - int coarseTimeLength = 1 + ((preambleField1 & 0x0C) >>> 2); - int fineTimeLength = preambleField1 & 0x03; - - if ((preambleField1 & 0x80) != 0x0) { - // there is an additional octet in preamble field - coarseTimeLength += (preambleField2 & 0x60) >>> 5; - fineTimeLength += (preambleField2 & 0x1C) >>> 2; - } - - if (timeField.length != coarseTimeLength + fineTimeLength) { - throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_LENGTH_TIME_FIELD, - timeField.length, coarseTimeLength + fineTimeLength); - } - - T seconds = field.getZero(); - for (int i = 0; i < coarseTimeLength; ++i) { - seconds = seconds.multiply(256).add(field.getZero().add(toUnsigned(timeField[i]))); - } - T subseconds = field.getZero(); - for (int i = timeField.length - 1; i >= coarseTimeLength; --i) { - subseconds = (subseconds.add(toUnsigned(timeField[i]))).divide(256); - } - return new FieldAbsoluteDate<>(epochF, seconds).shiftedBy(subseconds); - + final CcsdsUnsegmentedTimeCode> timeCode = + new CcsdsUnsegmentedTimeCode<>(preambleField1, preambleField2, timeField, + agencyDefinedEpoch, ccsdsEpoch); + return new FieldAbsoluteDate<>(timeCode.getEpoch(), timeCode.getSeconds()). + shiftedBy(timeCode.getSubSecond()); } /** Build an instance from a CCSDS Day Segmented Time Code (CDS). @@ -607,69 +663,16 @@ public static > FieldAbsoluteDate parseCCSD * @return an instance corresponding to the specified date * @since 10.1 */ - public static > FieldAbsoluteDate parseCCSDSDaySegmentedTimeCode( - final Field field, + public static > FieldAbsoluteDate parseCCSDSDaySegmentedTimeCode(final Field field, final byte preambleField, final byte[] timeField, final DateComponents agencyDefinedEpoch, final TimeScale utc) { - // time code identification - if ((preambleField & 0xF0) != 0x40) { - throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD, - formatByte(preambleField)); - } - - // reference epoch - final DateComponents epochDC; - if ((preambleField & 0x08) == 0x00) { - // the reference epoch is CCSDS epoch 1958-01-01T00:00:00 TAI - epochDC = DateComponents.CCSDS_EPOCH; - } else { - // the reference epoch is agency defined - if (agencyDefinedEpoch == null) { - throw new OrekitException(OrekitMessages.CCSDS_DATE_MISSING_AGENCY_EPOCH); - } - epochDC = agencyDefinedEpoch; - } - - // time field lengths - final int daySegmentLength = ((preambleField & 0x04) == 0x0) ? 2 : 3; - final int subMillisecondLength = (preambleField & 0x03) << 1; - if (subMillisecondLength == 6) { - throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD, - formatByte(preambleField)); - } - if (timeField.length != daySegmentLength + 4 + subMillisecondLength) { - throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_LENGTH_TIME_FIELD, - timeField.length, daySegmentLength + 4 + subMillisecondLength); - } - - - int i = 0; - int day = 0; - while (i < daySegmentLength) { - day = day * 256 + toUnsigned(timeField[i++]); - } - - long milliInDay = 0l; - while (i < daySegmentLength + 4) { - milliInDay = milliInDay * 256 + toUnsigned(timeField[i++]); - } - final int milli = (int) (milliInDay % 1000l); - final int seconds = (int) ((milliInDay - milli) / 1000l); - - double subMilli = 0; - double divisor = 1; - while (i < timeField.length) { - subMilli = subMilli * 256 + toUnsigned(timeField[i++]); - divisor *= 1000; - } - - final DateComponents date = new DateComponents(epochDC, day); - final TimeComponents time = new TimeComponents(seconds); - return new FieldAbsoluteDate<>(field, date, time, utc).shiftedBy(milli * 1.0e-3 + subMilli / divisor); - + final CcsdsSegmentedTimeCode timeCode = new CcsdsSegmentedTimeCode(preambleField, timeField, + agencyDefinedEpoch); + return new FieldAbsoluteDate<>(field, timeCode.getDate(), timeCode.getTime(), utc). + shiftedBy(timeCode.getSubSecond()); } /** Build an instance from a CCSDS Calendar Segmented Time Code (CCS). @@ -706,71 +709,12 @@ public FieldAbsoluteDate parseCCSDSCalendarSegmentedTimeCode(final byte pream * @return an instance corresponding to the specified date * @since 10.1 */ - public FieldAbsoluteDate parseCCSDSCalendarSegmentedTimeCode( - final byte preambleField, + public FieldAbsoluteDate parseCCSDSCalendarSegmentedTimeCode(final byte preambleField, final byte[] timeField, final TimeScale utc) { - - // time code identification - if ((preambleField & 0xF0) != 0x50) { - throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD, - formatByte(preambleField)); - } - - // time field length - final int length = 7 + (preambleField & 0x07); - if (length == 14) { - throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD, - formatByte(preambleField)); - } - if (timeField.length != length) { - throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_LENGTH_TIME_FIELD, - timeField.length, length); - } - - // date part in the first four bytes - final DateComponents date; - if ((preambleField & 0x08) == 0x00) { - // month of year and day of month variation - date = new DateComponents(toUnsigned(timeField[0]) * 256 + toUnsigned(timeField[1]), - toUnsigned(timeField[2]), - toUnsigned(timeField[3])); - } else { - // day of year variation - date = new DateComponents(toUnsigned(timeField[0]) * 256 + toUnsigned(timeField[1]), - toUnsigned(timeField[2]) * 256 + toUnsigned(timeField[3])); - } - - // time part from bytes 5 to last (between 7 and 13 depending on precision) - final TimeComponents time = new TimeComponents(toUnsigned(timeField[4]), - toUnsigned(timeField[5]), - toUnsigned(timeField[6])); - double subSecond = 0; - double divisor = 1; - for (int i = 7; i < length; ++i) { - subSecond = subSecond * 100 + toUnsigned(timeField[i]); - divisor *= 100; - } - - return new FieldAbsoluteDate<>(field, date, time, utc).shiftedBy(subSecond / divisor); - - } - - /** Decode a signed byte as an unsigned int value. - * @param b byte to decode - * @return an unsigned int value - */ - private static int toUnsigned(final byte b) { - final int i = (int) b; - return (i < 0) ? 256 + i : i; - } - - /** Format a byte as an hex string for error messages. - * @param data byte to format - * @return a formatted string - */ - private static String formatByte(final byte data) { - return "0x" + Integer.toHexString(data).toUpperCase(); + final CcsdsSegmentedTimeCode timeCode = new CcsdsSegmentedTimeCode(preambleField, timeField); + return new FieldAbsoluteDate<>(field, timeCode.getDate(), timeCode.getTime(), utc). + shiftedBy(timeCode.getSubSecond()); } /** Build an instance corresponding to a Julian Day date. @@ -787,6 +731,38 @@ public static > FieldAbsoluteDate createJDD TimeComponents.H12, timeScale).shiftedBy(secondsSinceNoon); } + /** Build an instance corresponding to a Julian Day date. + *

          + * This function should be preferred to {@link #createJDDate(int, CalculusFieldElement, TimeScale)} when the target time scale + * has a non-constant offset with respect to TAI. + *

          + * The idea is to introduce a pivot time scale that is close to the target time scale but has a constant bias with TAI. + *

          + * For example, to get a date from an MJD in TDB time scale, it's advised to use the TT time scale + * as a pivot scale. TT is very close to TDB and has constant offset to TAI. + *

          + * @param jd Julian day + * @param secondsSinceNoon seconds in the Julian day + * (BEWARE, Julian days start at noon, so 0.0 is noon) + * @param timeScale time scale in which the seconds in day are defined + * @param pivotTimeScale pivot timescale used as intermediate timescale + * @return a new instant + * @param the type of the field elements + */ + public static > FieldAbsoluteDate createJDDate(final int jd, final T secondsSinceNoon, + final TimeScale timeScale, + final TimeScale pivotTimeScale) { + // Get the date in pivot timescale + final FieldAbsoluteDate dateInPivotTimeScale = createJDDate(jd, secondsSinceNoon, pivotTimeScale); + + // Compare offsets to TAI of the two time scales + final T offsetFromTAI = timeScale.offsetFromTAI(dateInPivotTimeScale). + subtract(pivotTimeScale.offsetFromTAI(dateInPivotTimeScale)); + + // Return date in desired timescale + return dateInPivotTimeScale.shiftedBy(offsetFromTAI.multiply(-1.)); + } + /** Build an instance corresponding to a Modified Julian Day date. * @param mjd modified Julian day * @param secondsInDay seconds in the day @@ -1011,10 +987,10 @@ public static > FieldAbsoluteDate getFiftie DataContext.getDefault().getTimeScales().getFiftiesEpoch()); } - /** Reference epoch for CCSDS Time Code Format (CCSDS 301.0-B-4): - * - *

          This method uses the {@link DataContext#getDefault() default data context}. - * + /** Reference epoch for CCSDS Time Code Format (CCSDS 301.0-B-4). + *

          + * This method uses the {@link DataContext#getDefault() default data context}. + *

          * 1958-01-01T00:00:00 International Atomic Time (not UTC). * @param the type of the field elements * @param field field for the components @@ -1172,6 +1148,35 @@ public T durationFrom(final FieldAbsoluteDate instant) { return offset.subtract(instant.offset).add(epoch - instant.epoch); } + /** Compute the physically elapsed duration between two instants. + *

          The returned duration is the number of seconds physically + * elapsed between the two instants, measured in a regular time + * scale with respect to surface of the Earth (i.e either the {@link + * TAIScale TAI scale}, the {@link TTScale TT scale} or the {@link + * GPSScale GPS scale}). It is the only method that gives a + * duration with a physical meaning.

          + *

          This method gives the same result (with less computation) + * as calling {@link #offsetFrom(FieldAbsoluteDate, TimeScale)} + * with a second argument set to one of the regular scales cited + * above.

          + *

          This method is the reverse of the {@link #FieldAbsoluteDate(FieldAbsoluteDate, + * double)} constructor.

          + * @param instant instant to subtract from the instance + * @param timeUnit {@link TimeUnit} precision for the offset + * @return offset in seconds between the two instants (positive + * if the instance is posterior to the argument) + * @see #offsetFrom(FieldAbsoluteDate, TimeScale) + * @see #FieldAbsoluteDate(FieldAbsoluteDate, double) + */ + public T durationFrom(final FieldAbsoluteDate instant, final TimeUnit timeUnit) { + final long deltaEpoch = timeUnit.convert(epoch - instant.epoch, TimeUnit.SECONDS); + + final long multiplier = timeUnit.convert(1, TimeUnit.SECONDS); + final T deltaOffset = offset.getField().getZero().add(offset.subtract(instant.offset).multiply(multiplier).round()); + + return deltaOffset.add(deltaEpoch); + } + /** Compute the physically elapsed duration between two instants. *

          The returned duration is the number of seconds physically * elapsed between the two instants, measured in a regular time @@ -1195,6 +1200,35 @@ public T durationFrom(final AbsoluteDate instant) { return offset.subtract(instant.getOffset()).add(epoch - instant.getEpoch()); } + /** Compute the physically elapsed duration between two instants. + *

          The returned duration is the number of seconds physically + * elapsed between the two instants, measured in a regular time + * scale with respect to surface of the Earth (i.e either the {@link + * TAIScale TAI scale}, the {@link TTScale TT scale} or the {@link + * GPSScale GPS scale}). It is the only method that gives a + * duration with a physical meaning.

          + *

          This method gives the same result (with less computation) + * as calling {@link #offsetFrom(FieldAbsoluteDate, TimeScale)} + * with a second argument set to one of the regular scales cited + * above.

          + *

          This method is the reverse of the {@link #FieldAbsoluteDate(FieldAbsoluteDate, + * double)} constructor.

          + * @param instant instant to subtract from the instance + * @param timeUnit {@link TimeUnit} precision for the offset + * @return offset in the given timeunit between the two instants (positive + * if the instance is posterior to the argument), rounded to the nearest integer {@link TimeUnit} + * @see #FieldAbsoluteDate(FieldAbsoluteDate, long, TimeUnit) + * @since 12.1 + */ + public T durationFrom(final AbsoluteDate instant, final TimeUnit timeUnit) { + final long deltaEpoch = timeUnit.convert(epoch - instant.getEpoch(), TimeUnit.SECONDS); + + final long multiplier = timeUnit.convert(1, TimeUnit.SECONDS); + final T deltaOffset = offset.getField().getZero().add(offset.subtract(instant.getOffset()).multiply(multiplier).round()); + + return deltaOffset.add(deltaEpoch); + } + /** Compute the apparent clock offset between two instant in the * perspective of a specific {@link TimeScale time scale}. *

          The offset is the number of seconds counted in the given @@ -1251,6 +1285,36 @@ public Date toDate(final TimeScale timeScale) { return new Date(FastMath.round((time + 10957.5 * 86400.0) * 1000)); } + /** + * Convert the instance to a Java {@link java.time.Instant Instant}. + * Nanosecond precision is preserved during this conversion + * + * @return a {@link java.time.Instant Instant} instance representing the location + * of the instant in the utc time scale + * @since 12.1 + */ + @DefaultDataContext + public Instant toInstant() { + return toInstant(TimeScalesFactory.getTimeScales()); + } + + /** + * Convert the instance to a Java {@link java.time.Instant Instant}. + * Nanosecond precision is preserved during this conversion + * + * @param timeScales the timescales to use + * @return a {@link java.time.Instant Instant} instance representing the location + * of the instant in the utc time scale + * @since 12.1 + */ + public Instant toInstant(final TimeScales timeScales) { + final UTCScale utc = timeScales.getUTC(); + final String stringWithoutUtcOffset = toStringWithoutUtcOffset(utc, 9); + + final LocalDateTime localDateTime = LocalDateTime.parse(stringWithoutUtcOffset, DateTimeFormatter.ISO_LOCAL_DATE_TIME); + return localDateTime.toInstant(ZoneOffset.UTC); + } + /** Split the instance into date/time components. * @param timeScale time scale to use * @return date/time components @@ -1275,16 +1339,16 @@ public DateTimeComponents getComponents(final TimeScale timeScale) { // split date and time final long carry = (long) FastMath.floor(sumAndResidual.getSum()); double offset2000B = (sumAndResidual.getSum() - carry) + sumAndResidual.getResidual(); - long offset2000A = epoch + carry + 43200l; + long offset2000A = epoch + carry + 43200L; if (offset2000B < 0) { offset2000A -= 1; offset2000B += 1; } - long time = offset2000A % 86400l; - if (time < 0l) { - time += 86400l; + long time = offset2000A % 86400L; + if (time < 0L) { + time += 86400L; } - final int date = (int) ((offset2000A - time) / 86400l); + final int date = (int) ((offset2000A - time) / 86400L); // extract calendar elements final DateComponents dateComponents = new DateComponents(DateComponents.J2000_EPOCH, date); @@ -1628,6 +1692,29 @@ public String toString(final TimeZone timeZone, final TimeScale utc) { return getComponents(timeZone, utc).toString(minuteDuration); } + /** + * Return a string representation of this date-time, rounded to the given precision. + * + *

          The format used is ISO8601 without the UTC offset.

          + * + * + * @param timeScale to use to compute components. + * @param fractionDigits the number of digits to include after the decimal point in + * the string representation of the seconds. The date and time + * is first rounded as necessary. {@code fractionDigits} must be + * greater than or equal to {@code 0}. + * @return string representation of this date, time, and UTC offset + * @see #toString(TimeScale) + * @see DateTimeComponents#toString(int, int) + * @see DateTimeComponents#toStringWithoutUtcOffset(int, int) + * @since 12.2 + */ + public String toStringWithoutUtcOffset(final TimeScale timeScale, + final int fractionDigits) { + return this.getComponents(timeScale) + .toStringWithoutUtcOffset(timeScale.minuteDuration(this), fractionDigits); + } + /** Get a time-shifted date. *

          * Calling this method is equivalent to call new FieldAbsoluteDate(this, dt). @@ -1644,6 +1731,23 @@ public FieldAbsoluteDate shiftedBy(final double dt) { return new FieldAbsoluteDate<>(this, dt); } + /** Get a time-shifted date. + *

          + * Calling this method is equivalent to call new FieldAbsoluteDate(this, dt, timeUnit). + *

          + * @param dt time shift in time units + * @param timeUnit {@link TimeUnit} for dt + * @return a new date, shifted with respect to instance (which is immutable) + * @see org.orekit.utils.FieldPVCoordinates#shiftedBy(double) + * @see org.orekit.attitudes.FieldAttitude#shiftedBy(double) + * @see org.orekit.orbits.FieldOrbit#shiftedBy(double) + * @see org.orekit.propagation.FieldSpacecraftState#shiftedBy(double) + * @since 12.1 + */ + public FieldAbsoluteDate shiftedBy(final long dt, final TimeUnit timeUnit) { + return new FieldAbsoluteDate<>(this, dt, timeUnit); + } + /** Transform the FieldAbsoluteDate in an AbsoluteDate. * @return AbsoluteDate of the FieldObject diff --git a/src/main/java/org/orekit/time/FieldClockOffset.java b/src/main/java/org/orekit/time/FieldClockOffset.java new file mode 100644 index 0000000000..490764ee3f --- /dev/null +++ b/src/main/java/org/orekit/time/FieldClockOffset.java @@ -0,0 +1,81 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.time; + +import org.hipparchus.CalculusFieldElement; + +/** Container for time stamped clock offset. + * @param type of the field elements + * @author Luc Maisonobe + * @since 12.1 + */ +public class FieldClockOffset> implements FieldTimeStamped { + + /** Date. */ + private final FieldAbsoluteDate date; + + /** Clock offset. */ + private final T offset; + + /** Clock rate. */ + private final T rate; + + /** Clock acceleration. */ + private final T acceleration; + + /** Simple constructor. + * @param date date + * @param offset clock offset + * @param rate clock rate (can be set to {@code null} if unknown) + * @param acceleration clock acceleration (can be set to {@code null} if unknown) + */ + public FieldClockOffset(final FieldAbsoluteDate date, final T offset, + final T rate, final T acceleration) { + this.date = date; + this.offset = offset; + this.rate = rate; + this.acceleration = acceleration; + } + + /** {@inheritDoc} */ + @Override + public FieldAbsoluteDate getDate() { + return date; + } + + /** Get offset. + * @return offset + */ + public T getOffset() { + return offset; + } + + /** Get rate. + * @return rate ({@code null} if unknown) + */ + public T getRate() { + return rate; + } + + /** Get acceleration. + * @return acceleration ({@code null} if unknown) + */ + public T getAcceleration() { + return acceleration; + } + +} diff --git a/src/main/java/org/orekit/time/FieldClockOffsetHermiteInterpolator.java b/src/main/java/org/orekit/time/FieldClockOffsetHermiteInterpolator.java new file mode 100644 index 0000000000..6a170a30a3 --- /dev/null +++ b/src/main/java/org/orekit/time/FieldClockOffsetHermiteInterpolator.java @@ -0,0 +1,109 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.time; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.analysis.interpolation.FieldHermiteInterpolator; +import org.hipparchus.analysis.interpolation.HermiteInterpolator; +import org.hipparchus.util.MathArrays; + +import java.util.List; + +/**bHermite interpolator of time stamped clock offsets. + * @param type of the field elements + * @author Luc Maisonobe + * @see HermiteInterpolator + * @see TimeInterpolator + * @since 12.1 + */ +public class FieldClockOffsetHermiteInterpolator> + extends AbstractFieldTimeInterpolator, T> { + + /** + * Constructor with default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s). + *

          + * 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 the number of interpolation points or derivatives availability is not sufficient, + * the rate and acceleration of interpolated offset will be silently set to 0 (i.e. + * model will be constant or linear only). + *

          + * @param interpolationPoints number of interpolation points + */ + public FieldClockOffsetHermiteInterpolator(final int interpolationPoints) { + this(interpolationPoints, DEFAULT_EXTRAPOLATION_THRESHOLD_SEC); + } + + /** + * Constructor. + *

          + * 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 the number of interpolation points or derivatives availability is not sufficient, + * the rate and acceleration of interpolated offset will be silently set to 0 (i.e. + * model will be constant or linear only). + *

          + * @param interpolationPoints number of interpolation points + * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail + */ + public FieldClockOffsetHermiteInterpolator(final int interpolationPoints, final double extrapolationThreshold) { + super(interpolationPoints, extrapolationThreshold); + } + + /** {@inheritDoc} */ + @Override + protected FieldClockOffset interpolate(final InterpolationData interpolationData) { + final FieldHermiteInterpolator interpolator = new FieldHermiteInterpolator<>(); + + // Fill interpolator with sample + final FieldAbsoluteDate interpolationDate = interpolationData.getInterpolationDate(); + final List> neighborList = interpolationData.getNeighborList(); + for (FieldClockOffset value : neighborList) { + final T deltaT = value.getDate().durationFrom(interpolationDate); + final T[] offset = MathArrays.buildArray(interpolationDate.getField(), 1); + offset[0] = value.getOffset(); + if (value.getRate() == null || value.getRate().isNaN()) { + // no clock rate for this entry + interpolator.addSamplePoint(deltaT, offset); + } else { + // clock rate is available + final T[] rate = MathArrays.buildArray(interpolationDate.getField(), 1); + rate[0] = value.getRate(); + if (value.getAcceleration() == null || value.getAcceleration().isNaN()) { + // no clock acceleration for this entry + interpolator.addSamplePoint(deltaT, offset, rate); + } else { + // clock acceleration is available + final T[] acceleration = MathArrays.buildArray(interpolationDate.getField(), 1); + acceleration[0] = value.getAcceleration(); + interpolator.addSamplePoint(deltaT, offset, rate, acceleration); + } + } + } + + final T[][] y = interpolator.derivatives(interpolationDate.getField().getZero(), 2); + return new FieldClockOffset<>(interpolationDate, y[0][0], y[1][0], y[2][0]); + + } + +} diff --git a/src/main/java/org/orekit/time/GPSScale.java b/src/main/java/org/orekit/time/GPSScale.java index 57a1181f6e..a93a86c7eb 100644 --- a/src/main/java/org/orekit/time/GPSScale.java +++ b/src/main/java/org/orekit/time/GPSScale.java @@ -16,8 +16,6 @@ */ package org.orekit.time; -import org.hipparchus.CalculusFieldElement; - /** GPS time scale. *

          By convention, TGPS = TAI - 19 s.

          *

          This is intended to be accessed thanks to {@link TimeScales}, @@ -25,45 +23,15 @@ * @author Luc Maisonobe * @see AbsoluteDate */ -public class GPSScale implements TimeScale { +public class GPSScale extends ConstantOffsetTimeScale { /** Serializable UID. */ - private static final long serialVersionUID = 20131209L; - - /** Offset from TAI. */ - private static final double OFFSET = -19; + private static final long serialVersionUID = 20240321L; /** Package private constructor for the factory. */ GPSScale() { - } - - /** {@inheritDoc} */ - @Override - public double offsetFromTAI(final AbsoluteDate date) { - return OFFSET; - } - - /** {@inheritDoc} */ - @Override - public > T offsetFromTAI(final FieldAbsoluteDate date) { - return date.getField().getZero().add(OFFSET); - } - - /** {@inheritDoc} */ - @Override - public double offsetToTAI(final DateComponents date, final TimeComponents time) { - return -OFFSET; - } - - /** {@inheritDoc} */ - public String getName() { - return "GPS"; - } - - /** {@inheritDoc} */ - public String toString() { - return getName(); + super("GPS", -19); } } diff --git a/src/main/java/org/orekit/time/GalileoScale.java b/src/main/java/org/orekit/time/GalileoScale.java index 90b330dfa3..dfce487e3e 100644 --- a/src/main/java/org/orekit/time/GalileoScale.java +++ b/src/main/java/org/orekit/time/GalileoScale.java @@ -16,8 +16,6 @@ */ package org.orekit.time; -import org.hipparchus.CalculusFieldElement; - /** Galileo system time scale. *

          By convention, TGST = UTC + 13s at Galileo epoch (1999-08-22T00:00:00Z).

          *

          This is intended to be accessed thanks to {@link TimeScales}, @@ -32,45 +30,15 @@ * @author Luc Maisonobe * @see AbsoluteDate */ -public class GalileoScale implements TimeScale { +public class GalileoScale extends ConstantOffsetTimeScale { /** Serializable UID. */ - private static final long serialVersionUID = 20131209L; - - /** Offset from TAI. */ - private static final double OFFSET = -19; + private static final long serialVersionUID = 20240321L; /** Package private constructor for the factory. */ GalileoScale() { - } - - /** {@inheritDoc} */ - @Override - public double offsetFromTAI(final AbsoluteDate date) { - return OFFSET; - } - - /** {@inheritDoc} */ - @Override - public > T offsetFromTAI(final FieldAbsoluteDate date) { - return date.getField().getZero().add(OFFSET); - } - - /** {@inheritDoc} */ - @Override - public double offsetToTAI(final DateComponents date, final TimeComponents time) { - return -OFFSET; - } - - /** {@inheritDoc} */ - public String getName() { - return "GST"; - } - - /** {@inheritDoc} */ - public String toString() { - return getName(); + super("GST", -19); } } diff --git a/src/main/java/org/orekit/time/IRNSSScale.java b/src/main/java/org/orekit/time/IRNSSScale.java index a8ea0bad2d..7c2724d7d6 100644 --- a/src/main/java/org/orekit/time/IRNSSScale.java +++ b/src/main/java/org/orekit/time/IRNSSScale.java @@ -16,9 +16,6 @@ */ package org.orekit.time; -import org.hipparchus.CalculusFieldElement; - - /** IRNSS time scale (also called IRNWT for IRNSS NetWork Time). *

          By convention, TIRNSS = TAI - 19 s.

          *

          This is intended to be accessed thanks to {@link TimeScales}, @@ -26,45 +23,15 @@ * @author Luc Maisonobe * @see AbsoluteDate */ -public class IRNSSScale implements TimeScale { +public class IRNSSScale extends ConstantOffsetTimeScale { /** Serializable UID. */ - private static final long serialVersionUID = 20180323L; - - /** Offset from TAI. */ - private static final double OFFSET = -19; + private static final long serialVersionUID = 20240321L; /** Package private constructor for the factory. */ IRNSSScale() { - } - - /** {@inheritDoc} */ - @Override - public double offsetFromTAI(final AbsoluteDate date) { - return OFFSET; - } - - /** {@inheritDoc} */ - @Override - public > T offsetFromTAI(final FieldAbsoluteDate date) { - return date.getField().getZero().add(OFFSET); - } - - /** {@inheritDoc} */ - @Override - public double offsetToTAI(final DateComponents date, final TimeComponents time) { - return -OFFSET; - } - - /** {@inheritDoc} */ - public String getName() { - return "IRNSS"; - } - - /** {@inheritDoc} */ - public String toString() { - return getName(); + super("IRNSS", -19); } } diff --git a/src/main/java/org/orekit/time/PerfectClockModel.java b/src/main/java/org/orekit/time/PerfectClockModel.java new file mode 100644 index 0000000000..e913a7e9dc --- /dev/null +++ b/src/main/java/org/orekit/time/PerfectClockModel.java @@ -0,0 +1,52 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.time; + +import org.hipparchus.CalculusFieldElement; + +/** Clock model for perfect clock with constant zero offset. + * @author Luc Maisonobe + * @since 12.1 + */ +public class PerfectClockModel implements ClockModel { + + /** {@inheritDoc} */ + @Override + public AbsoluteDate getValidityStart() { + return AbsoluteDate.PAST_INFINITY; + } + + /** {@inheritDoc} */ + @Override + public AbsoluteDate getValidityEnd() { + return AbsoluteDate.FUTURE_INFINITY; + } + + /** {@inheritDoc} */ + @Override + public ClockOffset getOffset(final AbsoluteDate date) { + return new ClockOffset(date, 0, 0, 0); + } + + /** {@inheritDoc} */ + @Override + public > FieldClockOffset getOffset(final FieldAbsoluteDate date) { + final T zero = date.getField().getZero(); + return new FieldClockOffset<>(date, zero, zero, zero); + } + +} diff --git a/src/main/java/org/orekit/time/QZSSScale.java b/src/main/java/org/orekit/time/QZSSScale.java index dafef125db..c3eecdfa31 100644 --- a/src/main/java/org/orekit/time/QZSSScale.java +++ b/src/main/java/org/orekit/time/QZSSScale.java @@ -16,8 +16,6 @@ */ package org.orekit.time; -import org.hipparchus.CalculusFieldElement; - /** QZSS time scale. *

          By convention, TQZSS = TAI - 19 s.

          *

          The time scale is defined in > T offsetFromTAI(final FieldAbsoluteDate date) { - return date.getField().getZero().add(OFFSET); - } - - /** {@inheritDoc} */ - @Override - public double offsetToTAI(final DateComponents date, final TimeComponents time) { - return -OFFSET; - } - - /** {@inheritDoc} */ - public String getName() { - return "QZSS"; - } - - /** {@inheritDoc} */ - public String toString() { - return getName(); + super("QZSS", -19); } } diff --git a/src/main/java/org/orekit/time/SampledClockModel.java b/src/main/java/org/orekit/time/SampledClockModel.java new file mode 100644 index 0000000000..a93c0f50d8 --- /dev/null +++ b/src/main/java/org/orekit/time/SampledClockModel.java @@ -0,0 +1,105 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.time; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.orekit.utils.ImmutableTimeStampedCache; + +import java.util.List; +import java.util.stream.Stream; + +/** Offset clock model backed up by a sample. + * @author Luc Maisonobe + * @since 12.1 + */ +public class SampledClockModel implements ClockModel { + + /** sample. */ + private final ImmutableTimeStampedCache sample; + + /** Simple constructor. + * @param sample clock offsets sample + * @param nbInterpolationPoints number of points to use in interpolation + */ + public SampledClockModel(final List sample, final int nbInterpolationPoints) { + this.sample = new ImmutableTimeStampedCache<>(nbInterpolationPoints, sample); + } + + /** Get the clock offsets cache. + * @return clock offsets cache + */ + public ImmutableTimeStampedCache getCache() { + return sample; + } + + /** {@inheritDoc} */ + @Override + public AbsoluteDate getValidityStart() { + return sample.getEarliest().getDate(); + } + + /** {@inheritDoc} */ + @Override + public AbsoluteDate getValidityEnd() { + return sample.getLatest().getDate(); + } + + /** {@inheritDoc} */ + @Override + public ClockOffset getOffset(final AbsoluteDate date) { + return new ClockOffsetHermiteInterpolator(sample.getMaxNeighborsSize()). + interpolate(date, sample.getNeighbors(date)); + } + + /** {@inheritDoc} */ + @Override + public > FieldClockOffset getOffset(final FieldAbsoluteDate date) { + + // convert the neighbors to field + final Field field = date.getField(); + final T zero = field.getZero(); + final Stream> fieldSample = + sample. + getNeighbors(date.toAbsoluteDate()). + map(c -> { + final FieldAbsoluteDate dateF = new FieldAbsoluteDate<>(field, c.getDate()); + final T offsetF = zero.newInstance(c.getOffset()); + final T rateF; + final T accelerationF; + if (Double.isNaN(c.getRate())) { + // no rate available + rateF = null; + accelerationF = null; + } else { + // rate available + rateF = zero.newInstance(c.getRate()); + accelerationF = Double.isNaN(c.getAcceleration()) ? + null : + zero.newInstance(c.getAcceleration()); + } + return new FieldClockOffset<>(dateF, offsetF, rateF, accelerationF); + }); + + // perform interpolation + final FieldClockOffsetHermiteInterpolator interpolator = + new FieldClockOffsetHermiteInterpolator<>(sample.getMaxNeighborsSize()); + return interpolator.interpolate(date, fieldSample); + + } + +} diff --git a/src/main/java/org/orekit/time/TAIScale.java b/src/main/java/org/orekit/time/TAIScale.java index 04c61822c1..7d45464dae 100644 --- a/src/main/java/org/orekit/time/TAIScale.java +++ b/src/main/java/org/orekit/time/TAIScale.java @@ -16,50 +16,21 @@ */ package org.orekit.time; -import org.hipparchus.CalculusFieldElement; - /** International Atomic Time. *

          This is intended to be accessed thanks to {@link TimeScales}, * so there is no public constructor.

          * @author Luc Maisonobe * @see AbsoluteDate */ -public class TAIScale implements TimeScale { +public class TAIScale extends ConstantOffsetTimeScale { /** Serializable UID. */ - private static final long serialVersionUID = 20131209L; + private static final long serialVersionUID = 20240321L; /** Package private constructor for the factory. */ TAIScale() { - } - - /** {@inheritDoc} */ - @Override - public double offsetFromTAI(final AbsoluteDate taiTime) { - return 0; - } - - /** {@inheritDoc} */ - @Override - public > T offsetFromTAI(final FieldAbsoluteDate date) { - return date.getField().getZero(); - } - - /** {@inheritDoc} */ - @Override - public double offsetToTAI(final DateComponents date, final TimeComponents time) { - return 0; - } - - /** {@inheritDoc} */ - public String getName() { - return "TAI"; - } - - /** {@inheritDoc} */ - public String toString() { - return getName(); + super("TAI", 0); } } diff --git a/src/main/java/org/orekit/time/TTScale.java b/src/main/java/org/orekit/time/TTScale.java index 863898a4ef..add1f9dd5e 100644 --- a/src/main/java/org/orekit/time/TTScale.java +++ b/src/main/java/org/orekit/time/TTScale.java @@ -16,8 +16,6 @@ */ package org.orekit.time; -import org.hipparchus.CalculusFieldElement; - /** Terrestrial Time as defined by IAU(1991) recommendation IV. *

          Coordinate time at the surface of the Earth. IT is the * successor of Ephemeris Time TE.

          @@ -27,45 +25,15 @@ * @author Luc Maisonobe * @see AbsoluteDate */ -public class TTScale implements TimeScale { +public class TTScale extends ConstantOffsetTimeScale { /** Serializable UID. */ - private static final long serialVersionUID = 20131209L; - - /** Offset from TAI. */ - private static final double OFFSET = 32.184; + private static final long serialVersionUID = 20240321L; /** Package private constructor for the factory. */ TTScale() { - } - - /** {@inheritDoc} */ - @Override - public double offsetFromTAI(final AbsoluteDate date) { - return OFFSET; - } - - /** {@inheritDoc} */ - @Override - public > T offsetFromTAI(final FieldAbsoluteDate date) { - return date.getField().getZero().add(OFFSET); - } - - /** {@inheritDoc} */ - @Override - public double offsetToTAI(final DateComponents date, final TimeComponents time) { - return -OFFSET; - } - - /** {@inheritDoc} */ - public String getName() { - return "TT"; - } - - /** {@inheritDoc} */ - public String toString() { - return getName(); + super("TT", 32.184); } } diff --git a/src/main/java/org/orekit/time/TimeStampedDoubleAndDerivative.java b/src/main/java/org/orekit/time/TimeStampedDoubleAndDerivative.java new file mode 100644 index 0000000000..33c5bb5f1a --- /dev/null +++ b/src/main/java/org/orekit/time/TimeStampedDoubleAndDerivative.java @@ -0,0 +1,50 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.time; + +/** + * Class that associates a double, its time derivative with a date. + * + * @author Luc Maisonobe + * @since 12.1 + */ +public class TimeStampedDoubleAndDerivative extends TimeStampedDouble { + + /** Time derivative. */ + private final double derivative; + + /** + * Constructor. + * + * @param value value + * @param derivative time derivative + * @param date date associated to value + */ + public TimeStampedDoubleAndDerivative(final double value, final double derivative, + final AbsoluteDate date) { + super(value, date); + this.derivative = derivative; + } + + /** Get time derivative. + * @return time derivztive + */ + public double getDerivative() { + return derivative; + } + +} diff --git a/src/main/java/org/orekit/time/TimeStampedDoubleAndDerivativeHermiteInterpolator.java b/src/main/java/org/orekit/time/TimeStampedDoubleAndDerivativeHermiteInterpolator.java new file mode 100644 index 0000000000..8a0772627b --- /dev/null +++ b/src/main/java/org/orekit/time/TimeStampedDoubleAndDerivativeHermiteInterpolator.java @@ -0,0 +1,94 @@ +/* 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.time; + +import org.hipparchus.analysis.interpolation.HermiteInterpolator; + +import java.util.List; + +/** + * Hermite interpolator of time stamped double value. + * + * @author Vincent Cucchietti + * @author Luc Maisonobe + * @see HermiteInterpolator + * @see TimeInterpolator + */ +public class TimeStampedDoubleAndDerivativeHermiteInterpolator + extends AbstractTimeInterpolator { + + /** + * Constructor with : + *
            + *
          • Default number of interpolation points of {@code DEFAULT_INTERPOLATION_POINTS}
          • + *
          • Default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s)
          • + *
          + * 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). + */ + public TimeStampedDoubleAndDerivativeHermiteInterpolator() { + this(DEFAULT_INTERPOLATION_POINTS); + } + + /** + * Constructor with default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s). + *

          + * 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). + * + * @param interpolationPoints number of interpolation points + */ + public TimeStampedDoubleAndDerivativeHermiteInterpolator(final int interpolationPoints) { + this(interpolationPoints, DEFAULT_EXTRAPOLATION_THRESHOLD_SEC); + } + + /** + * Constructor. + *

          + * 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). + * + * @param interpolationPoints number of interpolation points + * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail + */ + public TimeStampedDoubleAndDerivativeHermiteInterpolator(final int interpolationPoints, final double extrapolationThreshold) { + super(interpolationPoints, extrapolationThreshold); + } + + /** {@inheritDoc} */ + @Override + protected TimeStampedDoubleAndDerivative interpolate(final InterpolationData interpolationData) { + final HermiteInterpolator interpolator = new HermiteInterpolator(); + + // Fill interpolator with sample + final AbsoluteDate interpolationDate = interpolationData.getInterpolationDate(); + final List neighborList = interpolationData.getNeighborList(); + for (TimeStampedDoubleAndDerivative value : neighborList) { + final double deltaT = value.getDate().durationFrom(interpolationDate); + interpolator.addSamplePoint(deltaT, + new double[] { value.getValue() }, + new double[] { value.getDerivative() }); + } + + final double[][] y = interpolator.derivatives(0, 1); + return new TimeStampedDoubleAndDerivative(y[0][0], y[1][0], interpolationDate); + } + +} diff --git a/src/main/java/org/orekit/time/UTCScale.java b/src/main/java/org/orekit/time/UTCScale.java index 9f78ef85c0..9848ea6b0a 100644 --- a/src/main/java/org/orekit/time/UTCScale.java +++ b/src/main/java/org/orekit/time/UTCScale.java @@ -301,7 +301,7 @@ public double getLeap(final AbsoluteDate date) { /** {@inheritDoc} */ @Override public > T getLeap(final FieldAbsoluteDate date) { - return date.getField().getZero().add(getLeap(date.toAbsoluteDate())); + return date.getField().getZero().newInstance(getLeap(date.toAbsoluteDate())); } /** Find the index of the offset valid at some date. diff --git a/src/main/java/org/orekit/time/UTCTAIOffset.java b/src/main/java/org/orekit/time/UTCTAIOffset.java index 472f817381..6106548f58 100644 --- a/src/main/java/org/orekit/time/UTCTAIOffset.java +++ b/src/main/java/org/orekit/time/UTCTAIOffset.java @@ -150,7 +150,7 @@ public > T getOffset(final FieldAbsoluteDate the type of the field elements - * @param field field for the argument and value - * @return converted function - */ - default > FieldPVCoordinatesProvider toFieldPVCoordinatesProvider(Field field) { - return this::getPVCoordinates; - } +public interface ExtendedPVCoordinatesProvider extends ExtendedPositionProvider { /** Get the position of the body in the selected frame. * @param date current date @@ -56,6 +46,7 @@ default > FieldVector3D getPosition(final F * @param type for the field elements * @return time-stamped position/velocity of the body (m and m/s) */ + @Override >TimeStampedFieldPVCoordinates getPVCoordinates(FieldAbsoluteDate date, Frame frame); diff --git a/src/main/java/org/orekit/utils/ExtendedPositionProvider.java b/src/main/java/org/orekit/utils/ExtendedPositionProvider.java new file mode 100644 index 0000000000..1df3ceedd0 --- /dev/null +++ b/src/main/java/org/orekit/utils/ExtendedPositionProvider.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.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; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.orekit.frames.Frame; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; + +/** + * Interface for position providers (including for Field). + * Emulates position (and derivatives) vector as a function of time. + * @author Romain Serra + * @since 12.1 + */ +public interface ExtendedPositionProvider extends PVCoordinatesProvider { + + /** Get the position in the selected frame. + * @param date current date + * @param frame the frame where to define the position + * @param field type + * @return position + */ + > FieldVector3D getPosition(FieldAbsoluteDate date, Frame frame); + + /** {@inheritDoc} */ + @Override + default TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame frame) { + final UnivariateDerivative2Field ud2Field = UnivariateDerivative2Field.getInstance(); + final UnivariateDerivative2 ud2Shift = new UnivariateDerivative2(0., 1., 0.); + final FieldAbsoluteDate fieldDate = new FieldAbsoluteDate<>(ud2Field, date).shiftedBy(ud2Shift); + final FieldVector3D ud2Position = getPosition(fieldDate, frame); + final Vector3D position = ud2Position.toVector3D(); + final Vector3D velocity = new Vector3D(ud2Position.getX().getFirstDerivative(), ud2Position.getY().getFirstDerivative(), + ud2Position.getZ().getFirstDerivative()); + final Vector3D acceleration = new Vector3D(ud2Position.getX().getSecondDerivative(), ud2Position.getY().getSecondDerivative(), + ud2Position.getZ().getSecondDerivative()); + return new TimeStampedPVCoordinates(date, new PVCoordinates(position, velocity, acceleration)); + } + + /** Get the position-velocity-acceleration in the selected frame. + * @param date current date + * @param frame the frame where to define the position + * @param field type + * @return position-velocity-acceleration vector + */ + 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 FieldVector3D> fud2Position = getPosition(fud2Date, frame); + final FieldVector3D position = new FieldVector3D<>(fud2Position.getX().getValue(), fud2Position.getY().getValue(), + fud2Position.getZ().getValue()); + final FieldVector3D velocity = new FieldVector3D<>(fud2Position.getX().getFirstDerivative(), fud2Position.getY().getFirstDerivative(), + fud2Position.getZ().getFirstDerivative()); + final FieldVector3D acceleration = new FieldVector3D<>(fud2Position.getX().getSecondDerivative(), fud2Position.getY().getSecondDerivative(), + fud2Position.getZ().getSecondDerivative()); + return new TimeStampedFieldPVCoordinates<>(date, position, velocity, acceleration); + } + + /** Convert to a {@link FieldPVCoordinatesProvider} with a specific type. + * @param the type of the field elements + * @param field field for the argument and value + * @return converted function + */ + default > FieldPVCoordinatesProvider toFieldPVCoordinatesProvider(Field field) { + return new FieldPVCoordinatesProvider() { + + @Override + public FieldVector3D getPosition(final FieldAbsoluteDate date, final Frame frame) { + return ExtendedPositionProvider.this.getPosition(date, frame); + } + + @Override + public TimeStampedFieldPVCoordinates getPVCoordinates(final FieldAbsoluteDate date, + final Frame frame) { + return ExtendedPositionProvider.this.getPVCoordinates(date, frame); + } + }; + } +} diff --git a/src/main/java/org/orekit/utils/FieldAngularCoordinates.java b/src/main/java/org/orekit/utils/FieldAngularCoordinates.java index 9a40840619..8890cb4e8f 100644 --- a/src/main/java/org/orekit/utils/FieldAngularCoordinates.java +++ b/src/main/java/org/orekit/utils/FieldAngularCoordinates.java @@ -75,10 +75,7 @@ public class FieldAngularCoordinates> */ public FieldAngularCoordinates(final FieldRotation rotation, final FieldVector3D rotationRate) { - this(rotation, rotationRate, - new FieldVector3D<>(rotation.getQ0().getField().getZero(), - rotation.getQ0().getField().getZero(), - rotation.getQ0().getField().getZero())); + this(rotation, rotationRate, FieldVector3D.getZero(rotation.getQ0().getField())); } /** Builds a rotation / rotation rate / rotation acceleration triplet. @@ -523,7 +520,7 @@ private T[] array6(final T e1, final T e2, final T e3, final T e4, final T e5, f FieldVector3D estimateRate(final FieldRotation start, final FieldRotation end, final double dt) { - return estimateRate(start, end, start.getQ0().getField().getZero().add(dt)); + return estimateRate(start, end, start.getQ0().getField().getZero().newInstance(dt)); } /** Estimate rotation rate between two orientations. @@ -608,7 +605,7 @@ public FieldRotation rotationShiftedBy(final T dt) { // frame seem to rotate in the opposite direction final FieldRotation quadraticContribution = new FieldRotation<>(rotationAcceleration, - acc.multiply(dt).multiply(dt).multiply(0.5), + acc.multiply(dt.square()).multiply(0.5), RotationConvention.FRAME_TRANSFORM); // the quadratic contribution is a small rotation: @@ -632,7 +629,7 @@ public FieldRotation rotationShiftedBy(final T dt) { */ @Override public FieldAngularCoordinates shiftedBy(final double dt) { - return shiftedBy(rotation.getQ0().getField().getZero().add(dt)); + return shiftedBy(rotation.getQ0().getField().getZero().newInstance(dt)); } /** Get a time-shifted state. @@ -688,7 +685,7 @@ public FieldAngularCoordinates shiftedBy(final T dt) { // frame seem to rotate in the opposite direction final FieldAngularCoordinates quadraticContribution = new FieldAngularCoordinates<>(new FieldRotation<>(rotationAcceleration, - acc.multiply(dt.multiply(0.5).multiply(dt)), + acc.multiply(dt.square().multiply(0.5)), RotationConvention.FRAME_TRANSFORM), new FieldVector3D<>(dt, rotationAcceleration), rotationAcceleration); @@ -998,7 +995,7 @@ private T linearCombination(final T a1, final T b1, final T a2, final T b2, fina public static > FieldAngularCoordinates createFromModifiedRodrigues(final T[][] r) { // rotation - final T rSquared = r[0][0].multiply(r[0][0]).add(r[0][1].multiply(r[0][1])).add(r[0][2].multiply(r[0][2])); + final T rSquared = r[0][0].square().add(r[0][1].square()).add(r[0][2].square()); final T oPQ0 = rSquared.add(1).reciprocal().multiply(2); final T q0 = oPQ0.subtract(1); final T q1 = oPQ0.multiply(r[0][0]); @@ -1016,9 +1013,9 @@ public static > FieldAngularCoordinates cr final T oZ = q0.linearCombination(q3.negate(), q0Dot, q2, q1Dot, q1.negate(), q2Dot, q0, q3Dot).multiply(2); // rotation acceleration - final T q0DotDot = q0.subtract(1).negate().divide(oPQ0).multiply(q0Dot).multiply(q0Dot). + final T q0DotDot = q0.subtract(1).negate().divide(oPQ0).multiply(q0Dot.square()). subtract(oPQ02.multiply(q0.linearCombination(r[0][0], r[2][0], r[0][1], r[2][1], r[0][2], r[2][2]))). - subtract(q1Dot.multiply(q1Dot).add(q2Dot.multiply(q2Dot)).add(q3Dot.multiply(q3Dot))); + subtract(q1Dot.square().add(q2Dot.square()).add(q3Dot.square())); final T q1DotDot = q0.linearCombination(oPQ0, r[2][0], r[1][0].add(r[1][0]), q0Dot, r[0][0], q0DotDot); final T q2DotDot = q0.linearCombination(oPQ0, r[2][1], r[1][1].add(r[1][1]), q0Dot, r[0][1], q0DotDot); final T q3DotDot = q0.linearCombination(oPQ0, r[2][2], r[1][2].add(r[1][2]), q0Dot, r[0][2], q0DotDot); diff --git a/src/main/java/org/orekit/utils/FieldLegendrePolynomials.java b/src/main/java/org/orekit/utils/FieldLegendrePolynomials.java index d71cc014dd..8e97e449c5 100644 --- a/src/main/java/org/orekit/utils/FieldLegendrePolynomials.java +++ b/src/main/java/org/orekit/utils/FieldLegendrePolynomials.java @@ -51,7 +51,7 @@ public FieldLegendrePolynomials(final int degree, final int order, // Initialize array this.pCoef = MathArrays.buildArray(field, degree + 1, order + 1); - final T t2 = t.multiply(t); + final T t2 = t.square(); for (int n = 0; n <= degree; n++) { diff --git a/src/main/java/org/orekit/utils/FieldPVCoordinates.java b/src/main/java/org/orekit/utils/FieldPVCoordinates.java index f71c017ad1..a9b57c11d0 100644 --- a/src/main/java/org/orekit/utils/FieldPVCoordinates.java +++ b/src/main/java/org/orekit/utils/FieldPVCoordinates.java @@ -668,7 +668,7 @@ public FieldPVCoordinates shiftedBy(final T dt) { */ public FieldVector3D positionShiftedBy(final T dt) { final T one = dt.getField().getOne(); - return new FieldVector3D<>(one, position, dt, velocity, dt.multiply(dt).multiply(0.5), acceleration); + return new FieldVector3D<>(one, position, dt, velocity, dt.square().multiply(0.5), acceleration); } /** Gets the position. diff --git a/src/main/java/org/orekit/utils/FieldShiftingPVCoordinatesProvider.java b/src/main/java/org/orekit/utils/FieldShiftingPVCoordinatesProvider.java new file mode 100644 index 0000000000..6bdd1e9b31 --- /dev/null +++ b/src/main/java/org/orekit/utils/FieldShiftingPVCoordinatesProvider.java @@ -0,0 +1,56 @@ +/* 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.utils; + +import org.hipparchus.CalculusFieldElement; +import org.orekit.frames.Frame; +import org.orekit.time.FieldAbsoluteDate; + +/** Provider using simple {@link FieldPVCoordinates#shiftedBy(CalculusFieldElement)} shiftedBy} and frame transforms for evolution. + * @param the type of the field elements + * @author Luc Maisonobe + * @since 12.1 + */ +public class FieldShiftingPVCoordinatesProvider> + implements FieldPVCoordinatesProvider { + + /** Reference coordinates. */ + private final TimeStampedFieldPVCoordinates referencePV; + + /** Frame in which {@link #referencePV} is defined. */ + private final Frame referenceFrame; + + /** Simple constructor. + * @param referencePV reference coordinates + * @param referenceFrame frame in which {@code reference} is defined + */ + public FieldShiftingPVCoordinatesProvider(final TimeStampedFieldPVCoordinates referencePV, + final Frame referenceFrame) { + this.referencePV = referencePV; + this.referenceFrame = referenceFrame; + } + + /** {@inheritDoc} */ + @Override + public TimeStampedFieldPVCoordinates getPVCoordinates(final FieldAbsoluteDate date, + final Frame frame) { + final TimeStampedFieldPVCoordinates shifted = referencePV.shiftedBy(date.durationFrom(referencePV)); + return referenceFrame.getTransformTo(frame, date).transformPVCoordinates(shifted); + } + +} diff --git a/src/main/java/org/orekit/utils/FieldSortedListTrimmer.java b/src/main/java/org/orekit/utils/FieldSortedListTrimmer.java new file mode 100644 index 0000000000..d3f2458ac4 --- /dev/null +++ b/src/main/java/org/orekit/utils/FieldSortedListTrimmer.java @@ -0,0 +1,167 @@ +/* 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.utils; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.exception.LocalizedCoreFormats; +import org.hipparchus.util.FastMath; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitIllegalArgumentException; +import org.orekit.errors.OrekitMessages; +import org.orekit.errors.TimeStampedCacheException; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.FieldTimeStamped; + +import java.util.List; + +/** A trimmer for externally stored chronologically sorted lists. + * @author Evan Ward + * @author Vincent Cucchietti + * @since 12.1 + */ +public class FieldSortedListTrimmer { + + /** Size list to return from {@link #getNeighborsSubList(FieldAbsoluteDate, List)}. */ + private final int neighborsSize; + + /** + * Create a new cache with the given neighbors size and data. + * + * @param neighborsSize size of the list returned from {@link #getNeighborsSubList(FieldAbsoluteDate, List)}. + */ + public FieldSortedListTrimmer(final int neighborsSize) { + if (neighborsSize < 1) { + throw new OrekitIllegalArgumentException(LocalizedCoreFormats.NUMBER_TOO_SMALL, + neighborsSize, 1); + } + // assign instance variables + this.neighborsSize = neighborsSize; + } + + /** Get size of the list returned from {@link #getNeighborsSubList(FieldAbsoluteDate, List)}. + * @return size of the list returned from {@link #getNeighborsSubList(FieldAbsoluteDate, List)} + */ + public int getNeighborsSize() { + return neighborsSize; + } + + /** Get the entries surrounding a central date. + *

          + * If the central date is well within covered range, the returned array will + * be balanced with half the points before central date and half the points + * after it (depending on n parity, of course). If the central date is near + * the boundary, then the returned array will be unbalanced and will contain + * only the n earliest (or latest) entries. A typical example of the later + * case is leap seconds cache, since the number of leap seconds cannot be + * arbitrarily increased. + *

          + * @param the type of data + * @param the type of the field elements + * @param central central date + * @param data complete list of entries (must be chronologically sorted) + * @return entries surrounding the specified date (sublist of {@code data}) + */ + public , K extends CalculusFieldElement> + List getNeighborsSubList(final FieldAbsoluteDate central, final List data) { + + if (neighborsSize > data.size()) { + throw new OrekitException(OrekitMessages.NOT_ENOUGH_DATA, data.size()); + } + + // Find central index + final int i = findIndex(central, data); + + // Check index in the range of the data + if (i < 0) { + final FieldAbsoluteDate earliest = data.get(0).getDate(); + throw new TimeStampedCacheException(OrekitMessages.UNABLE_TO_GENERATE_NEW_DATA_BEFORE, + earliest, central, earliest.durationFrom(central).getReal()); + } + else if (i >= data.size()) { + final FieldAbsoluteDate latest = data.get(data.size() - 1).getDate(); + throw new TimeStampedCacheException(OrekitMessages.UNABLE_TO_GENERATE_NEW_DATA_AFTER, + latest, central, central.durationFrom(latest).getReal()); + } + + // Force unbalanced range if necessary + int start = FastMath.max(0, i - (neighborsSize - 1) / 2); + final int end = FastMath.min(data.size(), start + neighborsSize); + start = end - neighborsSize; + + // Return list without copying + return data.subList(start, end); + } + + /** + * Find the index, i, to {@code data} such that {@code data[i] <= t} and + * {@code data[i+1] > t} if {@code data[i+1]} exists. + * + * @param the type of data + * @param the type of the field elements + * @param t the time + * @param data complete list of entries (must be chronologically sorted) + * + * @return the index of the data at or just before {@code t}, {@code -1} if {@code t} is before the first entry, or + * {@code data.size()} if {@code t} is after the last entry. + */ + private , K extends CalculusFieldElement> + int findIndex(final FieldAbsoluteDate t, final List data) { + // left bracket of search algorithm + int iInf = 0; + K dtInf = t.durationFrom(data.get(0)); + if (dtInf.getReal() < 0) { + // before first entry + return -1; + } + + // right bracket of search algorithm + int iSup = data.size() - 1; + K dtSup = t.durationFrom(data.get(data.size() - 1)); + if (dtSup.getReal() > 0) { + // after last entry + return data.size(); + } + + // search entries, using linear interpolation + // this should take only 2 iterations for near linear entries (most frequent use case) + // regardless of the number of entries + // this is much faster than binary search for large number of entries + while (iSup - iInf > 1) { + final int iInterp = (int) FastMath.rint(dtSup.multiply(iInf).subtract(dtInf.multiply(iSup)).divide(dtSup.subtract(dtInf)).getReal()); + final int iMed = FastMath.max(iInf + 1, FastMath.min(iInterp, iSup - 1)); + final K dtMed = t.durationFrom(data.get(iMed).getDate()); + if (dtMed.getReal() < 0) { + iSup = iMed; + dtSup = dtMed; + } else { + iInf = iMed; + dtInf = dtMed; + } + } + + // at this point data[iInf] <= t <= data[iSup], but the javadoc for this method + // says the upper bound is exclusive, so check for equality to make a half open + // interval. + if (dtSup.getReal() == 0.0) { + return iSup; + } + + return iInf; + + } + +} diff --git a/src/main/java/org/orekit/utils/FieldTimeStampedCache.java b/src/main/java/org/orekit/utils/FieldTimeStampedCache.java index f51f8266d6..dd88d47cc9 100644 --- a/src/main/java/org/orekit/utils/FieldTimeStampedCache.java +++ b/src/main/java/org/orekit/utils/FieldTimeStampedCache.java @@ -50,16 +50,38 @@ public interface FieldTimeStampedCache, KK extend * @param central central date * * @return list of cached entries surrounding the specified date. The size of the list is guaranteed to be - * {@link #getNeighborsSize()}. + * {@link #getMaxNeighborsSize()}. */ - Stream getNeighbors(FieldAbsoluteDate central); + default Stream getNeighbors(FieldAbsoluteDate central) { + return getNeighbors(central, getMaxNeighborsSize()); + } + + /** + * Get the entries surrounding a central date. + *

          + * If the central date is well within covered range, the returned array will be balanced with half the points before + * central date and half the points after it (depending on n parity, of course). If the central date is near the + * boundary, then the returned array will be unbalanced and will contain only the n earliest (or latest) entries. A + * typical example of the later case is leap seconds cache, since the number of leap seconds cannot be arbitrarily + * increased. + *

          + * This method is safe for multiple threads to execute concurrently. + * + * @param central central date + * @param n number of neighbors (cannot exceed {@link #getMaxNeighborsSize()}) + * + * @return list of cached entries surrounding the specified date. The size of the list is guaranteed to be + * {@link #getMaxNeighborsSize()}. + * @since 12.1 + */ + Stream getNeighbors(FieldAbsoluteDate central, int n); /** * Get the fixed size of the lists returned by {@link #getNeighbors(FieldAbsoluteDate)}. * * @return size of the list */ - int getNeighborsSize(); + int getMaxNeighborsSize(); /** * Get the earliest entry in this cache. diff --git a/src/main/java/org/orekit/utils/FieldTrackingCoordinates.java b/src/main/java/org/orekit/utils/FieldTrackingCoordinates.java index b5d9316478..9d46e33f7c 100644 --- a/src/main/java/org/orekit/utils/FieldTrackingCoordinates.java +++ b/src/main/java/org/orekit/utils/FieldTrackingCoordinates.java @@ -17,6 +17,7 @@ package org.orekit.utils; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; /** Container for azimut/elevation/range coordinates as seen from a ground point. * @param the type of the field elements @@ -45,6 +46,17 @@ public FieldTrackingCoordinates(final T azimuth, final T elevation, final T rang this.range = range; } + /** Build a new instance from a {@link TrackingCoordinates}. + * @param field field to which the elements belong + * @param trackingCoordinates tracking coordinates to convert + * @since 12.1 + */ + public FieldTrackingCoordinates(final Field field, final TrackingCoordinates trackingCoordinates) { + this(field.getZero().newInstance(trackingCoordinates.getAzimuth()), + field.getZero().newInstance(trackingCoordinates.getElevation()), + field.getZero().newInstance(trackingCoordinates.getRange())); + } + /** Get the azimuth. *

          The azimuth is the angle between the North direction at local point and * the projection in local horizontal plane of the direction from local point diff --git a/src/main/java/org/orekit/utils/Fieldifier.java b/src/main/java/org/orekit/utils/Fieldifier.java index c5af02d282..d216a7cff9 100644 --- a/src/main/java/org/orekit/utils/Fieldifier.java +++ b/src/main/java/org/orekit/utils/Fieldifier.java @@ -21,19 +21,8 @@ import org.hipparchus.linear.FieldMatrix; import org.hipparchus.linear.MatrixUtils; import org.hipparchus.linear.RealMatrix; -import org.orekit.errors.OrekitInternalError; -import org.orekit.frames.Frame; -import org.orekit.orbits.CircularOrbit; -import org.orekit.orbits.EquinoctialOrbit; -import org.orekit.orbits.FieldCartesianOrbit; -import org.orekit.orbits.FieldCircularOrbit; -import org.orekit.orbits.FieldEquinoctialOrbit; -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.propagation.FieldStateCovariance; import org.orekit.propagation.StateCovariance; import org.orekit.time.FieldAbsoluteDate; @@ -60,93 +49,12 @@ private Fieldifier() { * @param type of the elements * * @return fielded orbit + * @deprecated */ + @Deprecated public static > FieldOrbit fieldify(final Field field, final Orbit orbit) { - final T one = field.getOne(); - final FieldAbsoluteDate fieldDate = new FieldAbsoluteDate<>(field, orbit.getDate()); - final T fieldMu = one.multiply(orbit.getMu()); - final Frame frame = orbit.getFrame(); - - switch (orbit.getType()) { - case CIRCULAR: { - final CircularOrbit circOrbit = (CircularOrbit) OrbitType.CIRCULAR.convertType(orbit); - - // Get orbital elements - final T a = one.multiply(circOrbit.getA()); - final T ex = one.multiply(circOrbit.getCircularEx()); - final T ey = one.multiply(circOrbit.getCircularEy()); - final T i = one.multiply(circOrbit.getI()); - final T raan = one.multiply(circOrbit.getRightAscensionOfAscendingNode()); - final T alphaM = one.multiply(circOrbit.getAlphaM()); - - // Get derivatives - final T aDot = one.multiply(circOrbit.getADot()); - final T exDot = one.multiply(circOrbit.getCircularExDot()); - final T eyDot = one.multiply(circOrbit.getCircularEyDot()); - final T iDot = one.multiply(circOrbit.getIDot()); - final T raanDot = one.multiply(circOrbit.getRightAscensionOfAscendingNodeDot()); - final T alphaMDot = one.multiply(circOrbit.getAlphaMDot()); - - return new FieldCircularOrbit<>(a, ex, ey, i, raan, alphaM, aDot, exDot, eyDot, iDot, raanDot, alphaMDot, - PositionAngleType.MEAN, frame, fieldDate, fieldMu); - } - - case CARTESIAN: { - final FieldPVCoordinates orbitPV = new FieldPVCoordinates<>(field, orbit.getPVCoordinates()); - - return new FieldCartesianOrbit<>(orbitPV, orbit.getFrame(), fieldDate, fieldMu); - } - - case KEPLERIAN: { - final KeplerianOrbit kepOrbit = (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(orbit); - - // Get orbital elements - final T a = one.multiply(kepOrbit.getA()); - final T e = one.multiply(kepOrbit.getE()); - final T i = one.multiply(kepOrbit.getI()); - final T raan = one.multiply(kepOrbit.getRightAscensionOfAscendingNode()); - final T pa = one.multiply(kepOrbit.getPerigeeArgument()); - final T meanAnomaly = one.multiply(kepOrbit.getMeanAnomaly()); - - // Get derivatives - final T aDot = one.multiply(kepOrbit.getADot()); - final T eDot = one.multiply(kepOrbit.getEDot()); - final T iDot = one.multiply(kepOrbit.getIDot()); - final T raanDot = one.multiply(kepOrbit.getRightAscensionOfAscendingNodeDot()); - final T paDot = one.multiply(kepOrbit.getPerigeeArgumentDot()); - final T meanAnomalyDot = one.multiply(kepOrbit.getMeanAnomalyDot()); - - return new FieldKeplerianOrbit<>(a, e, i, pa, raan, meanAnomaly, aDot, eDot, iDot, paDot, raanDot, - meanAnomalyDot, PositionAngleType.MEAN, frame, fieldDate, fieldMu); - } - case EQUINOCTIAL: { - final EquinoctialOrbit equiOrbit = (EquinoctialOrbit) OrbitType.EQUINOCTIAL.convertType(orbit); - - // Get orbital elements - final T a = one.multiply(equiOrbit.getA()); - final T ex = one.multiply(equiOrbit.getEquinoctialEx()); - final T ey = one.multiply(equiOrbit.getEquinoctialEy()); - final T hx = one.multiply(equiOrbit.getHx()); - final T hy = one.multiply(equiOrbit.getHy()); - final T lm = one.multiply(equiOrbit.getLM()); - - // Get derivatives - final T aDot = one.multiply(equiOrbit.getADot()); - final T exDot = one.multiply(equiOrbit.getEquinoctialExDot()); - final T eyDot = one.multiply(equiOrbit.getEquinoctialEyDot()); - final T hxDot = one.multiply(equiOrbit.getHxDot()); - final T hyDot = one.multiply(equiOrbit.getHyDot()); - final T lmDot = one.multiply(equiOrbit.getLMDot()); - - return new FieldEquinoctialOrbit<>(a, ex, ey, hx, hy, lm, aDot, exDot, eyDot, hxDot, hyDot, - lmDot, PositionAngleType.MEAN, frame, fieldDate, fieldMu); - } - default: - // Should never happen - throw new OrekitInternalError(null); - } - + return orbit.getType().convertToFieldOrbit(field, orbit); } /** @@ -168,7 +76,7 @@ public static > FieldMatrix fieldify(final for (int i = 0; i < rowDim; i++) { for (int j = 0; j < columnDim; j++) { - fieldMatrix.setEntry(i, j, field.getOne().multiply(matrix.getEntry(i, j))); + fieldMatrix.setEntry(i, j, field.getOne().newInstance(matrix.getEntry(i, j))); } } diff --git a/src/main/java/org/orekit/utils/IERSConventions.java b/src/main/java/org/orekit/utils/IERSConventions.java index 30ed9f5d0d..015a7d9239 100644 --- a/src/main/java/org/orekit/utils/IERSConventions.java +++ b/src/main/java/org/orekit/utils/IERSConventions.java @@ -258,7 +258,7 @@ public > T[] value(final FieldAbsoluteDate final T f = elements.getF(); final T d = elements.getD(); final T t = elements.getTC(); - final T t2 = t.multiply(t); + final T t2 = t.square(); final FieldSinCos scOmega = FastMath.sinCos(omega); final FieldSinCos sc2omega = FieldSinCos.sum(scOmega, scOmega); @@ -1387,8 +1387,8 @@ public > T[] value(final FieldAbsoluteDate // for example removing derivatives // if T was DerivativeStructure annualCache.getNeighbors(aDate).forEach(neighbor -> { - y[0] = zero.add(neighbor.getX()); - y[1] = zero.add(neighbor.getY()); + y[0] = zero.newInstance(neighbor.getX()); + y[1] = zero.newInstance(neighbor.getY()); interpolator.addSamplePoint(central.durationFrom(neighbor.getDate()).negate(), y); }); final T[] interpolated = interpolator.value(date.durationFrom(central)); // here, we introduce derivatives again (in DerivativeStructure case) diff --git a/src/main/java/org/orekit/utils/ImmutableFieldTimeStampedCache.java b/src/main/java/org/orekit/utils/ImmutableFieldTimeStampedCache.java index 532163ff4c..ec381c3b21 100644 --- a/src/main/java/org/orekit/utils/ImmutableFieldTimeStampedCache.java +++ b/src/main/java/org/orekit/utils/ImmutableFieldTimeStampedCache.java @@ -25,7 +25,7 @@ import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; import org.hipparchus.exception.LocalizedCoreFormats; -import org.hipparchus.util.FastMath; +import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitIllegalArgumentException; import org.orekit.errors.OrekitIllegalStateException; import org.orekit.errors.OrekitMessages; @@ -49,114 +49,100 @@ public class ImmutableFieldTimeStampedCache, KK extends CalculusFieldElement> implements FieldTimeStampedCache { + /** An empty immutable cache that always throws an exception on attempted access. + * @since 12.1 + */ + @SuppressWarnings("rawtypes") + private static final ImmutableFieldTimeStampedCache EMPTY_CACHE = + new EmptyFieldTimeStampedCache(); + /** * the cached data. Be careful not to modify it after the constructor, or return a reference that allows mutating this * list. */ private final List data; - /** the size list to return from {@link #getNeighbors(FieldAbsoluteDate)}. */ - private final int neighborsSize; - - /** Earliest date. - * @since 12.0 - */ - private final FieldAbsoluteDate earliestDate; - - /** Latest date. - * @since 12.0 - */ - private final FieldAbsoluteDate latestDate; + /** the maximum size list to return from {@link #getNeighbors(FieldAbsoluteDate)}. */ + private final int maxNeighborsSize; /** * Create a new cache with the given neighbors size and data. * - * @param neighborsSize the size of the list returned from {@link #getNeighbors(FieldAbsoluteDate)}. Must be less than or + * @param maxNeighborsSize the maximum size of the list returned from {@link #getNeighbors(FieldAbsoluteDate)}. Must be less than or * equal to {@code data.size()}. * @param data the backing data for this cache. The list will be copied to ensure immutability. To guarantee immutability - * the entries in {@code data} must be immutable themselves. There must be more data than {@code neighborsSize}. + * the entries in {@code data} must be immutable themselves. There must be more data than {@code maxNeighborsSize}. * - * @throws IllegalArgumentException if {@code neighborsSize > data.size()} or if {@code neighborsSize} is negative + * @throws IllegalArgumentException if {@code maxNeighborsSize > data.size()} or if {@code maxNeighborsSize} is negative */ - public ImmutableFieldTimeStampedCache(final int neighborsSize, + public ImmutableFieldTimeStampedCache(final int maxNeighborsSize, final Collection data) { // Parameter check - if (neighborsSize > data.size()) { + if (maxNeighborsSize > data.size()) { throw new OrekitIllegalArgumentException(OrekitMessages.NOT_ENOUGH_CACHED_NEIGHBORS, - data.size(), neighborsSize); + data.size(), maxNeighborsSize); } - if (neighborsSize < 1) { + if (maxNeighborsSize < 1) { throw new OrekitIllegalArgumentException(LocalizedCoreFormats.NUMBER_TOO_SMALL, - neighborsSize, 0); + maxNeighborsSize, 1); } // Assign instance variables - this.neighborsSize = neighborsSize; + this.maxNeighborsSize = maxNeighborsSize; // Sort and copy data first this.data = new ArrayList<>(data); - Collections.sort(this.data, new FieldChronologicalComparator<>()); + this.data.sort(new FieldChronologicalComparator<>()); - this.earliestDate = this.data.get(0).getDate(); - this.latestDate = this.data.get(this.data.size() - 1).getDate(); + } + /** Private constructor for {@link #EMPTY_CACHE}. + */ + private ImmutableFieldTimeStampedCache() { + this.data = null; + this.maxNeighborsSize = 0; } /** - * private constructor for {@link #EMPTY_CACHE}. - * @param field field to which the elements belong + * Get an empty immutable cache, cast to the correct type. + * + * @param the type of data + * @param the type of the calculus field element + * @param ignored field to which the elements belong + * @return an empty {@link ImmutableTimeStampedCache}. + * @deprecated as of 12.1, replaced by {@link #emptyCache()} */ - private ImmutableFieldTimeStampedCache(final Field field) { - this.data = null; - this.neighborsSize = 0; - this.earliestDate = FieldAbsoluteDate.getArbitraryEpoch(field); - this.latestDate = FieldAbsoluteDate.getArbitraryEpoch(field); + @Deprecated + public static , CFE extends CalculusFieldElement> + ImmutableFieldTimeStampedCache emptyCache(final Field ignored) { + return emptyCache(); } /** - * Get an empty immutable cache, cast to the correct type. + * Get an empty immutable cache. * * @param the type of data * @param the type of the calculus field element - * @param field field to which the elements belong * @return an empty {@link ImmutableTimeStampedCache}. + * @since 12.1 */ + @SuppressWarnings("unchecked") public static , CFE extends CalculusFieldElement> - ImmutableFieldTimeStampedCache emptyCache(final Field field) { - return new EmptyFieldTimeStampedCache<>(field); + ImmutableFieldTimeStampedCache emptyCache() { + return (ImmutableFieldTimeStampedCache) EMPTY_CACHE; } /** {@inheritDoc} */ - public Stream getNeighbors(final FieldAbsoluteDate central) { - - // Find central index - final int i = findIndex(central); - - // Check index in the range of the data - if (i < 0) { - final FieldAbsoluteDate earliest = this.getEarliest().getDate(); - throw new TimeStampedCacheException(OrekitMessages.UNABLE_TO_GENERATE_NEW_DATA_BEFORE, - earliest, central, earliest.durationFrom(central).getReal()); + public Stream getNeighbors(final FieldAbsoluteDate central, final int n) { + if (n > maxNeighborsSize) { + throw new OrekitException(OrekitMessages.NOT_ENOUGH_DATA, maxNeighborsSize); } - else if (i >= this.data.size()) { - final FieldAbsoluteDate latest = this.getLatest().getDate(); - throw new TimeStampedCacheException(OrekitMessages.UNABLE_TO_GENERATE_NEW_DATA_AFTER, - latest, central, central.durationFrom(latest).getReal()); - } - - // Force unbalanced range if necessary - int start = FastMath.max(0, i - (this.neighborsSize - 1) / 2); - final int end = FastMath.min(this.data.size(), start + - this.neighborsSize); - start = end - this.neighborsSize; - - // Return list without copying - return this.data.subList(start, end).stream(); + return new FieldSortedListTrimmer(n).getNeighborsSubList(central, data).stream(); } /** {@inheritDoc} */ - public int getNeighborsSize() { - return this.neighborsSize; + public int getMaxNeighborsSize() { + return this.maxNeighborsSize; } /** {@inheritDoc} */ @@ -185,63 +171,10 @@ public String toString() { return "Immutable cache with " + this.data.size() + " entries"; } - /** - * Find the index, i, to {@link #data} such that {@code data[i] <= t} and {@code data[i+1] > t} if {@code data[i+1]} - * exists. - * - * @param t the time - * - * @return the index of the data at or just before {@code t}, {@code -1} if {@code t} is before the first entry, or - * {@code data.size()} if {@code t} is after the last entry. - */ - private int findIndex(final FieldAbsoluteDate t) { - // left bracket of search algorithm - int iInf = 0; - KK dtInf = t.durationFrom(earliestDate); - if (dtInf.getReal() < 0) { - // before first entry - return -1; - } - - // right bracket of search algorithm - int iSup = data.size() - 1; - KK dtSup = t.durationFrom(latestDate); - if (dtSup.getReal() > 0) { - // after last entry - return data.size(); - } - - // search entries, using linear interpolation - // this should take only 2 iterations for near linear entries (most frequent use case) - // regardless of the number of entries - // this is much faster than binary search for large number of entries - while (iSup - iInf > 1) { - final int iInterp = (int) FastMath.rint(dtSup.multiply(iInf).subtract(dtInf.multiply(iSup)).divide(dtSup.subtract(dtInf)).getReal()); - final int iMed = FastMath.max(iInf + 1, FastMath.min(iInterp, iSup - 1)); - final KK dtMed = t.durationFrom(data.get(iMed).getDate()); - if (dtMed.getReal() < 0) { - iSup = iMed; - dtSup = dtMed; - } else { - iInf = iMed; - dtInf = dtMed; - } - } - - return iInf; - } - /** An empty immutable cache that always throws an exception on attempted access. */ private static class EmptyFieldTimeStampedCache, KK extends CalculusFieldElement> extends ImmutableFieldTimeStampedCache { - /** Simple constructor. - * @param field field to which elements belong - */ - EmptyFieldTimeStampedCache(final Field field) { - super(field); - } - /** {@inheritDoc} */ @Override public Stream getNeighbors(final FieldAbsoluteDate central) { @@ -250,7 +183,7 @@ public Stream getNeighbors(final FieldAbsoluteDate central) { /** {@inheritDoc} */ @Override - public int getNeighborsSize() { + public int getMaxNeighborsSize() { return 0; } diff --git a/src/main/java/org/orekit/utils/ImmutableTimeStampedCache.java b/src/main/java/org/orekit/utils/ImmutableTimeStampedCache.java index 95febb133b..10b3dc2690 100644 --- a/src/main/java/org/orekit/utils/ImmutableTimeStampedCache.java +++ b/src/main/java/org/orekit/utils/ImmutableTimeStampedCache.java @@ -23,7 +23,6 @@ import java.util.stream.Stream; import org.hipparchus.exception.LocalizedCoreFormats; -import org.hipparchus.util.FastMath; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitIllegalArgumentException; import org.orekit.errors.OrekitIllegalStateException; @@ -51,7 +50,7 @@ public class ImmutableTimeStampedCache */ @SuppressWarnings("rawtypes") private static final ImmutableTimeStampedCache EMPTY_CACHE = - new EmptyTimeStampedCache(); + new EmptyTimeStampedCache<>(); /** * the cached data. Be careful not to modify it after the constructor, or @@ -65,16 +64,6 @@ public class ImmutableTimeStampedCache */ private final int maxNeighborsSize; - /** Earliest date. - * @since 12.0 - */ - private final AbsoluteDate earliestDate; - - /** Latest date. - * @since 12.0 - */ - private final AbsoluteDate latestDate; - /** * Create a new cache with the given neighbors size and data. * @@ -97,17 +86,14 @@ public ImmutableTimeStampedCache(final int maxNeighborsSize, } if (maxNeighborsSize < 1) { throw new OrekitIllegalArgumentException(LocalizedCoreFormats.NUMBER_TOO_SMALL, - maxNeighborsSize, 0); + maxNeighborsSize, 1); } // assign instance variables this.maxNeighborsSize = maxNeighborsSize; // sort and copy data first this.data = new ArrayList<>(data); - Collections.sort(this.data, new ChronologicalComparator()); - - this.earliestDate = this.data.get(0).getDate(); - this.latestDate = this.data.get(this.data.size() - 1).getDate(); + this.data.sort(new ChronologicalComparator()); } @@ -117,91 +103,14 @@ public ImmutableTimeStampedCache(final int maxNeighborsSize, private ImmutableTimeStampedCache() { this.data = null; this.maxNeighborsSize = 0; - this.earliestDate = AbsoluteDate.ARBITRARY_EPOCH; - this.latestDate = AbsoluteDate.ARBITRARY_EPOCH; } /** {@inheritDoc} */ public Stream getNeighbors(final AbsoluteDate central, final int n) { - if (n > maxNeighborsSize) { throw new OrekitException(OrekitMessages.NOT_ENOUGH_DATA, maxNeighborsSize); } - - // find central index - final int i = findIndex(central); - - // check index in in the range of the data - if (i < 0) { - final AbsoluteDate earliest = this.getEarliest().getDate(); - throw new TimeStampedCacheException(OrekitMessages.UNABLE_TO_GENERATE_NEW_DATA_BEFORE, - earliest, central, earliest.durationFrom(central)); - } else if (i >= this.data.size()) { - final AbsoluteDate latest = this.getLatest().getDate(); - throw new TimeStampedCacheException(OrekitMessages.UNABLE_TO_GENERATE_NEW_DATA_AFTER, - latest, central, central.durationFrom(latest)); - } - - // force unbalanced range if necessary - int start = FastMath.max(0, i - (n - 1) / 2); - final int end = FastMath.min(this.data.size(), start + n); - start = end - n; - - // return list without copying - return this.data.subList(start, end).stream(); - } - - /** - * Find the index, i, to {@link #data} such that {@code data[i] <= t} and - * {@code data[i+1] > t} if {@code data[i+1]} exists. - * - * @param t the time - * @return the index of the data at or just before {@code t}, {@code -1} if - * {@code t} is before the first entry, or {@code data.size()} if - * {@code t} is after the last entry. - */ - private int findIndex(final AbsoluteDate t) { - - // left bracket of search algorithm - int iInf = 0; - double dtInf = t.durationFrom(earliestDate); - if (dtInf < 0) { - // before first entry - return -1; - } - - // right bracket of search algorithm - int iSup = data.size() - 1; - double dtSup = t.durationFrom(latestDate); - if (dtSup > 0) { - // after last entry - return data.size(); - } - - // search entries, using linear interpolation - // this should take only 2 iterations for near linear entries (most frequent use case) - // regardless of the number of entries - // this is much faster than binary search for large number of entries - while (iSup - iInf > 1) { - final int iInterp = (int) FastMath.rint((iInf * dtSup - iSup * dtInf) / (dtSup - dtInf)); - final int iMed = FastMath.max(iInf + 1, FastMath.min(iInterp, iSup - 1)); - final double dtMed = t.durationFrom(data.get(iMed).getDate()); - if (dtMed < 0) { - iSup = iMed; - dtSup = dtMed; - } else { - iInf = iMed; - dtInf = dtMed; - } - } - - // at this point data[iInf] <= t <= data[iSup], but the javadoc for this method - // says the upper bound is exclusive, so check for equality to make a half open - // interval. - if (dtSup == 0.0) { - return iSup; - } - return iInf; + return new SortedListTrimmer(n).getNeighborsSubList(central, data).stream(); } /** {@inheritDoc} */ @@ -285,7 +194,7 @@ public String toString() { * @return an empty {@link ImmutableTimeStampedCache}. */ @SuppressWarnings("unchecked") - public static final ImmutableTimeStampedCache emptyCache() { + public static ImmutableTimeStampedCache emptyCache() { return (ImmutableTimeStampedCache) EMPTY_CACHE; } diff --git a/src/main/java/org/orekit/utils/PVCoordinatesProvider.java b/src/main/java/org/orekit/utils/PVCoordinatesProvider.java index 38ce1670c5..95e419cd8f 100644 --- a/src/main/java/org/orekit/utils/PVCoordinatesProvider.java +++ b/src/main/java/org/orekit/utils/PVCoordinatesProvider.java @@ -22,7 +22,7 @@ import org.orekit.time.AbsoluteDate; /** -** Interface for PV coordinates providers. + * Interface for PV coordinates providers. * @author Veronique Pommier *

          The PV coordinates provider interface can be used by any class used for position/velocity * computation, for example celestial bodies or spacecraft position/velocity propagators, diff --git a/src/main/java/org/orekit/utils/ParameterDriver.java b/src/main/java/org/orekit/utils/ParameterDriver.java index cc51438cdd..745060aaf9 100644 --- a/src/main/java/org/orekit/utils/ParameterDriver.java +++ b/src/main/java/org/orekit/utils/ParameterDriver.java @@ -103,7 +103,7 @@ public class ParameterDriver { /** Name of the parameter.*/ - private String SPAN = "Span"; + public static final String SPAN = "Span"; /** Name of the parameter. */ private String name; diff --git a/src/main/java/org/orekit/utils/ParameterDriversProvider.java b/src/main/java/org/orekit/utils/ParameterDriversProvider.java index e1636183bf..3dd6df1d1f 100644 --- a/src/main/java/org/orekit/utils/ParameterDriversProvider.java +++ b/src/main/java/org/orekit/utils/ParameterDriversProvider.java @@ -120,7 +120,7 @@ default > T[] getParametersAllValues(final Fie int paramIndex = 0; for (int i = 0; i < drivers.size(); ++i) { for (Span span = drivers.get(i).getValueSpanMap().getFirstSpan(); span != null; span = span.next()) { - parameters[paramIndex++] = field.getZero().add(span.getData()); + parameters[paramIndex++] = field.getZero().newInstance(span.getData()); } } return parameters; @@ -139,7 +139,7 @@ default > T[] getParameters(final Field fie final List drivers = getParametersDrivers(); final T[] parameters = MathArrays.buildArray(field, drivers.size()); for (int i = 0; i < drivers.size(); ++i) { - parameters[i] = field.getZero().add(drivers.get(i).getValue()); + parameters[i] = field.getZero().newInstance(drivers.get(i).getValue()); } return parameters; } @@ -156,7 +156,7 @@ default > T[] getParameters(final Field fie final List drivers = getParametersDrivers(); final T[] parameters = MathArrays.buildArray(field, drivers.size()); for (int i = 0; i < drivers.size(); ++i) { - parameters[i] = field.getZero().add(drivers.get(i).getValue(date.toAbsoluteDate())); + parameters[i] = field.getZero().newInstance(drivers.get(i).getValue(date.toAbsoluteDate())); } return parameters; } diff --git a/src/main/java/org/orekit/utils/ShiftingPVCoordinatesProvider.java b/src/main/java/org/orekit/utils/ShiftingPVCoordinatesProvider.java new file mode 100644 index 0000000000..ba8e214e5d --- /dev/null +++ b/src/main/java/org/orekit/utils/ShiftingPVCoordinatesProvider.java @@ -0,0 +1,53 @@ +/* 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.utils; + +import org.orekit.frames.Frame; +import org.orekit.time.AbsoluteDate; + +/** Provider using simple {@link PVCoordinates#shiftedBy(double)} shiftedBy} and frame transforms for evolution. + * @author Luc Maisonobe + * @since 12.1 + */ +public class ShiftingPVCoordinatesProvider implements PVCoordinatesProvider { + + /** Reference coordinates. */ + private final TimeStampedPVCoordinates referencePV; + + /** Frame in which {@link #referencePV} is defined. */ + private final Frame referenceFrame; + + /** Simple constructor. + * @param referencePV reference coordinates + * @param referenceFrame frame in which {@code reference} is defined + */ + public ShiftingPVCoordinatesProvider(final TimeStampedPVCoordinates referencePV, + final Frame referenceFrame) { + this.referencePV = referencePV; + this.referenceFrame = referenceFrame; + } + + /** {@inheritDoc} */ + @Override + public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, + final Frame frame) { + final TimeStampedPVCoordinates shifted = referencePV.shiftedBy(date.durationFrom(referencePV)); + return referenceFrame.getTransformTo(frame, date).transformPVCoordinates(shifted); + } + +} diff --git a/src/main/java/org/orekit/utils/SortedListTrimmer.java b/src/main/java/org/orekit/utils/SortedListTrimmer.java new file mode 100644 index 0000000000..ff5d40ebe8 --- /dev/null +++ b/src/main/java/org/orekit/utils/SortedListTrimmer.java @@ -0,0 +1,161 @@ +/* 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.utils; + +import org.hipparchus.exception.LocalizedCoreFormats; +import org.hipparchus.util.FastMath; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitIllegalArgumentException; +import org.orekit.errors.OrekitMessages; +import org.orekit.errors.TimeStampedCacheException; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.TimeStamped; + +import java.util.List; + +/** A trimmer for externally stored chronologically sorted lists. + * + * @author Evan Ward + * @since 12.1 + */ +public class SortedListTrimmer { + + /** Size of the list to return from {@link #getNeighborsSubList(AbsoluteDate, List)}. */ + private final int neighborsSize; + + /** Create a new trimmer with the given neighbors size. + * @param neighborsSize size of the list returned from {@link #getNeighborsSubList(AbsoluteDate, List)} + */ + public SortedListTrimmer(final int neighborsSize) { + if (neighborsSize < 1) { + throw new OrekitIllegalArgumentException(LocalizedCoreFormats.NUMBER_TOO_SMALL, + neighborsSize, 1); + } + // assign instance variables + this.neighborsSize = neighborsSize; + } + + /** Get size of the list returned from {@link #getNeighborsSubList(AbsoluteDate, List)}. + * @return size of the list returned from {@link #getNeighborsSubList(AbsoluteDate, List)} + */ + public int getNeighborsSize() { + return neighborsSize; + } + + /** Get the entries surrounding a central date. + *

          + * If the central date is well within covered range, the returned array will + * be balanced with half the points before central date and half the points + * after it (depending on n parity, of course). If the central date is near + * the boundary, then the returned array will be unbalanced and will contain + * only the n earliest (or latest) entries. A typical example of the later + * case is leap seconds cache, since the number of leap seconds cannot be + * arbitrarily increased. + *

          + * @param the type of data + * @param central central date + * @param data complete list of entries (must be chronologically sorted) + * @return entries surrounding the specified date (sublist of {@code data}) + */ + public List getNeighborsSubList(final AbsoluteDate central, final List data) { + + if (neighborsSize > data.size()) { + throw new OrekitException(OrekitMessages.NOT_ENOUGH_DATA, data.size()); + } + + // find central index + final int i = findIndex(central, data); + + // check index in in the range of the data + if (i < 0) { + final AbsoluteDate earliest = data.get(0).getDate(); + throw new TimeStampedCacheException(OrekitMessages.UNABLE_TO_GENERATE_NEW_DATA_BEFORE, + earliest, central, earliest.durationFrom(central)); + } else if (i >= data.size()) { + final AbsoluteDate latest = data.get(data.size() - 1).getDate(); + throw new TimeStampedCacheException(OrekitMessages.UNABLE_TO_GENERATE_NEW_DATA_AFTER, + latest, central, central.durationFrom(latest)); + } + + // force unbalanced range if necessary + int start = FastMath.max(0, i - (neighborsSize - 1) / 2); + final int end = FastMath.min(data.size(), start + neighborsSize); + start = end - neighborsSize; + + // return list without copying + return data.subList(start, end); + + } + + /** + * Find the index, i, to {@code data} such that {@code data[i] <= t} and + * {@code data[i+1] > t} if {@code data[i+1]} exists. + * + * @param the type of data + * @param t the time + * @param data complete list of entries (must be chronologically sorted) + * @return the index of the data at or just before {@code t}, {@code -1} if + * {@code t} is before the first entry, or {@code data.size()} if + * {@code t} is after the last entry. + */ + private int findIndex(final AbsoluteDate t, final List data) { + + // left bracket of search algorithm + int iInf = 0; + double dtInf = t.durationFrom(data.get(0)); + if (dtInf < 0) { + // before first entry + return -1; + } + + // right bracket of search algorithm + int iSup = data.size() - 1; + double dtSup = t.durationFrom(data.get(data.size() - 1)); + if (dtSup > 0) { + // after last entry + return data.size(); + } + + // search entries, using linear interpolation + // this should take only 2 iterations for near linear entries (most frequent use case) + // regardless of the number of entries + // this is much faster than binary search for large number of entries + while (iSup - iInf > 1) { + final int iInterp = (int) FastMath.rint((iInf * dtSup - iSup * dtInf) / (dtSup - dtInf)); + final int iMed = FastMath.max(iInf + 1, FastMath.min(iInterp, iSup - 1)); + final double dtMed = t.durationFrom(data.get(iMed).getDate()); + if (dtMed < 0) { + iSup = iMed; + dtSup = dtMed; + } else { + iInf = iMed; + dtInf = dtMed; + } + } + + // at this point data[iInf] <= t <= data[iSup], but the javadoc for this method + // says the upper bound is exclusive, so check for equality to make a half open + // interval. + if (dtSup == 0.0) { + return iSup; + } + + return iInf; + + } + +} diff --git a/src/main/java/org/orekit/utils/TimeSpanMap.java b/src/main/java/org/orekit/utils/TimeSpanMap.java index 9ae5f2967d..34a762bb9f 100644 --- a/src/main/java/org/orekit/utils/TimeSpanMap.java +++ b/src/main/java/org/orekit/utils/TimeSpanMap.java @@ -18,6 +18,8 @@ import java.util.function.Consumer; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; import org.orekit.time.AbsoluteDate; import org.orekit.time.TimeStamped; @@ -37,9 +39,7 @@ * example an orbit count that changes at ascending nodes (in which case the * entry would be an {@link Integer}), or a visibility status between several * objects (in which case the entry would be a {@link Boolean}) or a drag - * coefficient that is expected to be estimated daily or three-hourly (this is - * how {@link org.orekit.forces.drag.TimeSpanDragForce TimeSpanDragForce} is - * implemented). + * coefficient that is expected to be estimated daily or three-hourly. *

          *

          * Time span maps are built progressively. At first, they contain one @@ -413,6 +413,21 @@ public synchronized Span getFirstSpan() { return span; } + /** Get the first (earliest) span with non-null data. + * @return first (earliest) span with non-null data + * @since 12.1 + */ + public synchronized Span getFirstNonNullSpan() { + Span span = getFirstSpan(); + while (span.getData() == null) { + if (span.getEndTransition() == null) { + throw new OrekitException(OrekitMessages.NO_CACHED_ENTRIES); + } + span = span.next(); + } + return span; + } + /** Get the last (latest) span. * @return last (latest) span * @since 11.1 @@ -425,6 +440,21 @@ public synchronized Span getLastSpan() { return span; } + /** Get the last (latest) span with non-null data. + * @return last (latest) span with non-null data + * @since 12.1 + */ + public synchronized Span getLastNonNullSpan() { + Span span = getLastSpan(); + while (span.getData() == null) { + if (span.getStartTransition() == null) { + throw new OrekitException(OrekitMessages.NO_CACHED_ENTRIES); + } + span = span.previous(); + } + return span; + } + /** Extract a range of the map. *

          * The object returned will be a new independent instance that will contain diff --git a/src/main/java/org/orekit/utils/TimeStampedFieldAngularCoordinates.java b/src/main/java/org/orekit/utils/TimeStampedFieldAngularCoordinates.java index cab7267f4b..247786fd0c 100644 --- a/src/main/java/org/orekit/utils/TimeStampedFieldAngularCoordinates.java +++ b/src/main/java/org/orekit/utils/TimeStampedFieldAngularCoordinates.java @@ -191,7 +191,7 @@ public FieldAbsoluteDate getDate() { * @return a new state, shifted with respect to the instance (which is immutable) */ public TimeStampedFieldAngularCoordinates shiftedBy(final double dt) { - return shiftedBy(getDate().getField().getZero().add(dt)); + return shiftedBy(getDate().getField().getZero().newInstance(dt)); } /** Get a time-shifted state. diff --git a/src/main/java/org/orekit/utils/TimeStampedFieldAngularCoordinatesHermiteInterpolator.java b/src/main/java/org/orekit/utils/TimeStampedFieldAngularCoordinatesHermiteInterpolator.java index f01b15d8d9..39c30cb80e 100644 --- a/src/main/java/org/orekit/utils/TimeStampedFieldAngularCoordinatesHermiteInterpolator.java +++ b/src/main/java/org/orekit/utils/TimeStampedFieldAngularCoordinatesHermiteInterpolator.java @@ -246,7 +246,7 @@ protected TimeStampedFieldAngularCoordinates interpolate(final Interpolation // we need to offset all rotations to avoid the singularity offset = offset.addOffset( new FieldAngularCoordinates<>(new FieldRotation<>(FieldVector3D.getPlusI(field), - one.multiply(epsilon), + one.newInstance(epsilon), RotationConvention.VECTOR_OPERATOR), FieldVector3D.getZero(field), FieldVector3D.getZero(field))); } else { diff --git a/src/main/java/org/orekit/utils/units/Unit.java b/src/main/java/org/orekit/utils/units/Unit.java index 3fcdf0eb66..9df9be55f0 100644 --- a/src/main/java/org/orekit/utils/units/Unit.java +++ b/src/main/java/org/orekit/utils/units/Unit.java @@ -19,6 +19,7 @@ import java.io.Serializable; import java.util.List; +import org.hipparchus.CalculusFieldElement; import org.hipparchus.fraction.Fraction; import org.hipparchus.util.FastMath; import org.hipparchus.util.Precision; @@ -417,6 +418,16 @@ public double toSI(final Double value) { return value == null ? Double.NaN : value.doubleValue() * scale; } + /** Convert a value to SI units. + * @param type of the field elements + * @param value value instance unit + * @return value in SI units + * @since 12.1 + */ + public > T toSI(final T value) { + return value.multiply(scale); + } + /** Convert a value from SI units. * @param value value SI unit * @return value in instance units @@ -433,6 +444,15 @@ public double fromSI(final Double value) { return value == null ? Double.NaN : value.doubleValue() / scale; } + /** Convert a value from SI units. + * @param type of the field elements + * @param value value SI unit + * @return value in instance units + */ + public > T fromSI(final T value) { + return value.divide(scale); + } + /** Parse a unit. *

          * The grammar for unit specification allows chains units multiplication and diff --git a/src/main/java/org/orekit/utils/units/UnitsCache.java b/src/main/java/org/orekit/utils/units/UnitsCache.java index 42345596de..76728d69be 100644 --- a/src/main/java/org/orekit/utils/units/UnitsCache.java +++ b/src/main/java/org/orekit/utils/units/UnitsCache.java @@ -50,13 +50,7 @@ public Unit getUnits(final String specification) { return Unit.NONE; } - Unit cached = cache.get(specification); - if (cached == null) { - cached = Unit.parse(specification); - cache.put(specification, cached); - } - - return cached; + 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 new file mode 100644 index 0000000000..56517df3ec --- /dev/null +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_ca.utf8 @@ -0,0 +1,889 @@ +# internal error, please notify development team by creating a new topic at {0} +INTERNAL_ERROR = error intern, si us plau, informi del problem crant un nou tema {0} + +# altitude ({0} m) is below the {1} m allowed threshold +ALTITUDE_BELOW_ALLOWED_THRESHOLD = altitud ({0} m) inferior al llindar permès {1} m + +# point is inside ellipsoid +POINT_INSIDE_ELLIPSOID = el punt es troba dins de l''el·lipsoide + +# trajectory inside the Brillouin sphere (r = {0}) +TRAJECTORY_INSIDE_BRILLOUIN_SPHERE = trajectòria dins l''esfera de Brillouin (r = {0}) + +# almost equatorial orbit (i = {0} degrees) +ALMOST_EQUATORIAL_ORBIT = òrbita quasi equatorial (i = {0} grados) + +# almost critically inclined orbit (i = {0} degrees) +ALMOST_CRITICALLY_INCLINED_ORBIT = òrbita amb inclinació quasi crítica (i = {0} graus) + +# unable to compute Eckstein-Hechler mean parameters after {0} iterations +UNABLE_TO_COMPUTE_ECKSTEIN_HECHLER_MEAN_PARAMETERS = després de {0} iteracions els paramètres mitjans de Eckstein-Hechler no s''han aconseguit calcular + +# unable to compute Brouwer-Lyddane mean parameters after {0} iterations +UNABLE_TO_COMPUTE_BROUWER_LYDDANE_MEAN_PARAMETERS = després de {0} iteracions els paramètres mitjans de Brouwer-Lyddane no s''han aconseguit calcular + +# unable to compute TLE after {0} iterations +UNABLE_TO_COMPUTE_TLE = després de {0} iteracions el TLE no s''ha aconseguit calcular + +# null parent for frame {0} +NULL_PARENT_FOR_FRAME = sistema de referencia {0} orfe en l'arbre de sistemes de referencia + +# frame {0} is already attached to frame {1} +FRAME_ALREADY_ATTACHED = el sistema de referencia {0} ja está lligat al sistema de referencia {1} + +# frame {0} is not attached to the main frames tree +FRAME_NOT_ATTACHED = el sistema de referencia {0} encara no está lligat a l''arbre principal de sistemas de referencia + +# frame {0} is an ancestor of both frames {1} and {2} +FRAME_ANCESTOR_OF_BOTH_FRAMES = el sistema de referencia {0} és avantpassat de ambdós sistemes de referencia {1} y {2} + +# frame {0} is an ancestor of neither frame {1} nor {2} +FRAME_ANCESTOR_OF_NEITHER_FRAME = el sistema de referencia {0} no és avantpassat ni del sistema de referencia {1} ni del sistema de referencia {2} + +# frame {0} has depth {1}, it cannot have an ancestor {2} levels above +FRAME_NO_NTH_ANCESTOR = el sistema de referencia {0} está a una profunditat {1}, no pot tenir un avantpassat {2} nivells per sobre + +# ITRF frame {0} not found +NO_SUCH_ITRF_FRAME = no s''ha trobat el sistema de referencia ITRF {0} + +# unsupported local orbital frame {0} +UNSUPPORTED_LOCAL_ORBITAL_FRAME = sistema de referencia orbital local no reconegut {0} + +# non pseudo-inertial frame "{0}" +NON_PSEUDO_INERTIAL_FRAME = sistema de referencia no pseudo-inercial "{0}" + +# data root directory {0} does not exist +DATA_ROOT_DIRECTORY_DOES_NOT_EXIST = repertori de dades arrel {0} inexistent + +# {0} is not a directory +NOT_A_DIRECTORY = {0} no és un repertori + +# {0} is neither a directory nor a zip/jar archive file +NEITHER_DIRECTORY_NOR_ZIP_OR_JAR = {0} no és ni un repertori ni un fitxer de arxiu zip/jar + +# unable to find resource {0} in classpath +UNABLE_TO_FIND_RESOURCE = {0} no existeix en el "classpath" + +# no Earth Orientation Parameters loaded +NO_EARTH_ORIENTATION_PARAMETERS_LOADED = no s''han carregat els paràmetres d''orientació de la Terra + +# missing Earth Orientation Parameters between {0} and {1} +MISSING_EARTH_ORIENTATION_PARAMETERS_BETWEEN_DATES = falten paràmetred d''orientació de la Terra entre {0} i {1} + +# missing Earth Orientation Parameters between {0} and {1}, gap is {2,number,0.0##############E0} s +MISSING_EARTH_ORIENTATION_PARAMETERS_BETWEEN_DATES_GAP = falten paràmetred d''orientació de la Terra entre {0} y {1}, la diferència es {2,number,0.0##############E0} s + +# missing Earth Orientation Parameters +NO_EARTH_ORIENTATION_PARAMETERS = falten paràmetred d''orientació de la Terra + +# file {0} is not a supported IERS data file +NOT_A_SUPPORTED_IERS_DATA_FILE = el fitxer {0} no és un fitxer de dades IERS reconegut + +# inconsistent dates in IERS file {0}: {1}-{2}-{3} and MJD {4} +INCONSISTENT_DATES_IN_IERS_FILE = dates {1}-{2}-{3} y MJD {4} incoherents en el fitxer IERS {0} + +# unexpected data after line {0} in file {1}: {2} +UNEXPECTED_DATA_AFTER_LINE_IN_FILE = dades imprevistes en la linea {0} del fitxer {1} : {2} + +# unexpected data at line {0} in file {1} +UNEXPECTED_DATA_AT_LINE_IN_FILE = dades imprevistes en la linea {0} del fitxer {1} + +# non-chronological dates in file {0}, line {1} +NON_CHRONOLOGICAL_DATES_IN_FILE = dates no cronològiques en el fitxer {0} en la linea {1} + +# inconsistent sampling date: expected {0} but got {1} +INCONSISTENT_SAMPLING_DATE = dates de les mostres incoherents: s''esperava {0} , però s''ha donat {1} + +# no IERS UTC-TAI history data loaded +NO_IERS_UTC_TAI_HISTORY_DATA_LOADED = les dades històriques IERSD UTC-TAI no estan carregades + +# no entries found in IERS UTC-TAI history file {0} +NO_ENTRIES_IN_IERS_UTC_TAI_HISTORY_FILE = no es troben dades en el fitxer històric UTC-TAI del IERS {0} + +# missing serie j = {0} in file {1} (line {2}) +MISSING_SERIE_J_IN_FILE = falta la sèrie j = {0} en el fitxer {1} (linea {2}) + +# cannot parse both τ and γ from the same Poissons series file +CANNOT_PARSE_BOTH_TAU_AND_GAMMA = no se pueden llegir a la vegada τ i γ de un mateix fitxer de sèrie de Poisson + +# unexpected end of file {0} (after line {1}) +UNEXPECTED_END_OF_FILE_AFTER_LINE = fi imprevista del fitxer {0} (després de la linea {1}) + +# unable to parse line {0} of file {1}:\n{2} +UNABLE_TO_PARSE_LINE_IN_FILE = impossible d''analitzar la linea {0} del fitxer {1} :\n{2} + +# unable to parse element {0} at line {1}, file {2} +UNABLE_TO_PARSE_ELEMENT_IN_FILE = impossible d''analizar l''element {0} de la linea {1} del fitxer {2} + +# unable to find file {0} +UNABLE_TO_FIND_FILE = impossible de trobar el fitxer {0} + +# positive flow rate (q: {0}) +POSITIVE_FLOW_RATE = flux de massa positiu (q : {0}) + +# no gravity field data loaded +NO_GRAVITY_FIELD_DATA_LOADED = les dades del camp gravitacional no estan carregades + +# gravity field normalization underflow for degree {0} and order {1} +GRAVITY_FIELD_NORMALIZATION_UNDERFLOW = desbordament aritmètic en la normalització d''un camp gravitacional pel grau {0} i l''ordre {1} + +# no ocean tide data loaded +NO_OCEAN_TIDE_DATA_LOADED = les dades de les marees oceàniques no estan carregades + +# ocean tide data file {0} limited to degree {1} and order {2} +OCEAN_TIDE_DATA_DEGREE_ORDER_LIMITS = el fitxer de dades de les marees oceàniques {0} està limitat al grau {1} i l''ordre {2} + +# load deformation coefficients limited to degree {0}, cannot parse degree {1} term from file {2} +OCEAN_TIDE_LOAD_DEFORMATION_LIMITS = els coeficient de deformació sota càrrega estan limitats a grau {0}, no es poden llegir termes de grado {1} en el fitxer {2} + +# polar trajectory (distance to polar axis: {0}) +POLAR_TRAJECTORY = trajectòria polar (distància al eix dels pols: {0}) + +# unexpected format error for file {0} with loader {1} +UNEXPECTED_FILE_FORMAT_ERROR_FOR_LOADER = error de format imprevist pel fitxer {0} al carregar-lo amb {1} + +# duplicated gravity field coefficient {0}({1}, {2}) in file {3} +DUPLICATED_GRAVITY_FIELD_COEFFICIENT_IN_FILE = coeficient {0}({1}, {2}) de camp gravitacional duplicat en el fitxer {3} + +# missing gravity field coefficient {0}({1}, {2}) in file {3} +MISSING_GRAVITY_FIELD_COEFFICIENT_IN_FILE = falta el coeficient {0}({1}, {2}) del camp gravitacional en el fitxer {3} + +# too large degree (n = {0}, potential maximal degree is {1}) +TOO_LARGE_DEGREE_FOR_GRAVITY_FIELD = grau massa alt (n = {0}, el grau de potencial màxim és {1}) + +# too large order (m = {0}, potential maximal order is {1}) +TOO_LARGE_ORDER_FOR_GRAVITY_FIELD = ordre massa alt (n = {0}, l''ordre de potencial màxim és {1}) + +# no term ({0}, {1}) in a {2}x{3} spherical harmonics decomposition +WRONG_DEGREE_OR_ORDER = cap terme ({0}, {1}) en una descomposició armònica esfèrica de {2}x{3} + +# wrong orbital elements for averaging theory +WRONG_ELEMENTS_FOR_AVERAGING_THEORY = + +# several reference dates ({0} and {1} differ by {3,number,0.0##############E0} s) found in gravity field file {2} +SEVERAL_REFERENCE_DATES_IN_GRAVITY_FIELD = s''han trobat varies dates de referència ({0} i {1} difereixen de {3,number,0.0##############E0} s) en el fitxer de camp gravitacional {2} + +# no TLE data available for object {0} +NO_TLE_FOR_OBJECT = no hi ha dades TLE disponibles per l''objecte {0} + +# no TLE data available for launch year {0}, launch number {1}, launch piece {2} +NO_TLE_FOR_LAUNCH_YEAR_NUMBER_PIECE = no hi ha dades TLE disponibles per l''any de llançament {0}, número de llançament {1}, objecte llançat {2} + +# lines {0} and {1} are not TLE lines:\n{0}: "{2}"\n{1}: "{3}" +NOT_TLE_LINES = les lineas {0} i {1} no són lineas TLE:\n{0}: "{2}"\n{1}: "{3}" + +# expected a second TLE line after line {0}:\n{0}: "{1}" +MISSING_SECOND_TLE_LINE = esperava una segona linea TLE després de la linea {0}:\n{0}: "{1}" + +# TLE lines do not refer to the same object:\n{0}\n{1} +TLE_LINES_DO_NOT_REFER_TO_SAME_OBJECT = les lineas TLE no es refereixen al mateix objecte:\n{0}\n{1} + +# invalid TLE parameter for object {0}: {1} = {2} +TLE_INVALID_PARAMETER = parámetre del TLE invàlid per l''objecte {0}: {1} = {2} + +# wrong checksum of TLE line {0}, expected {1} but got {2} ({3}) +TLE_CHECKSUM_ERROR = suma de control incorrecta per la linea TLE {0}, en comptes del valor esperat {1} s''obté {2} ({3}) + +# no TLE data available +NO_TLE_DATA_AVAILABLE = no hi ha dades TLE disponibles + +# spacecraft mass is not positive: {0} kg +NOT_POSITIVE_SPACECRAFT_MASS = la massa del vehícle espacial no és positiva: {0} kg + +# too large eccentricity for propagation model: e = {0} +TOO_LARGE_ECCENTRICITY_FOR_PROPAGATION_MODEL = l''excentricitat és massa alta pel model de propagació (e : {0}) + +# no solar activity available at {0}, data available only in range [{1}, {2}] +NO_SOLAR_ACTIVITY_AT_DATE = no hi ha paràmetres d''activitat solar disponibles el {0}, dades disponibles únicament per l''interval [{1}, {2}] + +# non-existent month {0} +NON_EXISTENT_MONTH = mes inexistent {0} + +# non-existent date {0}-{1}-{2} +NON_EXISTENT_YEAR_MONTH_DAY = data inexistent {0}-{1}-{2} + +# non-existent week date {0}-W{1}-{2} +NON_EXISTENT_WEEK_DATE = data de la semana inexistent {0}-W{1}-{2} + +# non-existent date {0} +NON_EXISTENT_DATE = fecha inexistente {0} + +# no day number {0} in year {1} +NON_EXISTENT_DAY_NUMBER_IN_YEAR = no hi ha número del dia {0} en l''any {1} + +# non-existent time {0}:{1}:{2} +NON_EXISTENT_HMS_TIME = hora inexistent {0}:{1}:{2} + +# non-existent time {0} +NON_EXISTENT_TIME = hora inexistent {0} + +# out of range seconds number: {0} +OUT_OF_RANGE_SECONDS_NUMBER = número de segons fora de rang : {0} + +# out of range seconds number: {0} is not in [{1}, {2}] +OUT_OF_RANGE_SECONDS_NUMBER_DETAIL = número de segoms fora de rang : {0} no es troba entre [{1}, {2}] + +# angle type not supported, supported angles: {0}, {1} and {2} +ANGLE_TYPE_NOT_SUPPORTED = tipus d''angle no reconegut, angles reconeguts: {0}, {1} i {2} + +# satellite collided with target +SATELLITE_COLLIDED_WITH_TARGET = el satèl·lit col·lisiona contra el seu objectiu + +# attitude pointing law misses ground +ATTITUDE_POINTING_LAW_DOES_NOT_POINT_TO_GROUND = la llei d''actitud no apunta cap al terra + +# {0} seconds transition time for attitudes switch is too short, should be longer than {1} seconds +TOO_SHORT_TRANSITION_TIME_FOR_ATTITUDES_SWITCH = {0} segons per una transició entre actituds és poc temps, es necesitarien al menys {1} segons + +# orbit date ({0}) does not match attitude date ({1}) +ORBIT_AND_ATTITUDE_DATES_MISMATCH = la data de la òrbita({0}) no és coherent amb la data de l''actitud ({1}) + +# frame ({0}) does not match frame ({1}) +FRAMES_MISMATCH = el sistema de referència ({0}) no correspon al sistema de referència ({1}) + +# initial state not specified for orbit propagation +INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION = estat inicial no especificat per l''extrapolació d''òrbita + +# 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 + +# 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} + +# inconsistent values of astronomical unit in JPL ephemerides files: ({0} and {1}) +INCONSISTENT_ASTRONOMICAL_UNIT_IN_FILES = valors incoherents de l''unitat astronòmica en els fitxers d''efemèrides del JPL : ({0} y {1}) + +# inconsistent values of Earth/Moon mass ratio in JPL ephemerides files: ({0} and {1}) +INCONSISTENT_EARTH_MOON_RATIO_IN_FILES = valors incoherents de la relació de massa Terra/Lluna en els fitxers d''efemèrides del JPL : ({0} y {1}) + +# no data loaded for celestial body {0} +NO_DATA_LOADED_FOR_CELESTIAL_BODY = no hi ha dades carregades pel cos celestial {0} + +# file {0} is not a JPL ephemerides binary file +NOT_A_JPL_EPHEMERIDES_BINARY_FILE = el fitxer {0} no és un fitxer d''efemèrides binari del JPL + +# file {0} is not a Marshall Solar Activity Future Estimation (MSAFE) file +NOT_A_MARSHALL_SOLAR_ACTIVITY_FUTURE_ESTIMATION_FILE = el fitxer {0} no és un fitxer d''Estimació d''Activitat Solar Futura Marshall (MSAFE) + +# no JPL ephemerides binary files found +NO_JPL_EPHEMERIDES_BINARY_FILES_FOUND = no s''ha trobat cap fitxer binari de les efemèrides JPL + +# out of range date for {0} ephemerides: {1} +OUT_OF_RANGE_BODY_EPHEMERIDES_DATE = data fora del rang de validesa de les efemèrides « {0} » : {1} + +# out of range date: {0}, [{1}, {2}] +OUT_OF_RANGE_DATE = data fora del rang de validesa : {0}, [{1}, {2}] + +# out of range date for ephemerides: {0} is {3,number,0.0##############E0} s before [{1}, {2}] +OUT_OF_RANGE_EPHEMERIDES_DATE_BEFORE = data fora del rang de validesa de les efemèrides: {0} és {3,number,0.0##############E0} segons anterior a [{1}, {2}] + +# out of range date for ephemerides: {0} is {3,number,0.0##############E0} s after [{1}, {2}] +OUT_OF_RANGE_EPHEMERIDES_DATE_AFTER = data fora del rang de validesa de les efemèrides: {0} es {3,number,0.0##############E0} segons posterior a [{1}, {2}] + +# unexpected two elevation values: {0} and {1}, for one azimuth: {2} +UNEXPECTED_TWO_ELEVATION_VALUES_FOR_ONE_AZIMUTH = dades inesperades, dos valors d''elevació: {0} i {1}, per un azimut : {2} + +# unsupported parameter name {0}, supported names: {1} +UNSUPPORTED_PARAMETER_NAME = nombre de paràmetre no conegut {0}, paràmetre conegut {1} + +# {0} parameter contains several span in its value TimeSpanMap, the {1} method must be called +PARAMETER_WITH_SEVERAL_ESTIMATED_VALUES = {0} paràmetre conté varis intervals en el seu valor TimeSpanMap, és necessari utiitzar {1} + +# setPeriod was already called once on {0} parameter, another parameter should be created if the periods have to be changed +PARAMETER_PERIODS_HAS_ALREADY_BEEN_SET = la funció setPeriod ja ha sigut utilitzada pel paràmetre {0}, s''ha de crear un nou paràmetre + +# scale factor for parameter {0} is too small: {1} +TOO_SMALL_SCALE_FOR_PARAMETER = el factor d''escala pel paràmetre {0} és massa petit: {1} + +# unknown additional state "{0}" +UNKNOWN_ADDITIONAL_STATE = estat adicional desconegut "{0}" + +# unknown month "{0}" +UNKNOWN_MONTH = mes desconegut "{0}" + +# Jacobian matrix for type {0} is singular with current orbit +SINGULAR_JACOBIAN_FOR_ORBIT_TYPE = la matriu jacobiana associada al tipus {0} és singular per l''òrbita actual + +# state Jacobian has not been initialized yet +STATE_JACOBIAN_NOT_INITIALIZED = la matriu jacobiana de l''estat no ha estat encara inicialitzada + +# state Jacobian is a {0}x{1} matrix, it should be a 6x6 matrix +STATE_JACOBIAN_NOT_6X6 = la matriu jacobiana de l''estat és una matriu {0}x{1}, i hauria de ser una matriu 6x6 + +# state Jacobian has {0} rows but parameters Jacobian has {1} rows +STATE_AND_PARAMETERS_JACOBIANS_ROWS_MISMATCH = la matriu jacobiana de l''estat té {0} lineas mentres que la matriu jacobiana de paràmetres en té {1} + +# initial Jacobian matrix has {0} columns, but {1} parameters have been selected +INITIAL_MATRIX_AND_PARAMETERS_NUMBER_MISMATCH = la matriu jacobiana inicial té {0} columnas, mentres que {1} paràmetres han estat seleccionats + +# orbit should be either elliptic with a > 0 and e < 1 or hyperbolic with a < 0 and e > 1, a = {0}, e = {1} +ORBIT_A_E_MISMATCH_WITH_CONIC_TYPE = l''òrbita hauria de ser el·líptica amb a > 0 i e < 1 o, més aviat, hiperbòlica amb a < 0 i e > 1, a = {0}, e = {1} + +# true anomaly {0} out of hyperbolic range (e = {1}, {2} < v < {3}) +ORBIT_ANOMALY_OUT_OF_HYPERBOLIC_RANGE = l''anomalia verdadera {0} està fora del rang per l''òrbita hiperbòlica (e = {1}, {2} < v < {3}) + +# hyperbolic orbits cannot be handled as {0} instances +HYPERBOLIC_ORBIT_NOT_HANDLED_AS = les òrbites hiperbòliques no poden ser casos de {0} + +# invalid preamble field in CCSDS date: {0} +CCSDS_DATE_INVALID_PREAMBLE_FIELD = camp de preàmbul invàlid en una data CCSDS : {0} + +# invalid time field length in CCSDS date: {0}, expected {1} +CCSDS_DATE_INVALID_LENGTH_TIME_FIELD = longitud invàlidq pel camp data en una data CCSDS : {0} en comptes del valor esperat {1} + +# missing agency epoch in CCSDS date +CCSDS_DATE_MISSING_AGENCY_EPOCH = falta agencia de l''època de referència específica en una data CCSDS + +# missing mandatory key {0} in CCSDS file {1} +CCSDS_MISSING_KEYWORD = falta paraula clau {0} en el fitxer CCSDS {1} + +# key {0} is not allowed in format version {1} +CCSDS_KEYWORD_NOT_ALLOWED_IN_VERSION = la paraula clau {0} no està permesa en la versió {1} del format + +# unexpected keyword in CCSDS line number {0} of file {1}:\n{2} +CCSDS_UNEXPECTED_KEYWORD = paraula clau inesperada en la linea {0} del fitxer CCSDS {1}:\n{2} + +# the central body gravitational coefficient cannot be retrieved from the ODM +CCSDS_UNKNOWN_GM = el coeficient gravitacional del cos central no es pot recuperar del ODM + +# there is no spacecraft mass associated with this ODM file +CCSDS_UNKNOWN_SPACECRAFT_MASS = no hi ha massa de satèl·lit asociada a aquest fitxer ODM + +# no IERS conventions have been set before parsing +CCSDS_UNKNOWN_CONVENTIONS = no s''ha inicialitzat cap convenció IERS abans de la lectura + +# frame {0} is not valid in this CCSDS file context +CCSDS_INVALID_FRAME = el sistema de referència {0} no és vàlid en aquest context de fitxer CCSDS + +# this LVLH local orbital frame uses a different definition, please use LVLH_CCSDS instead +CCSDS_DIFFERENT_LVLH_DEFINITION = aquest punt de referència local LVLH utilitza una definició diferent, utilitzi el punt de referència LVLH_CCSDS en el seu lloc + +# inconsistent time systems: {0} ≠ {1} +CCSDS_INCONSISTENT_TIME_SYSTEMS = sistemes temporals inconsistents: {0} ≠ {1} + +# No CCSDS TDM keyword was found at line {0} of file {1}:\n{2} +CCSDS_TDM_KEYWORD_NOT_FOUND = no s''ha trobat la paraula clau per CCSDS TDM en la linea {0} del fitxer {1}:\n{2} + +# no Range Units converter configured for parsing Tracking Data Message +CCSDS_TDM_MISSING_RANGE_UNITS_CONVERTER = no s''ha trobat cap convertidor d''unitats de pseudo-distància configurat per la lectura dels missatges de seguimient de dades (« Tracking Data Message ») + +# Time system should have already been set before line {0} of file {1} +CCSDS_TIME_SYSTEM_NOT_READ_YET = el sistema temporal hauria d''haver sigut inicialitzat abans de la linea {0} del fitxer {1} + +# cannot estimate precession without proper derivatives +CANNOT_ESTIMATE_PRECESSION_WITHOUT_PROPER_DERIVATIVES = impossible d''estimar la precisió sense les derivades correctes + +# name "{0}" is already used for an additional state +ADDITIONAL_STATE_NAME_ALREADY_IN_USE = el nombre "{0}" ja s''està utilitzant per un estat adicional + +# reset state not allowed +NON_RESETABLE_STATE = reinicialització de l''estat no autoritzada + +# Cannot compute Newcomb operators for sigma > rho ({0} > {1}) +DSST_NEWCOMB_OPERATORS_COMPUTATION = no es pueden calcular els operadors de Newcomb amb sigma ({0}) > rho ({1}) + +# Cannot compute the Vmns coefficient with m > n ({0} > {1}) +DSST_VMNS_COEFFICIENT_ERROR_MS = no es pueden calcular els coeficients Vmns amb m > n ({0} > {1}) + +# inconsistent shadow computation: entry = {0} whereas exit = {1} +DSST_SPR_SHADOW_INCONSISTENT = càlcul de sombra incoherent : entrada = {0} mentres que sortida = {1} + +# The current orbit has an eccentricity ({0} > 0.5). DSST needs an unimplemented time dependent numerical method to compute the averaged rates +DSST_ECC_NO_NUMERICAL_AVERAGING_METHOD = l''òrbita té una excentricitat ({0} > 0.5). DSST necessita per calcular les derivades mesurades un mètode numèric dependent del temps que no està implementat + +# unsupported sp3 file version {0} +SP3_UNSUPPORTED_VERSION = versió de format sp3 {0} no reconeguda + +# invalid header entry {0} "{1}" in file {2} (format version {3}) +SP3_INVALID_HEADER_ENTRY = Entrada {0} "{1}" de la capçalera invàlida dins del fitxer {2} (versió del format {3}) + +# version "{0}" supports only up to {1} satellites, found {2} in file {3} +SP3_TOO_MANY_SATELLITES_FOR_VERSION = La versió "{0}" accepta fins a {1} satèl·lits, però se n’’han trobat {2} dins el fitxer {3} + +# found {0} epochs in file {1}, expected {2} +SP3_NUMBER_OF_EPOCH_MISMATCH = s''han trobat {0} èpoques en el fitxer {1} en comptes de {2} + +# cannot splice sp3 files with incompatible metadata +SP3_INCOMPATIBLE_FILE_METADATA = impossible d''adjuntar els fitxers sp3 tenint metadata diferent + +# cannot splice sp3 files with incompatible satellite metadata for satellite {0} +SP3_INCOMPATIBLE_SATELLITE_MEDATADA = impossible d''adjuntar els fitxers sp3 tenint metadata del satèl·lit {0} diferent + +# frame {0} not allowed here +FRAME_NOT_ALLOWED = + +# STK coordinate system "{0}" is invalid or not yet supported +STK_INVALID_OR_UNSUPPORTED_COORDINATE_SYSTEM = el sistema de coordenades "{0}" de STK és invàlid o no s''ha implementat encara + +# STK coordinate system "{0}" has not been mapped to an Orekit frame +STK_UNMAPPED_COORDINATE_SYSTEM = el sistema de coordenades "{0}" de STK encara no ha estat mapejat a una estructura Orekit + +# unexpected end of STK file (after line {0}) +STK_UNEXPECTED_END_OF_FILE = final inesperat d''un fitxer STK (després de la linea {0}) + +# unsupported clock file version {0} +CLOCK_FILE_UNSUPPORTED_VERSION = versió de l''arxiu de rellotge {0} no reconeguda + +# version {0} from file {1} is not supported, supported version: {2} +UNSUPPORTED_FILE_FORMAT_VERSION = versió {0} del fitxer {1} no s''ha tingut en compte, versions tingudes en compte: {2} + +# non-existent geomagnetic model {0} for year {1} +NON_EXISTENT_GEOMAGNETIC_MODEL = no existeix el fitxer del model geomagnètic {0} per l''any {1} + +# geomagnetic model {0} with epoch {1} does not support time transformation, no secular variation coefficients defined +UNSUPPORTED_TIME_TRANSFORM = el model geomagnètic {0} en l''època {1} no té en coñpte les transformacions temporals, no hi ha definit cap coefficient de variació secular + +# time transformation of geomagnetic model {0} with epoch {1} is outside its validity range: {2} != [{3}, {4}] +OUT_OF_RANGE_TIME_TRANSFORM = la transformació temporal del model geomagnètic {0} en l''època {1} està fora del domini de validesa: {2} != [{3}, {4}] + +# not enough data (sample size = {0}) +NOT_ENOUGH_DATA = no hi ha dades suficients (mida de la mostra = {0}) + +# too small number of cached neighbors: {0} (must be at least {1}) +NOT_ENOUGH_CACHED_NEIGHBORS = número de veïns emmagatzemada massa petit : {0} (deu ser almenys {1}) + +# no cached entries +NO_CACHED_ENTRIES = cap entrada emmagatzemada + +# generated entries not sorted: {0} > {1} by {2,number,0.0##############E0} s +NON_CHRONOLOGICALLY_SORTED_ENTRIES = les entrades generades no estan ordenades: {0} > {1} de {2,number,0.0##############E0} segons + +# no data generated around date: {0} +NO_DATA_GENERATED = cap dada generada al voltant de la data: {0} + +# unable to generate new data before {0}, but data is requested for {1} which is {2,number,0.0##############E0} s before +UNABLE_TO_GENERATE_NEW_DATA_BEFORE = no es pot generar noves dades abans de {0}, les dades solicitades per {1} són de {2,number,0.0##############E0} segons abans + +# unable to generate new data after {0}, but data is requested for {1} which is {2,number,0.0##############E0} s after +UNABLE_TO_GENERATE_NEW_DATA_AFTER = no es poden generar noves dades després de {0}, les dades sol·licitades per a {1} són de {2,number,0.0##############E0} segonds després + +# unable to compute hyperbolic eccentric anomaly from the mean anomaly after {0} iterations +UNABLE_TO_COMPUTE_HYPERBOLIC_ECCENTRIC_ANOMALY = després de {0} iteracins no s''ha aconseguit calcular l''anomalia excèntrica hiperbòlica a partir de l''anomalia mitjana + +# unable to compute eccentric longitude argument from the mean one after {0} iterations +UNABLE_TO_COMPUTE_ECCENTRIC_LONGITUDE_ARGUMENT = + +# unable to compute eccentric latitude argument from the mean one after {0} iterations +UNABLE_TO_COMPUTE_ECCENTRIC_LATITUDE_ARGUMENT = + +# unable to compute mean orbit from osculating orbit after {0} iterations +UNABLE_TO_COMPUTE_DSST_MEAN_PARAMETERS = no es pot calcular l''òrbita mitjana a partir de l''òrbita osculating al cap de {0} iteracions + +# derivation order {0} is out of range +OUT_OF_RANGE_DERIVATION_ORDER = l''ordre de derivació {0} està fora del rang + +# orbit type {0} not allowed here, allowed types: {1} +ORBIT_TYPE_NOT_ALLOWED = tipus d''òrbita {0} no permesa, els tipus permesos són: {1} + +# non pseudo-inertial frame {0} is not suitable as reference for inertial forces +NON_PSEUDO_INERTIAL_FRAME_NOT_SUITABLE_AS_REFERENCE_FOR_INERTIAL_FORCES = sistema de referència no pseudo-inèrcial {0} no és adeqüat com referència per a les forces inercials + +# method not available in the absence of a central body +METHOD_NOT_AVAILABLE_WITHOUT_CENTRAL_BODY = mètode no disponible en absència d''un cos central + +# operation not available between frames {0} and {1} +INCOMPATIBLE_FRAMES = operació no disponible entre els sistemes de referència {0} i {1} + +# orbit not defined, state rather contains an absolute position-velocity-acceleration +UNDEFINED_ORBIT = òrbita no definida, l''estat conté més aviat una posició-velocitat-acceleració absoluta + +# absolute position-velocity-acceleration not defined, state rather contains an orbit +UNDEFINED_ABSOLUTE_PVCOORDINATES = posició-velocitat-acceleració absoluta no definida, l''estat conté més aviat una òrbita + +# an inertial force model has to be used when propagating in non-inertial frame {0} +INERTIAL_FORCE_MODEL_MISSING = és necesari un model de força inercial per propagar en un sistema de refèrencia no inercial {0} + +# no SEM almanac file found +NO_SEM_ALMANAC_AVAILABLE = no s''ha trobat fitxer d''almanacs SEM + +# file {0} is not a supported SEM almanac file +NOT_A_SUPPORTED_SEM_ALMANAC_FILE = el fitxer {0} no es reconeix com fitxer vàlid d''almanacs SEM + +# no Yuma almanac file found +NO_YUMA_ALMANAC_AVAILABLE = no es troba el fitxer d''almanacs Yuma + +# file {0} is not a supported Yuma almanac file +NOT_A_SUPPORTED_YUMA_ALMANAC_FILE = el fitxer {0} no es reconeix com fitxer vàlid d''almanacs Yuma + +# only {0} GNSS orbits are provided while {1} are needed to compute the DOP +NOT_ENOUGH_GNSS_FOR_DOP = tan sols s''han especificat {0} òrbites GNSS i es necessiten {1} per calcular el DOP + +# use of time system {0} in CCSDS files requires an additional ICD and is not implemented in Orekit +CCSDS_TIME_SYSTEM_NOT_IMPLEMENTED = per utilitzar el sistema temporal {0} en els fitxers CCSDS es necessita un ICD adicional i no està disponible en Orekit + +# unknown attitude type {0} +CCSDS_UNKNOWN_ATTITUDE_TYPE = tipus d''orientació {0} desconeguda + +# incomplete data +CCSDS_INCOMPLETE_DATA = dades incompletes + +# invalid rotation sequence {0} at line {1} of file {2} +CCSDS_INVALID_ROTATION_SEQUENCE = sequència de rotació {0} invàlida en la linea {1} del fitxer {2} + +# element set type {0} ({1}) is not supported yet +CCSDS_UNSUPPORTED_ELEMENT_SET_TYPE = encara no hi ha suport pel tipus d''elements {0} ({1}) + +# retrograde factor not supported in element set {0} +CCSDS_UNSUPPORTED_RETROGRADE_EQUINOCTIAL = factor retrògrad sense suport en el tipus d''elements {0} + +# wrong number of units for maneuver {0} +CCSDS_MANEUVER_UNITS_WRONG_NB_COMPONENTS = número incorrecte d''unitats per la maniobra {0} + +# missing time field for maneuver {0} +CCSDS_MANEUVER_MISSING_TIME = falta camp temporal per la maniobra {0} + +# attitude type {0} and rate type {1} calls for {2} states, got {3} +CCSDS_INCONSISTENT_NUMBER_OF_ATTITUDE_STATES = el tipus d''orientació {0} i el tipus de velocitat {1} necessiten {2} estats, però {3} s''han donat + +# incompatible keys {0} and {1} should not both be used +CCSDS_INCOMPATIBLE_KEYS_BOTH_USED = les claus incompatibles {0} i {1} no s''haurien d''utilitzar juntes + +# sensor index {0} is already used +CCSDS_SENSOR_INDEX_ALREADY_USED = índex de captor {0} ja utilitzat + +# missing sensor index {0} +CCSDS_MISSING_SENSOR_INDEX = falta l''índex de captor {0} + +# inconsistent number of elements: expected {0}, got {1} +INCONSISTENT_NUMBER_OF_ELEMENTS = número d''elements incoherents: esperats {0}, donats {1} + +# Creating an aggregate propagator requires at least one constituent propagator, but none were provided. +NOT_ENOUGH_PROPAGATORS = per crear un propagador combinat es necesita al menys un propagador, però no s''en ha especificat cap + +# Creating an aggregate attitude provider requires at least one constituent attitude provider, but none were provided. +NOT_ENOUGH_ATTITUDE_PROVIDERS = la creació d''un proveïdor d''actitud agregat necessita al menys un proveïdor d''actitud, però no se n''ha previst cap + +# argument {0} cannot be null +NULL_ARGUMENT = l''entrada {0} no pot ser nul·la + +# value {0} not found in {1} +VALUE_NOT_FOUND = no es troba el valor {0} en {1} + +# Klobuchar coefficients α or β could not be loaded from {0} +KLOBUCHAR_ALPHA_BETA_NOT_LOADED = els coeficients Klobuchar α o β no s''han pogut carregar de {0} + +# Klobuchar coefficients α or β not available for date {0} +KLOBUCHAR_ALPHA_BETA_NOT_AVAILABLE_FOR_DATE = falten els coeficients Klobuchar α o β per la data {0} + +# file {0} does not contain Klobuchar coefficients α or β +NO_KLOBUCHAR_ALPHA_BETA_IN_FILE = el fitxer {0} no conté els coeficients Klobuchar α o β + +# no reference date set for parameter {0} +NO_REFERENCE_DATE_FOR_PARAMETER = la data de referència per el paràmetre {0} no està inicialitzada + +# station {0} not found, known stations: {1} +STATION_NOT_FOUND = no es troba l''estació {0}, estaciones conegudes: {1} + +# unknown satellite system {0} +UNKNOWN_SATELLITE_SYSTEM = sistema de satèl·lits desconeguts {0} + +# unknown time system {0} +UNKNOWN_TIME_SYSTEM = sistema de temps desconegut {0} + +# unknown UTC Id {0} +UNKNOWN_UTC_ID = identificador UTC {0} desconegut + +# unknown clock data type {0} +UNKNOWN_CLOCK_DATA_TYPE = tipus de dades de rellotge desconegudes {0} + +# unknown satellite antenna code {0} +UNKNOWN_SATELLITE_ANTENNA_CODE = codi d''antena de satèl·lit desconegut {0} + +# frequency {0} is not supported by antenna {1} +UNSUPPORTED_FREQUENCY_FOR_ANTENNA = frequència {0} no presa en compte per l''antena {1} + +# cannot find satellite {0} in satellite system {1} +CANNOT_FIND_SATELLITE_IN_SYSTEM = no es pot trobar el satèl·lit {0} en el sistema de satèl·lits {1} + +# unknown RINEX frequency {0} in file {1}, line {2} +UNKNOWN_RINEX_FREQUENCY = frequència RINEX desconeguda en el fitxer {1}, linea {2} + +# mismatched frequencies in file {0}, line {1} (expected {2}, got {3}) +MISMATCHED_FREQUENCIES = frequències incoherents en el fitxer {0}, linea {1} (s''esperava {2} i s''ha trobat {3}) + +# wrong parsing type for file {0} +WRONG_PARSING_TYPE = tipus d''anàlisis incorrecte pel fitxer {0} + +# wrong number of columns in file {0}, line {1} (expected {2} columns, got {3} columns) +WRONG_COLUMNS_NUMBER = número de columnes erròni en el fitxer {0}, linea {1} (s''esperavan {2} columnes i s''han trobat {3}) + +# unsupported format for file {0} +UNSUPPORTED_FILE_FORMAT = format no reconegut pel fitxer {0} + +# incomplete header in file {0} +INCOMPLETE_HEADER = capçalera incompleta en el fitxer {0} + +# inconsistent number of satellites in line {0}, file {1}: observation with {2} satellites and number of max satellites is {3} +INCONSISTENT_NUMBER_OF_SATS = número incoherent de satèl·lits en la linea {0}, fitxer {1}: observació amb {2} satèl·lits, no obstant el número màxim de satèl·lits és {3} + +# the satellite system {3} from line {0}, file {1} is not consistent with the Rinex Satellite System {2} in header +INCONSISTENT_SATELLITE_SYSTEM = el sistema de satèl·lits {3} en la linea {0}, fitxer {1} no és coherent amb el Sistema de Satèl·lits Rinex {2} en la capçalera + +# no propagator configured +NO_PROPAGATOR_CONFIGURED = cap propagador ha estat configurat + +# dimension {0} is inconsistent with parameters list: {1} +DIMENSION_INCONSISTENT_WITH_PARAMETERS = la dimensió {0} no és coherent amb la llista de paràmetres: {1} + +# file {0} is not a supported Unix-compressed file +NOT_A_SUPPORTED_UNIX_COMPRESSED_FILE = el fitxer {0} no respecta el format Unix-compressed reconegut + +# unexpected end of file {0} +UNEXPECTED_END_OF_FILE = fi inesperada del fitxer {0} + +# file {0} is corrupted +CORRUPTED_FILE = l''arxiu {0} està fet malbé + +# Vienna coefficients ah or aw or zh or zw could not be loaded from {0} +VIENNA_ACOEF_OR_ZENITH_DELAY_NOT_LOADED = els coeficients ah o aw o zh o zw del model de Viena no han pogut carregar-se des de {0} + +# Vienna coefficients ah or aw or zh or zw not available for date {0} +VIENNA_ACOEF_OR_ZENITH_DELAY_NOT_AVAILABLE_FOR_DATE = els coeficients ah o aw o zh o zw del model de Viena no estan disponibles per la data {0} + +# file {0} does not contain Vienna coefficients ah, aw, zh or zw +NO_VIENNA_ACOEF_OR_ZENITH_DELAY_IN_FILE = el fitxer {0} no conté els coeficients ah o aw o zh o zw del model de Viena + +# irregular or incomplete grid in file {0} +IRREGULAR_OR_INCOMPLETE_GRID = quadrícula irregular o incompleta en el fitxer {0} + +# invalid satellite system {0} +INVALID_SATELLITE_SYSTEM = el sistema de satèl·lits {0} no és vàlid + +# IONEX files {0} does not contain TEC data for date {1} +NO_TEC_DATA_IN_FILES_FOR_DATE = els fitxers IONEX {0} no contenent dades TEC per la data {1} + +# number of maps {0} is inconsistent with header specification: {1} +INCONSISTENT_NUMBER_OF_TEC_MAPS_IN_FILE = el número de mapes {0} no és coherent amb l''especificació de la capçalera {1} + +# file {0} does not contain latitude or longitude bondaries in its header section +NO_LATITUDE_LONGITUDE_BONDARIES_IN_IONEX_HEADER = el fitxer {0} no conté límits de latitud o longitud en la secció de la capçalera + +# file {0} does not contain epoch of first or last map in its header section +NO_EPOCH_IN_IONEX_HEADER = el fitxer {0} no conté èpoques del primer o últim mapa en la secció de la capçalera + +# The first column of itrf-versions.conf is a plain prefix that is matched against the +# name of each loaded file. It should not contain any regular expression syntax or +# directory components, i.e. \"/\" or \"\\\". Actual value: \"{0}\". +ITRF_VERSIONS_PREFIX_ONLY = la primera columna del fitxer itrf-versions.conf és un prèfix simple que es compara als noms de cada un dels fitxers carregats. No hauria contenir cap expresió racional ni cap separador de directori, per exemple: \"/\" o \"\\\". El valor real és: \"{0}\". + +# cannot compute aiming direction at singular point: latitude = {0}, longitude = {1} +CANNOT_COMPUTE_AIMING_AT_SINGULAR_POINT = no es pot calcular la direcció objectiu en el punt singular latitud = {0}, longitud = {1} + +# STEC integration did not converge +STEC_INTEGRATION_DID_NOT_CONVERGE = l''integració STEC no ha arribat a la convergència + +# MODIP grid not be loaded from {0} +MODIP_GRID_NOT_LOADED = no s''ha pogut carregar la quadrícula MODIP des de {0} + +# NeQuick coefficient f2 or fm3 not be loaded from {0} +NEQUICK_F2_FM3_NOT_LOADED = impossible de carregar els coeficients NeQuick f2 y fm3 des de {0} + +# file {0} is not a supported Hatanaka-compressed file +NOT_A_SUPPORTED_HATANAKA_COMPRESSED_FILE = no es reconeix el fitxer {0} com fitxer comprimit per el mètodo de Hatanaka + +# Cannot compute around {0} +CANNOT_COMPUTE_LAGRANGIAN = no es poden realitzar els calculs al voltant de {0} + +# The trajectory does not cross XZ Plane, it will not result in a Halo Orbit +TRAJECTORY_NOT_CROSSING_XZPLANE = la trajectoria no intersecciona amb el Pla XZ, no pot derivar en una òrbita Halo + +# The multiple shooting problem is underconstrained : {0} free variables, {1} constraints +MULTIPLE_SHOOTING_UNDERCONSTRAINED = el problema multiple-shooting no té suficients restriccions: {0} variables lliures, {1} restriccions + +# invalid measurement types {0} and {1} for the combination of measurements {2} +INVALID_MEASUREMENT_TYPES_FOR_COMBINATION_OF_MEASUREMENTS = tipus de mesures {0} i {1} incompatibles per la combinació de mesures {2} + +# frequencies {0} and {1} are incompatibles for the {2} combination +INCOMPATIBLE_FREQUENCIES_FOR_COMBINATION_OF_MEASUREMENTS = les frequències {0} i {1} són incompatibles per la combinació {2} + +# observations are not in chronological order: {0} is {2} s after {1} +NON_CHRONOLOGICAL_DATES_FOR_OBSERVATIONS = les observacions no es troben en ordre cronològic: {0} és {2} s després de {1} + +# Use of the ExceptionalDataContext detected. This is typically used to detect developer errors. +EXCEPTIONAL_DATA_CONTEXT = s''ha detectat una utilització de ExceptionalDataContext. Això s''utilitza normalment per detectar errors del desenvolupador + +# Observations must have different dates: {0}, {1} ({3,number,0.0##############E0} s from first observation), and {2} ({4,number,0.0##############E0} s from first observation, {5,number,0.0##############E0} s from second observation) +NON_DIFFERENT_DATES_FOR_OBSERVATIONS = les observacions han de tenir dates diferents: {0}, {1} ({3,number,0.0##############E0} s des de la primera observació), i {2} ({4,number,0.0##############E0} des de la primera observació, {5,number,0.0##############E0} des de la segona observació) + +# observations are not in the same plane +NON_COPLANAR_POINTS = les observacions no estan en el mateix pla + +# invalid parameter {0}: {1} not in range [{2}, {3}] +INVALID_PARAMETER_RANGE = invalidesa del paràmetre {0}: {1} fora del rang [{2}, {3}] + +# The parameter {0} should not be null in {1} +PARAMETER_NOT_SET = el paràmetre {0} no hauria de ser nul en {1} + +# {0} is not implemented +FUNCTION_NOT_IMPLEMENTED = {0} no està implementat + +# Impossible to execute {0} with {1} set to {2} +INVALID_TYPE_FOR_FUNCTION = impossible ejecutar {0} amb {1} establit a {2} + +# No data could be parsed from file {0} +NO_DATA_IN_FILE = cap dada ha pogut llegir-se des del fitxer {0} + +# Unexpected end of CPF file (after line {0}) +CPF_UNEXPECTED_END_OF_FILE = fi inesperada del fitxer CPF (després de la linea {0}) + +# Unexpected file format. Must be {0} but is {1} +UNEXPECTED_FORMAT_FOR_ILRS_FILE = Format de fitxer inesperat. Deu ser {0} però és {1} + +# Invalid range indicator {0} in CRD file header +INVALID_RANGE_INDICATOR_IN_CRD_FILE = Indicador de rang invàlid {0} en la capçalera del fitxer CRD + +# Unexpected end of CRD file (after line {0}) +CRD_UNEXPECTED_END_OF_FILE = fi inesperada del fitxer CRD (després de la linea {0}) + +# end of encoded message reached +END_OF_ENCODED_MESSAGE = s''ha arribat al final del missatge codificat + +# too large data type ({0} bits) +TOO_LARGE_DATA_TYPE = tipus de dades massa grans ({0} bits) + +# unknown encoded message number {0} +UNKNOWN_ENCODED_MESSAGE_NUMBER = número de missatge codificat {0} desconegut + +# unknown authentication method: {0} +UNKNOWN_AUTHENTICATION_METHOD = el mètode d''autentificació és desconegut: {0} + +# unknown carrier phase code: {0} +UNKNOWN_CARRIER_PHASE_CODE = el codi de la fase portadora {0} és desconegut + +# unknown data format: {0} +UNKNOWN_DATA_FORMAT = el format de dades {0} és desconegut + +# unknown navigation system: {0} +UNKNOWN_NAVIGATION_SYSTEM = sistema de navegació {0} desconegut + +# data stream {0} requires a NMEA fix data +STREAM_REQUIRES_NMEA_FIX = el flux de dades {0} requereix una dada fixa NMEA + +# failed authentication for mountpoint {0} +FAILED_AUTHENTICATION = fallo de l''autentificació pel punt de montatge {0} + +# error connecting to {0}: {1} +CONNECTION_ERROR = error de conexió a {0}: {1} + +# unexpected content type {0} +UNEXPECTED_CONTENT_TYPE = tipus de contingut {0} inesperat + +# cannot parse GNSS data from {0} +CANNOT_PARSE_GNSS_DATA = no es poden analitzar les dades GNSS de {0} + +# invalid GNSS data: {0} +INVALID_GNSS_DATA = dades GNSS invàides: {0} + +# GNSS parity error on word {0} +GNSS_PARITY_ERROR = error de paritat GNSS en la paraula {0} + +# unknown host {0} +UNKNOWN_HOST = anfitrió {0} desconegut + +# error parsing sourcetable line {0} from {1}: {2} +SOURCETABLE_PARSE_ERROR = error d''anàlisis de la linea {0} de la taula d''origen {1}: {2} + +# cannot parse sourcetable from {0} +CANNOT_PARSE_SOURCETABLE = no es pot analitzar la taula d''origen des de {0} + +# mount point {0} is already connected +MOUNPOINT_ALREADY_CONNECTED = el punt de montatge {0} ja està conectat + +# missing header from {0}: {1} +MISSING_HEADER = falta capçalera de {0}: {1} + +# {0} is not a valid international designator +NOT_VALID_INTERNATIONAL_DESIGNATOR = {0} no és un designador internacional vàlid + +# value for key {0} has not been initialized +UNINITIALIZED_VALUE_FOR_KEY = el valor no s''ha inicialitzat per la clau {0} + +# unknown unit {0} +UNKNOWN_UNIT = unitat {0} desconeguda + +# units {0} and {1} are not compatible +INCOMPATIBLE_UNITS = les unitats {0} i {1} no són compatibles + +# missing velocity data +MISSING_VELOCITY = falten dades de velocitat + +# attempt to generate file {0} with a formatting error +ATTEMPT_TO_GENERATE_MALFORMED_FILE = tentativa de generar el fitxer {0} amb un error de format + +# {0} failed to find root between {1} (g={2,number,0.0##############E0}) and {3} (g={4,number,0.0##############E0})\nLast iteration at {5} (g={6,number,0.0##############E0}) +FIND_ROOT = {0} ha fallat buscant una solució entre {1} (g={2,number,0.0##############E0}) i {3} (g={4,number,0.0##############E0})\nÚltima iteració en {5} (g={6,number,0.0##############E0}) + +# missing station data for epoch {0} +MISSING_STATION_DATA_FOR_EPOCH = Falten dades de l''estació per l''època {0} + +# inconsistent parameters selection between pairs {0}/{1} and {2}/{3} +INCONSISTENT_SELECTION = selecció de paràmetres inconsistents entre els pars {0}/{1} i {2}/{3} + +# no unscented transform configured +NO_UNSCENTED_TRANSFORM_CONFIGURED = cap transformació unscented configurada + +# value is not strictly positive: {0} +NOT_STRICTLY_POSITIVE = el valor {0} no és estrictament positiu + +# transform from {0} to {1} is not implemented +UNSUPPORTED_TRANSFORM = la transformació de {0} a {1} no s''ha implementat + +# orbital parameters type: {0} is different from expected orbital type : {1} +WRONG_ORBIT_PARAMETERS_TYPE = paràmetre orbital de tipus: {0} és diferent del paràmetre orbital esperat de tipus: {1} + +# {0} expects {1} elements, got {2} +WRONG_NB_COMPONENTS = {0} necessita {1} elements, en té {2} + +# cannot change covariance type if defined in a local orbital frame +CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_LOF = no pot camviar el tipis de covariança si es defineix en el el marc de referència de l''orbita local + +# cannot change covariance type if defined in a non pseudo-inertial reference frame +CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_NON_INERTIAL_FRAME = no pot camviar el tipis de covariança si es defineix en el el marc de referència no pseudo-inercial + +# primary collision object time of closest approach is different from the secondary collision object's one +DIFFERENT_TIME_OF_CLOSEST_APPROACH = el temps del major apropament del primer objecte de col·lisió és diferent del segon objecte de col·lisió + +# first date {0} does not match second date {1} +DATES_MISMATCH = la primera data {0} no coincideix amb la segona data {1} + +# first orbit mu {0} does not match second orbit mu {1} +ORBITS_MUS_MISMATCH = la mu {0} de la primera òrbita no coincideix amb la mu de la segona òrbita {1} + +# one state is defined using an orbit while the other is defined using an absolute position-velocity-acceleration +DIFFERENT_STATE_DEFINITION = un estat està definit utilizant una òrbita mentres que l''altre estat està definit utilizant una posició-velocitat-acceleració absolutes + +# state date {0} does not match its covariance date {1} +STATE_AND_COVARIANCE_DATES_MISMATCH = la data {0} de l''estat no coincideix amb la data {1} de la seva covariança + +# creating a spacecraft state interpolator requires at least one orbit interpolator or an absolute position-velocity-acceleration interpolator +NO_INTERPOLATOR_FOR_STATE_DEFINITION = crear un interpolador pel vehicle espacial requereix al menys un in interpolador d''òrbita o un interpolador absolut de posició-velocitat-acceleració + +# wrong interpolator defined for this spacecraft state type (orbit or absolute PV) +WRONG_INTERPOLATOR_DEFINED_FOR_STATE_INTERPOLATION = interpolador mal definit per aquest tipus de definició del vehicle espacial (òrbita o PV absoluts) + +# multiple interpolators are used so they may use different numbers of interpolation points +MULTIPLE_INTERPOLATOR_USED = mala interpolació definida per aquest tipus de definició del vehícule espacial (òrbita o PV) + +# header for file {0} has not been written yet +HEADER_NOT_WRITTEN = la capçalera del fitxer {0} encara no s''ha escrit + +# header for file {0} has already been written +HEADER_ALREADY_WRITTEN = la capçalera del fitxer {0} ja s''ha escrit + +# Cannot start the propagation from an infinitely far date +CANNOT_START_PROPAGATION_FROM_INFINITY = La propagagació no pot començar a partir d’’una data infinita + +# Too long time gap between data points: {0} s +TOO_LONG_TIME_GAP_BETWEEN_DATA_POINTS = + +# invalid satellite id {0} +INVALID_SATELLITE_ID = Id {0} del satél·lit invàlid + +# EOP interpolation degree must be of the form 4k-1, got {0} +WRONG_EOP_INTERPOLATION_DEGREE = El grau de l’’interpolador EOP ha de tenir la forma 4k-1, ara és {0} + +# number of planes {0} is inconsistent with number of satellites {1} in Walker constellation +WALKER_INCONSISTENT_PLANES = + +# Infinite value appears during computation of atmospheric density in NRLMSISE00 model +INFINITE_NRLMSISE00_DENSITY = 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 f4073f88c3..03dfe749fe 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_da.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_da.utf8 @@ -157,6 +157,9 @@ TOO_LARGE_ORDER_FOR_GRAVITY_FIELD = for høj kontrol (m = {0}, maksimal potentie # no term ({0}, {1}) in a {2}x{3} spherical harmonics decomposition WRONG_DEGREE_OR_ORDER = +# wrong orbital elements for averaging theory +WRONG_ELEMENTS_FOR_AVERAGING_THEORY = + # several reference dates ({0} and {1} differ by {3,number,0.0##############E0} s) found in gravity field file {2} SEVERAL_REFERENCE_DATES_IN_GRAVITY_FIELD = @@ -406,6 +409,9 @@ SP3_INCOMPATIBLE_FILE_METADATA = # cannot splice sp3 files with incompatible satellite metadata for satellite {0} SP3_INCOMPATIBLE_SATELLITE_MEDATADA = +# frame {0} not allowed here +FRAME_NOT_ALLOWED = + # STK coordinate system "{0}" is invalid or not yet supported STK_INVALID_OR_UNSUPPORTED_COORDINATE_SYSTEM = @@ -454,6 +460,12 @@ UNABLE_TO_GENERATE_NEW_DATA_AFTER = # unable to compute hyperbolic eccentric anomaly from the mean anomaly after {0} iterations UNABLE_TO_COMPUTE_HYPERBOLIC_ECCENTRIC_ANOMALY = kan ikke beregne hyperbolsk excentrisk anomali fra hovedanomalien efter {0} iterationer +# unable to compute eccentric longitude argument from the mean one after {0} iterations +UNABLE_TO_COMPUTE_ECCENTRIC_LONGITUDE_ARGUMENT = + +# unable to compute eccentric latitude argument from the mean one after {0} iterations +UNABLE_TO_COMPUTE_ECCENTRIC_LATITUDE_ARGUMENT = + # unable to compute mean orbit from osculating orbit after {0} iterations UNABLE_TO_COMPUTE_DSST_MEAN_PARAMETERS = kan ikke beregne hovedbane ud fra oskulerende bane efter {0} iterationer @@ -861,8 +873,17 @@ HEADER_ALREADY_WRITTEN = # Cannot start the propagation from an infinitely far date CANNOT_START_PROPAGATION_FROM_INFINITY = +# Too long time gap between data points: {0} s +TOO_LONG_TIME_GAP_BETWEEN_DATA_POINTS = + # invalid satellite id {0} INVALID_SATELLITE_ID = # EOP interpolation degree must be of the form 4k-1, got {0} WRONG_EOP_INTERPOLATION_DEGREE = + +# number of planes {0} is inconsistent with number of satellites {1} in Walker constellation +WALKER_INCONSISTENT_PLANES = + +# Infinite value appears during computation of atmospheric density in NRLMSISE00 model +INFINITE_NRLMSISE00_DENSITY = 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 5559b29dac..3732af6ee1 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_de.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_de.utf8 @@ -157,6 +157,9 @@ TOO_LARGE_ORDER_FOR_GRAVITY_FIELD = Order zu groß (m = {0}, maximale Order des # no term ({0}, {1}) in a {2}x{3} spherical harmonics decomposition WRONG_DEGREE_OR_ORDER = +# wrong orbital elements for averaging theory +WRONG_ELEMENTS_FOR_AVERAGING_THEORY = + # several reference dates ({0} and {1} differ by {3,number,0.0##############E0} s) found in gravity field file {2} SEVERAL_REFERENCE_DATES_IN_GRAVITY_FIELD = @@ -406,6 +409,9 @@ SP3_INCOMPATIBLE_FILE_METADATA = # cannot splice sp3 files with incompatible satellite metadata for satellite {0} SP3_INCOMPATIBLE_SATELLITE_MEDATADA = +# frame {0} not allowed here +FRAME_NOT_ALLOWED = + # STK coordinate system "{0}" is invalid or not yet supported STK_INVALID_OR_UNSUPPORTED_COORDINATE_SYSTEM = @@ -454,6 +460,12 @@ UNABLE_TO_GENERATE_NEW_DATA_AFTER = # unable to compute hyperbolic eccentric anomaly from the mean anomaly after {0} iterations UNABLE_TO_COMPUTE_HYPERBOLIC_ECCENTRIC_ANOMALY = die hyperbolisch-exzentrische Anomalie kann nicht aus der mittleren Anomalie berechnet werden nach {0} Iterationen +# unable to compute eccentric longitude argument from the mean one after {0} iterations +UNABLE_TO_COMPUTE_ECCENTRIC_LONGITUDE_ARGUMENT = + +# unable to compute eccentric latitude argument from the mean one after {0} iterations +UNABLE_TO_COMPUTE_ECCENTRIC_LATITUDE_ARGUMENT = + # unable to compute mean orbit from osculating orbit after {0} iterations UNABLE_TO_COMPUTE_DSST_MEAN_PARAMETERS = die Durschnittsumlaufbahn kann auch nach {0} Iterationen nicht von der oskulierenden Umlaufbahn berechnet werden @@ -861,8 +873,17 @@ HEADER_ALREADY_WRITTEN = # Cannot start the propagation from an infinitely far date CANNOT_START_PROPAGATION_FROM_INFINITY = +# Too long time gap between data points: {0} s +TOO_LONG_TIME_GAP_BETWEEN_DATA_POINTS = + # invalid satellite id {0} INVALID_SATELLITE_ID = # EOP interpolation degree must be of the form 4k-1, got {0} WRONG_EOP_INTERPOLATION_DEGREE = + +# number of planes {0} is inconsistent with number of satellites {1} in Walker constellation +WALKER_INCONSISTENT_PLANES = + +# Infinite value appears during computation of atmospheric density in NRLMSISE00 model +INFINITE_NRLMSISE00_DENSITY = 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 5caea1b28d..5c3a6ccb88 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_el.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_el.utf8 @@ -157,6 +157,9 @@ TOO_LARGE_ORDER_FOR_GRAVITY_FIELD = πολύ μεγάλη τάξη (m = {0}, δ # no term ({0}, {1}) in a {2}x{3} spherical harmonics decomposition WRONG_DEGREE_OR_ORDER = +# wrong orbital elements for averaging theory +WRONG_ELEMENTS_FOR_AVERAGING_THEORY = + # several reference dates ({0} and {1} differ by {3,number,0.0##############E0} s) found in gravity field file {2} SEVERAL_REFERENCE_DATES_IN_GRAVITY_FIELD = @@ -406,6 +409,9 @@ SP3_INCOMPATIBLE_FILE_METADATA = # cannot splice sp3 files with incompatible satellite metadata for satellite {0} SP3_INCOMPATIBLE_SATELLITE_MEDATADA = +# frame {0} not allowed here +FRAME_NOT_ALLOWED = + # STK coordinate system "{0}" is invalid or not yet supported STK_INVALID_OR_UNSUPPORTED_COORDINATE_SYSTEM = @@ -454,6 +460,12 @@ UNABLE_TO_GENERATE_NEW_DATA_AFTER = # unable to compute hyperbolic eccentric anomaly from the mean anomaly after {0} iterations UNABLE_TO_COMPUTE_HYPERBOLIC_ECCENTRIC_ANOMALY = αδύνατο να υπολογίστει υπερβολική εκκεντρική ανωμαλία από τη μέση ανωμαλία μετά από {0} επαναλήψεις +# unable to compute eccentric longitude argument from the mean one after {0} iterations +UNABLE_TO_COMPUTE_ECCENTRIC_LONGITUDE_ARGUMENT = + +# unable to compute eccentric latitude argument from the mean one after {0} iterations +UNABLE_TO_COMPUTE_ECCENTRIC_LATITUDE_ARGUMENT = + # unable to compute mean orbit from osculating orbit after {0} iterations UNABLE_TO_COMPUTE_DSST_MEAN_PARAMETERS = @@ -861,8 +873,17 @@ HEADER_ALREADY_WRITTEN = # Cannot start the propagation from an infinitely far date CANNOT_START_PROPAGATION_FROM_INFINITY = +# Too long time gap between data points: {0} s +TOO_LONG_TIME_GAP_BETWEEN_DATA_POINTS = + # invalid satellite id {0} INVALID_SATELLITE_ID = # EOP interpolation degree must be of the form 4k-1, got {0} WRONG_EOP_INTERPOLATION_DEGREE = + +# number of planes {0} is inconsistent with number of satellites {1} in Walker constellation +WALKER_INCONSISTENT_PLANES = + +# Infinite value appears during computation of atmospheric density in NRLMSISE00 model +INFINITE_NRLMSISE00_DENSITY = 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 50760d653e..d4a40f432c 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_en.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_en.utf8 @@ -157,6 +157,9 @@ TOO_LARGE_ORDER_FOR_GRAVITY_FIELD = too large order (m = {0}, potential maximal # no term ({0}, {1}) in a {2}x{3} spherical harmonics decomposition WRONG_DEGREE_OR_ORDER = no term ({0}, {1}) in a {2}x{3} spherical harmonics decomposition +# wrong orbital elements for averaging theory +WRONG_ELEMENTS_FOR_AVERAGING_THEORY = unexpected type of orbital elements for required averaging theory + # several reference dates ({0} and {1} differ by {3,number,0.0##############E0} s) found in gravity field file {2} SEVERAL_REFERENCE_DATES_IN_GRAVITY_FIELD = several reference dates ({0} and {1} differ by {3,number,0.0##############E0} s) found in gravity field file {2} @@ -406,6 +409,9 @@ SP3_INCOMPATIBLE_FILE_METADATA = cannot splice sp3 files with incompatible metad # cannot splice sp3 files with incompatible satellite metadata for satellite {0} SP3_INCOMPATIBLE_SATELLITE_MEDATADA = cannot splice sp3 files with incompatible satellite metadata for satellite {0} +# frame {0} not allowed here +FRAME_NOT_ALLOWED = frame {0} not allowed here + # STK coordinate system "{0}" is invalid or not yet supported STK_INVALID_OR_UNSUPPORTED_COORDINATE_SYSTEM = STK coordinate system "{0}" is invalid or not yet supported @@ -454,6 +460,12 @@ UNABLE_TO_GENERATE_NEW_DATA_AFTER = unable to generate new data after {0}, but d # unable to compute hyperbolic eccentric anomaly from the mean anomaly after {0} iterations UNABLE_TO_COMPUTE_HYPERBOLIC_ECCENTRIC_ANOMALY = unable to compute hyperbolic eccentric anomaly from the mean anomaly after {0} iterations +# unable to compute eccentric longitude argument from the mean one after {0} iterations +UNABLE_TO_COMPUTE_ECCENTRIC_LONGITUDE_ARGUMENT = unable to compute eccentric longitude argument from the mean one after {0} iterations + +# unable to compute eccentric latitude argument from the mean one after {0} iterations +UNABLE_TO_COMPUTE_ECCENTRIC_LATITUDE_ARGUMENT = unable to compute eccentric latitude argument from the mean one after {0} iterations + # unable to compute mean orbit from osculating orbit after {0} iterations UNABLE_TO_COMPUTE_DSST_MEAN_PARAMETERS = unable to compute mean orbit from osculating orbit after {0} iterations @@ -861,8 +873,17 @@ HEADER_ALREADY_WRITTEN = header for file {0} has already been written # Cannot start the propagation from an infinitely far date CANNOT_START_PROPAGATION_FROM_INFINITY = Cannot start the propagation from an infinitely far date +# Too long time gap between data points: {0} s +TOO_LONG_TIME_GAP_BETWEEN_DATA_POINTS = Too long time gap between data points: {0} s + # invalid satellite id {0} INVALID_SATELLITE_ID = invalid satellite id {0} # EOP interpolation degree must be of the form 4k-1, got {0} WRONG_EOP_INTERPOLATION_DEGREE = EOP interpolation degree must be of the form 4k-1, got {0} + +# number of planes {0} is inconsistent with number of satellites {1} in Walker constellation +WALKER_INCONSISTENT_PLANES = number of planes {0} is inconsistent with number of satellites {1} in Walker constellation + +# 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 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 7d14d8b07a..0c6363d924 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_es.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_es.utf8 @@ -157,6 +157,9 @@ TOO_LARGE_ORDER_FOR_GRAVITY_FIELD = orden demasiado alto (n = {0}, el orden de p # no term ({0}, {1}) in a {2}x{3} spherical harmonics decomposition WRONG_DEGREE_OR_ORDER = ningún término ({0}, {1}) en una descomposición armónica esférica de {2}x{3} +# wrong orbital elements for averaging theory +WRONG_ELEMENTS_FOR_AVERAGING_THEORY = + # several reference dates ({0} and {1} differ by {3,number,0.0##############E0} s) found in gravity field file {2} SEVERAL_REFERENCE_DATES_IN_GRAVITY_FIELD = se han encontrado varias fechas de referencia ({0} y {1} difieren de {3,number,0.0##############E0} s) en el fichero de campo gravitacional {2} @@ -406,6 +409,9 @@ SP3_INCOMPATIBLE_FILE_METADATA = imposible de adjuntar los ficheros sp3 teniendo # cannot splice sp3 files with incompatible satellite metadata for satellite {0} SP3_INCOMPATIBLE_SATELLITE_MEDATADA = imposible de adjuntar los ficheros sp3 teniendo metadata del satéllite {0} diferente +# frame {0} not allowed here +FRAME_NOT_ALLOWED = + # STK coordinate system "{0}" is invalid or not yet supported STK_INVALID_OR_UNSUPPORTED_COORDINATE_SYSTEM = el sistema de coordenadas "{0}" de STK es inválido o no aún implementado @@ -454,6 +460,12 @@ UNABLE_TO_GENERATE_NEW_DATA_AFTER = no se pueden generar nuevos datos después d # unable to compute hyperbolic eccentric anomaly from the mean anomaly after {0} iterations UNABLE_TO_COMPUTE_HYPERBOLIC_ECCENTRIC_ANOMALY = después de {0} iteraciones no se ha conseguido calcular la anomalía excéntrica hiperbólica a partir de la anomalía media +# unable to compute eccentric longitude argument from the mean one after {0} iterations +UNABLE_TO_COMPUTE_ECCENTRIC_LONGITUDE_ARGUMENT = unable to compute eccentric longitude argument from the mean one after {0} iterations + +# unable to compute eccentric latitude argument from the mean one after {0} iterations +UNABLE_TO_COMPUTE_ECCENTRIC_LATITUDE_ARGUMENT = unable to compute eccentric latitude argument from the mean one after {0} iterations + # unable to compute mean orbit from osculating orbit after {0} iterations UNABLE_TO_COMPUTE_DSST_MEAN_PARAMETERS = no se puede calcular la órbita media a partir de la órbita osculatriz tras {0} iteraciones @@ -861,8 +873,18 @@ HEADER_ALREADY_WRITTEN = la cabezera del fichero {0} ya se ha escrito # Cannot start the propagation from an infinitely far date CANNOT_START_PROPAGATION_FROM_INFINITY = +# Too long time gap between data points: {0} s +TOO_LONG_TIME_GAP_BETWEEN_DATA_POINTS = + # invalid satellite id {0} INVALID_SATELLITE_ID = # EOP interpolation degree must be of the form 4k-1, got {0} WRONG_EOP_INTERPOLATION_DEGREE = + +# number of planes {0} is inconsistent with number of satellites {1} in Walker constellation +WALKER_INCONSISTENT_PLANES = + +# Infinite value appears during computation of atmospheric density in NRLMSISE00 model +INFINITE_NRLMSISE00_DENSITY = + 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 13e6244952..5c8ccff1ef 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_fr.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_fr.utf8 @@ -157,6 +157,9 @@ TOO_LARGE_ORDER_FOR_GRAVITY_FIELD = ordre trop important (n = {0}, l''ordre de p # no term ({0}, {1}) in a {2}x{3} spherical harmonics decomposition WRONG_DEGREE_OR_ORDER = pas de terme ({0}, {1}) dans une décomposition en harmoniques sphériques {2}x{3} +# wrong orbital elements for averaging theory +WRONG_ELEMENTS_FOR_AVERAGING_THEORY = type d'éléments orbitaux inattendus pour cette théorie de moyennisation + # several reference dates ({0} and {1} differ by {3,number,0.0##############E0} s) found in gravity field file {2} SEVERAL_REFERENCE_DATES_IN_GRAVITY_FIELD = plusieurs dates de références ({0} et {1} diffèrent de {3,number,0.0##############E0} s) trouvées dans le fichier de champ de gravité {2} @@ -406,11 +409,14 @@ SP3_INCOMPATIBLE_FILE_METADATA = impossible de joindre des fichiers sp3 ayant de # cannot splice sp3 files with incompatible satellite metadata for satellite {0} SP3_INCOMPATIBLE_SATELLITE_MEDATADA = impossible de joindre des fichiers sp3 ayant des métadonnées différentes pour le satellite {0} +# frame {0} not allowed here +FRAME_NOT_ALLOWED = repère {0} non autorisé ici + # STK coordinate system "{0}" is invalid or not yet supported -STK_INVALID_OR_UNSUPPORTED_COORDINATE_SYSTEM = +STK_INVALID_OR_UNSUPPORTED_COORDINATE_SYSTEM = système de coordonnées STK "{0}" invalide ou non pris en compte # STK coordinate system "{0}" has not been mapped to an Orekit frame -STK_UNMAPPED_COORDINATE_SYSTEM = +STK_UNMAPPED_COORDINATE_SYSTEM = le système de coordonnées STK "{0}" n''est pas associé à un repère Orekit # unexpected end of STK file (after line {0}) STK_UNEXPECTED_END_OF_FILE = fin inattendue d''un fichier STK (après la ligne {0}) @@ -454,6 +460,12 @@ UNABLE_TO_GENERATE_NEW_DATA_AFTER = impossible de générer des données après # unable to compute hyperbolic eccentric anomaly from the mean anomaly after {0} iterations UNABLE_TO_COMPUTE_HYPERBOLIC_ECCENTRIC_ANOMALY = impossible de calculer l''anomalie excentrique hyperbolique à partir de l''anomalie moyenne après {0} itérations +# unable to compute eccentric longitude argument from the mean one after {0} iterations +UNABLE_TO_COMPUTE_ECCENTRIC_LONGITUDE_ARGUMENT = impossible de calculer l''argument de longitude excentrique à partir de celle moyenne {0} itérations + +# unable to compute eccentric latitude argument from the mean one after {0} iterations +UNABLE_TO_COMPUTE_ECCENTRIC_LATITUDE_ARGUMENT = impossible de calculer l''argument de latitude excentrique à partir de celle moyenne {0} itérations + # unable to compute mean orbit from osculating orbit after {0} iterations UNABLE_TO_COMPUTE_DSST_MEAN_PARAMETERS = impossible de calculer l''orbite moyenne à partir de l''orbite osculatrice après {0} itérations @@ -861,8 +873,18 @@ HEADER_ALREADY_WRITTEN = l''en-tête du fichier {0} a déjà été écrit # Cannot start the propagation from an infinitely far date CANNOT_START_PROPAGATION_FROM_INFINITY = Impossible de lancer une propagation à partir d'une date infiniment lointaine +# Too long time gap between data points: {0} s +TOO_LONG_TIME_GAP_BETWEEN_DATA_POINTS = Intervalle temporel trop long entre les points : {0} s + # invalid satellite id {0} INVALID_SATELLITE_ID = identifiant satellite {0} invalide # EOP interpolation degree must be of the form 4k-1, got {0} WRONG_EOP_INTERPOLATION_DEGREE = le degré d''interpolation pour les EOP devrait être de la forme 4k-1, mais {0} a été fourni + +# number of planes {0} is inconsistent with number of satellites {1} in Walker constellation +WALKER_INCONSISTENT_PLANES = le nombre {0} de plans est incohérent avec le nombre {1} total de satellites pour une constellation de Walker + +# 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 + 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 d9922665fc..01cef4f554 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_gl.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_gl.utf8 @@ -157,6 +157,9 @@ TOO_LARGE_ORDER_FOR_GRAVITY_FIELD = orde demasiado importante (n = {0}, a orde m # no term ({0}, {1}) in a {2}x{3} spherical harmonics decomposition WRONG_DEGREE_OR_ORDER = +# wrong orbital elements for averaging theory +WRONG_ELEMENTS_FOR_AVERAGING_THEORY = + # several reference dates ({0} and {1} differ by {3,number,0.0##############E0} s) found in gravity field file {2} SEVERAL_REFERENCE_DATES_IN_GRAVITY_FIELD = @@ -406,6 +409,9 @@ SP3_INCOMPATIBLE_FILE_METADATA = # cannot splice sp3 files with incompatible satellite metadata for satellite {0} SP3_INCOMPATIBLE_SATELLITE_MEDATADA = +# frame {0} not allowed here +FRAME_NOT_ALLOWED = + # STK coordinate system "{0}" is invalid or not yet supported STK_INVALID_OR_UNSUPPORTED_COORDINATE_SYSTEM = @@ -454,6 +460,12 @@ UNABLE_TO_GENERATE_NEW_DATA_AFTER = # unable to compute hyperbolic eccentric anomaly from the mean anomaly after {0} iterations UNABLE_TO_COMPUTE_HYPERBOLIC_ECCENTRIC_ANOMALY = imposible de calcular la anomalía excéntrica hiperbólica a partir de la anomalía media después de {0} iteraciones +# unable to compute eccentric longitude argument from the mean one after {0} iterations +UNABLE_TO_COMPUTE_ECCENTRIC_LONGITUDE_ARGUMENT = unable to compute eccentric longitude argument from the mean one after {0} iterations + +# unable to compute eccentric latitude argument from the mean one after {0} iterations +UNABLE_TO_COMPUTE_ECCENTRIC_LATITUDE_ARGUMENT = unable to compute eccentric latitude argument from the mean one after {0} iterations + # unable to compute mean orbit from osculating orbit after {0} iterations UNABLE_TO_COMPUTE_DSST_MEAN_PARAMETERS = @@ -861,8 +873,18 @@ HEADER_ALREADY_WRITTEN = # Cannot start the propagation from an infinitely far date CANNOT_START_PROPAGATION_FROM_INFINITY = +# Too long time gap between data points: {0} s +TOO_LONG_TIME_GAP_BETWEEN_DATA_POINTS = + # invalid satellite id {0} INVALID_SATELLITE_ID = # EOP interpolation degree must be of the form 4k-1, got {0} WRONG_EOP_INTERPOLATION_DEGREE = + +# number of planes {0} is inconsistent with number of satellites {1} in Walker constellation +WALKER_INCONSISTENT_PLANES = + +# Infinite value appears during computation of atmospheric density in NRLMSISE00 model +INFINITE_NRLMSISE00_DENSITY = + 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 0041216a32..839812ba7a 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_it.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_it.utf8 @@ -157,6 +157,9 @@ TOO_LARGE_ORDER_FOR_GRAVITY_FIELD = ordine troppo elevato (m = {0}, l''ordine ma # no term ({0}, {1}) in a {2}x{3} spherical harmonics decomposition WRONG_DEGREE_OR_ORDER = nessun termine ({0}, {1}) nella decomposizione armonica sferica {2}x{3} +# wrong orbital elements for averaging theory +WRONG_ELEMENTS_FOR_AVERAGING_THEORY = + # several reference dates ({0} and {1} differ by {3,number,0.0##############E0} s) found in gravity field file {2} SEVERAL_REFERENCE_DATES_IN_GRAVITY_FIELD = sono state trovate varie date di riferimento ({0} e {1} differenti di {3,number,0.0##############E0} s) nel file del campo gravitazionale {2} @@ -406,6 +409,9 @@ SP3_INCOMPATIBLE_FILE_METADATA = # cannot splice sp3 files with incompatible satellite metadata for satellite {0} SP3_INCOMPATIBLE_SATELLITE_MEDATADA = +# frame {0} not allowed here +FRAME_NOT_ALLOWED = + # STK coordinate system "{0}" is invalid or not yet supported STK_INVALID_OR_UNSUPPORTED_COORDINATE_SYSTEM = @@ -454,6 +460,12 @@ UNABLE_TO_GENERATE_NEW_DATA_AFTER =impossibile generare dati dopo il {0}, dati r # unable to compute hyperbolic eccentric anomaly from the mean anomaly after {0} iterations UNABLE_TO_COMPUTE_HYPERBOLIC_ECCENTRIC_ANOMALY = impossibile calcolare l''anomalia eccentrica iperbolica a partire dall''anomalia media dopo {0} iterazioni +# unable to compute eccentric longitude argument from the mean one after {0} iterations +UNABLE_TO_COMPUTE_ECCENTRIC_LONGITUDE_ARGUMENT = unable to compute eccentric longitude argument from the mean one after {0} iterations + +# unable to compute eccentric latitude argument from the mean one after {0} iterations +UNABLE_TO_COMPUTE_ECCENTRIC_LATITUDE_ARGUMENT = unable to compute eccentric latitude argument from the mean one after {0} iterations + # unable to compute mean orbit from osculating orbit after {0} iterations UNABLE_TO_COMPUTE_DSST_MEAN_PARAMETERS = impossibile calcolare l''orbita media a partire dall''orbita osculatrice dopo {0} iterazioni @@ -861,8 +873,17 @@ HEADER_ALREADY_WRITTEN = # Cannot start the propagation from an infinitely far date CANNOT_START_PROPAGATION_FROM_INFINITY = +# Too long time gap between data points: {0} s +TOO_LONG_TIME_GAP_BETWEEN_DATA_POINTS = + # invalid satellite id {0} INVALID_SATELLITE_ID = # EOP interpolation degree must be of the form 4k-1, got {0} WRONG_EOP_INTERPOLATION_DEGREE = + +# number of planes {0} is inconsistent with number of satellites {1} in Walker constellation +WALKER_INCONSISTENT_PLANES = + +# Infinite value appears during computation of atmospheric density in NRLMSISE00 model +INFINITE_NRLMSISE00_DENSITY = 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 f5b7b6164c..b5836a1f62 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_no.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_no.utf8 @@ -157,6 +157,9 @@ TOO_LARGE_ORDER_FOR_GRAVITY_FIELD = for høy kontroll (m = {0}, maksimal potensi # no term ({0}, {1}) in a {2}x{3} spherical harmonics decomposition WRONG_DEGREE_OR_ORDER = +# wrong orbital elements for averaging theory +WRONG_ELEMENTS_FOR_AVERAGING_THEORY = + # several reference dates ({0} and {1} differ by {3,number,0.0##############E0} s) found in gravity field file {2} SEVERAL_REFERENCE_DATES_IN_GRAVITY_FIELD = @@ -406,6 +409,9 @@ SP3_INCOMPATIBLE_FILE_METADATA = # cannot splice sp3 files with incompatible satellite metadata for satellite {0} SP3_INCOMPATIBLE_SATELLITE_MEDATADA = +# frame {0} not allowed here +FRAME_NOT_ALLOWED = + # STK coordinate system "{0}" is invalid or not yet supported STK_INVALID_OR_UNSUPPORTED_COORDINATE_SYSTEM = @@ -454,6 +460,12 @@ UNABLE_TO_GENERATE_NEW_DATA_AFTER = # unable to compute hyperbolic eccentric anomaly from the mean anomaly after {0} iterations UNABLE_TO_COMPUTE_HYPERBOLIC_ECCENTRIC_ANOMALY = kan ikke beregne hyperbolsk eksentrisk anomali fra hovedanomalien etter {0} iterasjoner +# unable to compute eccentric longitude argument from the mean one after {0} iterations +UNABLE_TO_COMPUTE_ECCENTRIC_LONGITUDE_ARGUMENT = unable to compute eccentric longitude argument from the mean one after {0} iterations + +# unable to compute eccentric latitude argument from the mean one after {0} iterations +UNABLE_TO_COMPUTE_ECCENTRIC_LATITUDE_ARGUMENT = unable to compute eccentric latitude argument from the mean one after {0} iterations + # unable to compute mean orbit from osculating orbit after {0} iterations UNABLE_TO_COMPUTE_DSST_MEAN_PARAMETERS = kan ikke beregne hovedbane utfra osculærbane etter {0} gjentakelser @@ -861,8 +873,17 @@ HEADER_ALREADY_WRITTEN = # Cannot start the propagation from an infinitely far date CANNOT_START_PROPAGATION_FROM_INFINITY = +# Too long time gap between data points: {0} s +TOO_LONG_TIME_GAP_BETWEEN_DATA_POINTS = + # invalid satellite id {0} INVALID_SATELLITE_ID = # EOP interpolation degree must be of the form 4k-1, got {0} WRONG_EOP_INTERPOLATION_DEGREE = + +# number of planes {0} is inconsistent with number of satellites {1} in Walker constellation +WALKER_INCONSISTENT_PLANES = + +# Infinite value appears during computation of atmospheric density in NRLMSISE00 model +INFINITE_NRLMSISE00_DENSITY = 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 f0bfc97bf9..aaf4d32ab9 100644 --- a/src/main/resources/assets/org/orekit/localization/OrekitMessages_ro.utf8 +++ b/src/main/resources/assets/org/orekit/localization/OrekitMessages_ro.utf8 @@ -92,7 +92,7 @@ UNEXPECTED_DATA_AT_LINE_IN_FILE = date neașteptate la linia {0} din fișierul { NON_CHRONOLOGICAL_DATES_IN_FILE = date necronologice în fișierul {0}, linia {1} # inconsistent sampling date: expected {0} but got {1} -INCONSISTENT_SAMPLING_DATE = +INCONSISTENT_SAMPLING_DATE = dat[ de e;antionare invalidă: așteptat {0} dar primit {1} # no IERS UTC-TAI history data loaded NO_IERS_UTC_TAI_HISTORY_DATA_LOADED = datele istorice UTC-TAI nu sunt încărcate @@ -155,7 +155,10 @@ TOO_LARGE_DEGREE_FOR_GRAVITY_FIELD = grad prea mare (n = {0}, gradul maxim al po TOO_LARGE_ORDER_FOR_GRAVITY_FIELD = ordin prea mare (n = {0}, ordinul maxim al potențialului este {1}) # no term ({0}, {1}) in a {2}x{3} spherical harmonics decomposition -WRONG_DEGREE_OR_ORDER = +WRONG_DEGREE_OR_ORDER = nu există termenul ({0}, {1}) în decompozitia armonicilor sferice de {2}x{3} + +# wrong orbital elements for averaging theory +WRONG_ELEMENTS_FOR_AVERAGING_THEORY = elemente orbitale invalide pentru teoria de mediere # several reference dates ({0} and {1} differ by {3,number,0.0##############E0} s) found in gravity field file {2} SEVERAL_REFERENCE_DATES_IN_GRAVITY_FIELD = mai multe date de referință ({0} and {1} separate de {3,number,0.0##############E0} s) descoperite în fișierul de câmp gravitațional {2} @@ -284,10 +287,10 @@ UNEXPECTED_TWO_ELEVATION_VALUES_FOR_ONE_AZIMUTH = date neașteptate, două valor UNSUPPORTED_PARAMETER_NAME = parametrul {0} necunoscut, parametrii cunoscuți: {1} # {0} parameter contains several span in its value TimeSpanMap, the {1} method must be called -PARAMETER_WITH_SEVERAL_ESTIMATED_VALUES = +PARAMETER_WITH_SEVERAL_ESTIMATED_VALUES = parametrul {0} conține mai multe intervale în valoarea sa TimeSpanMap, trebuie apelată metoda {1} # setPeriod was already called once on {0} parameter, another parameter should be created if the periods have to be changed -PARAMETER_PERIODS_HAS_ALREADY_BEEN_SET = +PARAMETER_PERIODS_HAS_ALREADY_BEEN_SET = metoda setPeriod a fost deja apelată o dată pentru parametrul {0}, alt parametru trebuie creat dacă intervalele trebuie modificate # scale factor for parameter {0} is too small: {1} TOO_SMALL_SCALE_FOR_PARAMETER = factorul de scalare pentru parametrul {0} este prea mic: {1} @@ -353,7 +356,7 @@ CCSDS_UNKNOWN_CONVENTIONS = convențiile IERS nu au fost inițializate înainte CCSDS_INVALID_FRAME = sistemul de referință {0} nu este valid în contextul acestui fișier CCSDS # this LVLH local orbital frame uses a different definition, please use LVLH_CCSDS instead -CCSDS_DIFFERENT_LVLH_DEFINITION = +CCSDS_DIFFERENT_LVLH_DEFINITION = acest sistem de referință local LVLH folosește o definiție diferită, vă rugăm să utilizați în schimb LVLH_CCSDS # inconsistent time systems: {0} ≠ {1} CCSDS_INCONSISTENT_TIME_SYSTEMS = sisteme de timp incoerente : {0} ≠ {1} @@ -368,7 +371,7 @@ CCSDS_TDM_MISSING_RANGE_UNITS_CONVERTER = niciun convertor de unitate pseudo-dis CCSDS_TIME_SYSTEM_NOT_READ_YET = Sistemul de timp ar fi trebuit deja configurat înaintea liniei {0} din fișierul {1} # cannot estimate precession without proper derivatives -CANNOT_ESTIMATE_PRECESSION_WITHOUT_PROPER_DERIVATIVES = +CANNOT_ESTIMATE_PRECESSION_WITHOUT_PROPER_DERIVATIVES = imposibil de estimar precesia fără derivate adecvate # name "{0}" is already used for an additional state ADDITIONAL_STATE_NAME_ALREADY_IN_USE = numele "{0}" este deja folosit pentru o stare adițională @@ -392,25 +395,28 @@ DSST_ECC_NO_NUMERICAL_AVERAGING_METHOD = Orbita curentă are o eccentricitate {0 SP3_UNSUPPORTED_VERSION = versiune neacceptată a fișierului sp3 "{0}" # invalid header entry {0} "{1}" in file {2} (format version {3}) -SP3_INVALID_HEADER_ENTRY = +SP3_INVALID_HEADER_ENTRY = înregistrarea {0} "{1}" din antetul fișierului {2} este invalidă (versiune de format {3}) # version "{0}" supports only up to {1} satellites, found {2} in file {3} -SP3_TOO_MANY_SATELLITES_FOR_VERSION = +SP3_TOO_MANY_SATELLITES_FOR_VERSION = versiunea "{0}" permite maxim {1} sateliți, au fost găsiți {2} în fișierul {3} # found {0} epochs in file {1}, expected {2} SP3_NUMBER_OF_EPOCH_MISMATCH = au fost găsite {0} date de referință în fișierul {1}, așteptate {2} # cannot splice sp3 files with incompatible metadata -SP3_INCOMPATIBLE_FILE_METADATA = +SP3_INCOMPATIBLE_FILE_METADATA = imposibil de concatenat fișiere sp3 cu metadate incompatibile # cannot splice sp3 files with incompatible satellite metadata for satellite {0} -SP3_INCOMPATIBLE_SATELLITE_MEDATADA = +SP3_INCOMPATIBLE_SATELLITE_MEDATADA = imposibil de concatenat fișiere sp3 cu metadate incompatibile pentru satelitul {0} + +# frame {0} not allowed here +FRAME_NOT_ALLOWED = sistemul de referință {0} nu este permis aici # STK coordinate system "{0}" is invalid or not yet supported -STK_INVALID_OR_UNSUPPORTED_COORDINATE_SYSTEM = +STK_INVALID_OR_UNSUPPORTED_COORDINATE_SYSTEM = sistemul de soordonate STK "{0}" este invalid sau nu este suportat încă # STK coordinate system "{0}" has not been mapped to an Orekit frame -STK_UNMAPPED_COORDINATE_SYSTEM = +STK_UNMAPPED_COORDINATE_SYSTEM = sistemul de coordonate STK "{0}" nu a fost mapat la un sistem de referință Orekit # unexpected end of STK file (after line {0}) STK_UNEXPECTED_END_OF_FILE = sfârșit neașteptat al fișierului STK (după linia {0}) @@ -419,7 +425,7 @@ STK_UNEXPECTED_END_OF_FILE = sfârșit neașteptat al fișierului STK (după lin CLOCK_FILE_UNSUPPORTED_VERSION = versiune neacceptată a fișierului de ceas {0} # version {0} from file {1} is not supported, supported version: {2} -UNSUPPORTED_FILE_FORMAT_VERSION = +UNSUPPORTED_FILE_FORMAT_VERSION = versiunea {0} din fișierul {1} nu este suportată, versiune suportată: {2} # non-existent geomagnetic model {0} for year {1} NON_EXISTENT_GEOMAGNETIC_MODEL = fișierul de model geomagnetic {0} inexistent pentru anul {1} @@ -454,6 +460,12 @@ UNABLE_TO_GENERATE_NEW_DATA_AFTER = imposibil de generat noi date înainte de {0 # unable to compute hyperbolic eccentric anomaly from the mean anomaly after {0} iterations UNABLE_TO_COMPUTE_HYPERBOLIC_ECCENTRIC_ANOMALY = imposibil de calculat anomalia excentrică hiperbolică pornind de la anomalia medie după {0} iterații +# unable to compute eccentric longitude argument from the mean one after {0} iterations +UNABLE_TO_COMPUTE_ECCENTRIC_LONGITUDE_ARGUMENT = unable to compute eccentric longitude argument from the mean one after {0} iterations + +# unable to compute eccentric latitude argument from the mean one after {0} iterations +UNABLE_TO_COMPUTE_ECCENTRIC_LATITUDE_ARGUMENT = unable to compute eccentric latitude argument from the mean one after {0} iterations + # unable to compute mean orbit from osculating orbit after {0} iterations UNABLE_TO_COMPUTE_DSST_MEAN_PARAMETERS = imposibil de calculat orbita medie din orbita osculatorie după {0} iterații @@ -500,10 +512,10 @@ NOT_ENOUGH_GNSS_FOR_DOP = numai {0} orbite GNSS sunt oferite în timp ce {1} sun CCSDS_TIME_SYSTEM_NOT_IMPLEMENTED = utilizarea sistemului de timp {0} în fișierele CCSDS necesită un ICD adițional și nu este disponibil în Orekit # unknown attitude type {0} -CCSDS_UNKNOWN_ATTITUDE_TYPE = +CCSDS_UNKNOWN_ATTITUDE_TYPE = tipul de atitudine {0} este necunoscut # incomplete data -CCSDS_INCOMPLETE_DATA = +CCSDS_INCOMPLETE_DATA = date incomplete # invalid rotation sequence {0} at line {1} of file {2} CCSDS_INVALID_ROTATION_SEQUENCE = secvență de rotație {0} invalidă la linia {1} din fișierul {2} @@ -521,19 +533,19 @@ CCSDS_MANEUVER_UNITS_WRONG_NB_COMPONENTS = număr greșit al unităților pentru CCSDS_MANEUVER_MISSING_TIME = lipsește câmpul de timp pentru manevra {0} # attitude type {0} and rate type {1} calls for {2} states, got {3} -CCSDS_INCONSISTENT_NUMBER_OF_ATTITUDE_STATES = +CCSDS_INCONSISTENT_NUMBER_OF_ATTITUDE_STATES = tipul de atitudine {0} și tipul de rată {1} necesită {2} stări, sunt definite {3} # incompatible keys {0} and {1} should not both be used -CCSDS_INCOMPATIBLE_KEYS_BOTH_USED = +CCSDS_INCOMPATIBLE_KEYS_BOTH_USED = nu ar trebui folosite ambele chei incompatibile {0} și {1} # sensor index {0} is already used -CCSDS_SENSOR_INDEX_ALREADY_USED = +CCSDS_SENSOR_INDEX_ALREADY_USED = senzorul cu indice {0} este deja utilizat # missing sensor index {0} -CCSDS_MISSING_SENSOR_INDEX = +CCSDS_MISSING_SENSOR_INDEX = lipsește senzorul cu indice {0} # inconsistent number of elements: expected {0}, got {1} -INCONSISTENT_NUMBER_OF_ELEMENTS = +INCONSISTENT_NUMBER_OF_ELEMENTS = numărul de elemente este inconsistent: așteptat {0}, existent {1} # Creating an aggregate propagator requires at least one constituent propagator, but none were provided. NOT_ENOUGH_PROPAGATORS = Crearea unui propagator combinator necesită cel puțin un propagator, dar nici unul nu a fost definit. @@ -569,7 +581,7 @@ UNKNOWN_SATELLITE_SYSTEM = sistemul de sateliți {0} este necunoscut UNKNOWN_TIME_SYSTEM = sistem de timp necunoscut {0} # unknown UTC Id {0} -UNKNOWN_UTC_ID = +UNKNOWN_UTC_ID = identificator UTC {0} necunoscut # unknown clock data type {0} UNKNOWN_CLOCK_DATA_TYPE = tip de date de ceas necunoscut {0} @@ -590,7 +602,7 @@ UNKNOWN_RINEX_FREQUENCY = frecventa RINEX {0} din fișierul {1}, linia {2} este MISMATCHED_FREQUENCIES = frecvențe nepotrivite în fișierul {0}, linia {1} (așteptate {2}, găsite {3}) # wrong parsing type for file {0} -WRONG_PARSING_TYPE = +WRONG_PARSING_TYPE = metodă de parsare eronată pentru fișierul {0} # wrong number of columns in file {0}, line {1} (expected {2} columns, got {3} columns) WRONG_COLUMNS_NUMBER = număr incorect de coloane în fișierul {0}, linia {1} (așteptate {2} coloane, găsite {3} coloane) @@ -760,10 +772,10 @@ UNEXPECTED_CONTENT_TYPE = tip de conținut {0} neașteptat CANNOT_PARSE_GNSS_DATA = imposibil de analizat datele GNSS de la {0} # invalid GNSS data: {0} -INVALID_GNSS_DATA = +INVALID_GNSS_DATA = valoare GNSS invalidă: {0} # GNSS parity error on word {0} -GNSS_PARITY_ERROR = +GNSS_PARITY_ERROR = Eroare de paritate GNSS la cuvântul {0} # unknown host {0} UNKNOWN_HOST = mașina gazdă {0} este necunoscută @@ -802,67 +814,76 @@ ATTEMPT_TO_GENERATE_MALFORMED_FILE = încercare de generare a fișierului {0} ce FIND_ROOT = {0} nu a găsit rădăcina între {1} (g={2,number,0.0##############E0}) și {3} (g={4,number,0.0##############E0})\nUltima iterație la {5} (g={6,number,0.0##############E0}) # missing station data for epoch {0} -MISSING_STATION_DATA_FOR_EPOCH = +MISSING_STATION_DATA_FOR_EPOCH = lipsă date referitoare la stație pentru data {0} # inconsistent parameters selection between pairs {0}/{1} and {2}/{3} -INCONSISTENT_SELECTION = +INCONSISTENT_SELECTION = selecție inconsecventă a parametrilor între perechile {0}/{1} și {2}/{3} # no unscented transform configured -NO_UNSCENTED_TRANSFORM_CONFIGURED = +NO_UNSCENTED_TRANSFORM_CONFIGURED = nicio transformare unscented configurată # value is not strictly positive: {0} -NOT_STRICTLY_POSITIVE = +NOT_STRICTLY_POSITIVE = valoarea nu este strict pozitivă: {0} # transform from {0} to {1} is not implemented -UNSUPPORTED_TRANSFORM = +UNSUPPORTED_TRANSFORM = transformarea de la {0} la {1} nu este implementată # orbital parameters type: {0} is different from expected orbital type : {1} -WRONG_ORBIT_PARAMETERS_TYPE = +WRONG_ORBIT_PARAMETERS_TYPE = tipul prametrilor orbitali: {0} este diferit de tipul parametrilor orbitali: {1} # {0} expects {1} elements, got {2} -WRONG_NB_COMPONENTS = +WRONG_NB_COMPONENTS = {0} așteaptă {1} elemente, găsite {2} # cannot change covariance type if defined in a local orbital frame -CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_LOF = +CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_LOF = imposibil de schimbat tipul de covarianță dacă este definit într-un sistem de referință orbital local # cannot change covariance type if defined in a non pseudo-inertial reference frame -CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_NON_INERTIAL_FRAME = +CANNOT_CHANGE_COVARIANCE_TYPE_IF_DEFINED_IN_NON_INERTIAL_FRAME = imposibil de schimbat tipul de covarianță dacă este definit într-un sistem de referință care nu este pseudo-inertial # primary collision object time of closest approach is different from the secondary collision object's one -DIFFERENT_TIME_OF_CLOSEST_APPROACH = +DIFFERENT_TIME_OF_CLOSEST_APPROACH = data de apropiere maximă a primului obiect este diferită de cea a celui de-al doilea obiect # first date {0} does not match second date {1} -DATES_MISMATCH = +DATES_MISMATCH = prima dată {0} nu este identicp cu cea de-a doua dată {1} # first orbit mu {0} does not match second orbit mu {1} -ORBITS_MUS_MISMATCH = +ORBITS_MUS_MISMATCH = valoarea {0} a lui mu pentru prima orbită nu este egală cu vloarea {1} a lui mu pentru a doua orbită # one state is defined using an orbit while the other is defined using an absolute position-velocity-acceleration -DIFFERENT_STATE_DEFINITION = +DIFFERENT_STATE_DEFINITION = o stare este definită folosind parametrii orbitali, în timp ce cealaltă este definită folosind valori absolute pentru poziție-viteză-accelerație # state date {0} does not match its covariance date {1} -STATE_AND_COVARIANCE_DATES_MISMATCH = +STATE_AND_COVARIANCE_DATES_MISMATCH = data de început {0} nu este identică cu data de covarianță {1} # creating a spacecraft state interpolator requires at least one orbit interpolator or an absolute position-velocity-acceleration interpolator -NO_INTERPOLATOR_FOR_STATE_DEFINITION = +NO_INTERPOLATOR_FOR_STATE_DEFINITION = crearea unui interpolator pentru starea navetei necesită cel puțin un interpolator pentru parametrii orbitali sau un interpolator pentru valori absolute pentru poziție-viteză-accelerație # wrong interpolator defined for this spacecraft state type (orbit or absolute PV) -WRONG_INTERPOLATOR_DEFINED_FOR_STATE_INTERPOLATION = +WRONG_INTERPOLATOR_DEFINED_FOR_STATE_INTERPOLATION = interpolator incorect definit pentru tipul curent de stare a navetei )parametrii orbitali sau valori abolute PV # multiple interpolators are used so they may use different numbers of interpolation points -MULTIPLE_INTERPOLATOR_USED = +MULTIPLE_INTERPOLATOR_USED = mai multi interpolatori sunt folosiți și aceștia ar putea utiliza un număr diferit de puncte de interpolare # header for file {0} has not been written yet -HEADER_NOT_WRITTEN = +HEADER_NOT_WRITTEN = antetul pentru fișierul {0} nu a fost încă scris # header for file {0} has already been written -HEADER_ALREADY_WRITTEN = +HEADER_ALREADY_WRITTEN = antetul pentru fișierul {0} a fost deja scris # Cannot start the propagation from an infinitely far date -CANNOT_START_PROPAGATION_FROM_INFINITY = +CANNOT_START_PROPAGATION_FROM_INFINITY = nu se poate începe o propagare de la o dată infinit îndepărtată + +# Too long time gap between data points: {0} s +TOO_LONG_TIME_GAP_BETWEEN_DATA_POINTS = diferență de timp prea mare între date: {0} s # invalid satellite id {0} -INVALID_SATELLITE_ID = +INVALID_SATELLITE_ID = identificatorul satelitului {0} este invalid # EOP interpolation degree must be of the form 4k-1, got {0} -WRONG_EOP_INTERPOLATION_DEGREE = +WRONG_EOP_INTERPOLATION_DEGREE = gradul pentru interpolatorul EOP trebuie sa fie de forma 4k-1, definit {0} + +# number of planes {0} is inconsistent with number of satellites {1} in Walker constellation +WALKER_INCONSISTENT_PLANES = numărul de planuri {0} este neconsecvent cu numărul de sateliți {1} în constelația Walker + +# 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 diff --git a/src/site/markdown/building.md b/src/site/markdown/building.md index b31a5a332e..228d91539b 100644 --- a/src/site/markdown/building.md +++ b/src/site/markdown/building.md @@ -124,29 +124,4 @@ own application projects that will depend on the Orekit project. You can also check everything works correctly by running the junit tests. If you want to go further and run the tutorials, you need to check the -sister project [Orekit tutorials](https://gitlab.orekit.org/orekit/orekit-tutorials). - -## Building with Ant - -[Ant](http://ant.apache.org/) is a build tool for Java applications. - -For systems not providing ant as a package, ant can be -[downloaded](http://ant.apache.org/bindownload.cgi) from its site at the -Apache Software Foundation. This site also explains the -installation procedure. - -if you are behind a proxy (which is a traditional setting in a corporate -environment), then you need to configure ant to use it. This is explained -in the ant [documentation](http://ant.apache.org/manual/proxy.html). - -Either download the Hipparchus jars from the Hipparchus project and -put it in a lib directory just below the top-level orekit -directory or edit the ant build.xml file to edit the get-hipparchus -target that can automatically download Hipparchus. - -To create a file named build/orekit-x.y.jar where x.y is the version number, -use the following command: - - ant jar - -For other commands, see the ant command line [documentation](https://ant.apache.org/manual/running.html) \ No newline at end of file +sister project [Orekit tutorials](https://gitlab.orekit.org/orekit/orekit-tutorials). \ No newline at end of file diff --git a/src/site/markdown/contributing.md b/src/site/markdown/contributing.md index 313ab4631c..bfa2d2701b 100644 --- a/src/site/markdown/contributing.md +++ b/src/site/markdown/contributing.md @@ -330,11 +330,15 @@ initialize the project in SonarQube. Here is how to do it. 4. Go to the continuous integration (CI) configuration page of your fork (*Settings* -> *CI/CD* -> *Variables* -> *Expand*) and declare a variable - named `SONAR_TOKEN`. The value of this variable must be the value of the + named `SONAR_TOKEN`. + The value of this variable must be the value of the token provided by SonarQube. Check the *Mask variable* option and click on *Add variable*. -5. SonarQube dynamically initiates the project on the first submission, but +5. Also declare a variable `SONAR_HOST_URL` with value https://sonar.orekit.org. + This will allow Gitlab to connect to Orekit SonarQube instance. + +6. SonarQube dynamically initiates the project on the first submission, but this first submission must be on the **master** branch. You can cause this by manually triggering a pipeline. Starting with Orekit version 11, you just need to go to the pipelines page (*Project homepage* -> *CI/CD* -> @@ -342,7 +346,7 @@ initialize the project in SonarQube. Here is how to do it. branch, then click on the *Run pipeline* button. Then wait for half an hour, which is approximately the time needed to compile and run the tests. -6. After that, you can run again the pipeline on your working branch. +7. After that, you can run again the pipeline on your working branch. If your working branch is from an Orekit version prior to 11.0, step 5 described above will not work because of the continuous integration scripts diff --git a/src/site/markdown/data/filtering.md b/src/site/markdown/data/filtering.md index d689706b34..9b9dc14911 100644 --- a/src/site/markdown/data/filtering.md +++ b/src/site/markdown/data/filtering.md @@ -49,11 +49,11 @@ by the `FiltersManager.applyRelevantFilters` method as needed, each one reading underlying stack element and providing filtered data to the next element upward. In the `DataProvidersManager` case, if at the end the name part of the `DataSource` matches the -name that the`DataLoader` instance expects, then the data stream of the top of the stack is opened. +name that the `DataLoader` instance expects, then the data stream of the top of the stack is opened. This is were the lazy opening occurs, and it generally ends up with all the intermediate bytes or characters streams being opened as well. The opened stream is then passed to the `DataLoader` to be parsed. If on the other hand the name part of the `DataSource` does not match the name that the -`DataLoader` instance expects, then neither the data stream is *not* opened, the full stack is discarded +`DataLoader` instance expects, then the data stream is *not* opened, the full stack is discarded and the next resource/file from the `DataProvider` is considered for filtering and loading. In the explicit loading case, application can decide on its own to open or discard the top diff --git a/src/site/markdown/downloads.md.vm b/src/site/markdown/downloads.md.vm index a6a8bbb986..285f3f6867 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.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.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 c273f08d90..62a7b4ea18 100644 --- a/src/site/markdown/faq.md +++ b/src/site/markdown/faq.md @@ -155,6 +155,7 @@ Math to Hipparchus Orekit 12.0 | Hipparchus 3.0 Orekit 12.0.1 | Hipparchus 3.0 Orekit 12.0.2 | Hipparchus 3.0 + Orekit 12.1 | 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 d1dc0f6454..ff757c0412 100644 --- a/src/site/markdown/index.md +++ b/src/site/markdown/index.md @@ -53,6 +53,7 @@ * Cartesian, Keplerian (elliptic, parabolic, hyperbolic), circular and equinoctial parameters, with non-Keplerian derivatives if available + * Walker constellations (including in-orbit spares with shifted position) * Two-Line Elements (TLE) * Two-Line Elements generation using Fixed-Point algorithm or Least Squares Fitting * transparent conversion between all parameters @@ -89,6 +90,7 @@ and the perturbative acceleration due to atmospheric drag * SDP4/SGP4 with 2006 corrections * GNSS: GPS, QZSS, Galileo, GLONASS, Beidou, IRNSS and SBAS + * Intelsat's 11 elements * numerical propagators * central attraction * gravity models including time-dependent like trends and pulsations @@ -148,6 +150,7 @@ step handler alongside the operational ones * handling of discrete events during integration (models changes, G-stop, simple notifications ...) + * adaptable max checking interval for discrete events detection * predefined discrete events * eclipse (both umbra and penumbra) * ascending and descending node crossing @@ -173,6 +176,8 @@ * impulse maneuvers occurrence * geomagnetic intensity * extremum approach for TCA (Time of Closest Approach) computing + * beta angle + * relative distance with another object * possibility of slightly shifting events in time (for example to switch from solar pointing mode to something else a few minutes before eclipse entry and reverting to solar pointing mode a few minutes after eclipse exit) @@ -224,7 +229,7 @@ * orbit determination can be performed with numerical, DSST, SDP4/SGP4, Eckstein-Hechler, Brouwer-Lyddane, or Keplerian propagators * ephemeris-based orbit determination to estimate measurement parameters like station biases or clock offsets * multi-satellites orbit determination - * initial orbit determination methods (Gibbs, Gooding, Lambert and Laplace) + * initial orbit determination methods (Gibbs, Gooding, Lambert, Gauss, and Laplace) * ground stations displacements due to solid tides * ground stations displacements due to ocean loading (based on Onsala Space Observatory files in BLQ format) * ground stations displacements due to plate tectonics @@ -237,6 +242,7 @@ * position-velocity * position * inter-satellites range (one way and two way) + * inter-satellites GNSS one way range rate * inter-satellites GNSS phase * GNSS code * GNSS phase with integer ambiguity resolution and wind-up effect @@ -255,7 +261,9 @@ * biases * delays * Antenna Phase Center + * Phase ambiguity * Shapiro relativistic effect + * aberration of light in telescope measurements * possibility to add custom measurement modifiers (even for predefined events) * combination of GNSS measurements * dual frequency combination of measurements @@ -278,7 +286,7 @@ * loading and writing of RINEX observation files (version 2, 3, and 4) * loading of RINEX navigation files (version 2, 3, and 4) * support for Hatanaka compact RINEX format - * loading of SINEX file (can load station positions, eccentricities, EOPs, and Differential Code Biases) + * loading of SINEX file (can load station positions, velocities, eccentricities, Post-Seismic Deformation models, EOPs, and Differential Code Biases) * loading of RINEX clock files (version 2 and version 3) * parsing of IGS SSR messages for all constellations (version 1) * parsing of RTCM messages (both ephemeris and correction messages) @@ -289,7 +297,8 @@ * Orbit file handling - * loading and writing of SP3 orbit files (from version a to d) + * loading and writing of SP3 orbit files (from version a to d, including extension to a few inertial frames) + * splicing and interpolation of SP3 files * loading and writing of CCSDS Orbit Data Messages (OPM, OEM, OMM and OCM types are supported, in both KVN and XML formats, standalone or in combined NDM) * loading of SEM and YUMA files for GPS constellation * exporting of ephemeris in CCSDS OEM and OCM file formats @@ -309,7 +318,7 @@ * Global Ionospheric Map (GIM) model * NeQuick ionospheric model * VTEC estimated ionospheric model with Single Layer Model (SLM) ionospheric mapping function - * Global Pression and Temperature models (GPT and GPT2) + * Global Pressure and Temperature models (GPT, GPT2, GPT2w, GPT3) * geomagnetic field (WMM, IGRF) * geoid model from any gravity field * displacement of ground points due to tides @@ -320,7 +329,7 @@ * Collisions * loading and writing of CCSDS Conjunction Data Messages (CDM in both KVN and XML formats) - * 2D probability of collision computing methods assuming short term encounter and spherical bodies : + * 2D probability of collision computing methods assuming short term encounter and spherical bodies: * Chan 1997 * Alfriend 1999 @@ -345,6 +354,7 @@ * Localized in several languages + * Catalan * Danish * English * French diff --git a/src/site/markdown/release-guide.md b/src/site/markdown/release-guide.md index dd8b06b6f0..86aac33382 100644 --- a/src/site/markdown/release-guide.md +++ b/src/site/markdown/release-guide.md @@ -145,13 +145,11 @@ Several files must be updated to take into account the new version: | file name | usage | required update | |-------------------------------------|----------------------------|--------------------------------------------------------------------------------------------------------| -| `build.xml` | building file for Ant users| Update project version number. Check all dependencies' versions are consistent with pom.xml | | `src/site/markdown/index.md` | site home page | Update the text about the latest available version, including important changes from **changes.xml** | | `org/orekit/overview.html` | API documentation | Update the text about the latest available version, including important changes from **changes.xml** | | `src/site/markdown/downloads.md.vm` | downloads links | Declare the new versions, don't forget the date | | `src/site/markdown/faq.md` | FAQ | Add line to the table of dependencies. | -Make sure the ant build works: `ant clean clean-lib jar javadoc`. Once the files have been updated, commit the changes: diff --git a/src/test/java/org/orekit/Utils.java b/src/test/java/org/orekit/Utils.java index 9a58dc23cf..77831718d4 100644 --- a/src/test/java/org/orekit/Utils.java +++ b/src/test/java/org/orekit/Utils.java @@ -28,7 +28,6 @@ import org.orekit.frames.EopHistoryLoader; import org.orekit.frames.FramesFactory; import org.orekit.frames.ITRFVersion; -import org.orekit.models.earth.weather.GlobalPressureTemperature2Model; import org.orekit.orbits.FieldCartesianOrbit; import org.orekit.orbits.FieldCircularOrbit; import org.orekit.orbits.FieldEquinoctialOrbit; @@ -70,6 +69,7 @@ public class Utils { public static final double ae = 6378136.460; public static final double mu = 3.986004415e+14; + @SuppressWarnings("deprecation") public static void clearFactories() { DataContext.setDefault(new LazyLoadedDataContext()); clearFactoryMaps(CelestialBodyFactory.class); @@ -88,7 +88,7 @@ public static void clearFactories() { clearFactoryMaps(c); } } - clearAtomicReference(GlobalPressureTemperature2Model.class); + clearAtomicReference(org.orekit.models.earth.weather.GlobalPressureTemperature2Model.class); FramesFactory.clearEOPHistoryLoaders(); FramesFactory.setEOPContinuityThreshold(5 * Constants.JULIAN_DAY); TimeScalesFactory.clearUTCTAIOffsetsLoaders(); diff --git a/src/test/java/org/orekit/attitudes/AttitudeProviderModifierTest.java b/src/test/java/org/orekit/attitudes/AttitudeProviderModifierTest.java new file mode 100644 index 0000000000..a07caa69cd --- /dev/null +++ b/src/test/java/org/orekit/attitudes/AttitudeProviderModifierTest.java @@ -0,0 +1,79 @@ +/* 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.attitudes; + +import org.hipparchus.complex.Complex; +import org.hipparchus.complex.ComplexField; +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.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; +import org.orekit.utils.FieldPVCoordinatesProvider; +import org.orekit.utils.PVCoordinatesProvider; + +class AttitudeProviderModifierTest { + + @Test + void testGetFrozenAttitudeProvider() { + // GIVEN + final AttitudeProvider attitudeProvider = Mockito.mock(AttitudeProvider.class); + final Rotation expectedRotation = new Rotation(Vector3D.MINUS_I, Vector3D.MINUS_K); + final PVCoordinatesProvider mockedPVCoordinatesProvider = Mockito.mock(PVCoordinatesProvider.class); + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final Frame mockedFrame = Mockito.mock(Frame.class); + Mockito.when(attitudeProvider.getAttitudeRotation(mockedPVCoordinatesProvider, date, mockedFrame)).thenReturn(expectedRotation); + // WHEN + final AttitudeProviderModifier frozenAttitudeProvider = AttitudeProviderModifier.getFrozenAttitudeProvider(attitudeProvider); + final Attitude attitude = frozenAttitudeProvider.getAttitude(mockedPVCoordinatesProvider, date, mockedFrame); + // THEN + Assertions.assertEquals(attitudeProvider, frozenAttitudeProvider.getUnderlyingAttitudeProvider()); + final Rotation actualRotation = attitude.getRotation(); + Assertions.assertEquals(0., Rotation.distance(expectedRotation, actualRotation)); + Assertions.assertEquals(Vector3D.ZERO, attitude.getRotationAcceleration()); + Assertions.assertEquals(Vector3D.ZERO, attitude.getSpin()); + } + + @SuppressWarnings("unchecked") + @Test + void testGetFrozenAttitudeProviderField() { + // GIVEN + final AttitudeProvider attitudeProvider = Mockito.mock(AttitudeProvider.class); + final Rotation expectedRotation = new Rotation(Vector3D.MINUS_I, Vector3D.MINUS_K); + final ComplexField field = ComplexField.getInstance(); + final FieldRotation fieldRotation = new FieldRotation<>(field, expectedRotation); + final FieldPVCoordinatesProvider mockedPVCoordinatesProvider = Mockito.mock(FieldPVCoordinatesProvider.class); + final FieldAbsoluteDate date = FieldAbsoluteDate.getArbitraryEpoch(field); + final Frame mockedFrame = Mockito.mock(Frame.class); + Mockito.when(attitudeProvider.getAttitudeRotation(mockedPVCoordinatesProvider, date, mockedFrame)).thenReturn(fieldRotation); + // WHEN + final AttitudeProvider frozenAttitudeProvider = AttitudeProviderModifier.getFrozenAttitudeProvider(attitudeProvider); + final FieldAttitude attitude = frozenAttitudeProvider.getAttitude(mockedPVCoordinatesProvider, date, + mockedFrame); + // THEN + final Rotation actualRotation = attitude.getRotation().toRotation(); + Assertions.assertEquals(0., Rotation.distance(expectedRotation, actualRotation)); + Assertions.assertEquals(FieldVector3D.getZero(field), attitude.getRotationAcceleration()); + Assertions.assertEquals(FieldVector3D.getZero(field), attitude.getSpin()); + } + +} diff --git a/src/test/java/org/orekit/attitudes/AttitudeProviderTest.java b/src/test/java/org/orekit/attitudes/AttitudeProviderTest.java index fcfed341ea..23b8a5bcdd 100644 --- a/src/test/java/org/orekit/attitudes/AttitudeProviderTest.java +++ b/src/test/java/org/orekit/attitudes/AttitudeProviderTest.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2024 Romain Serra +/* 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. diff --git a/src/test/java/org/orekit/attitudes/AttitudesSequenceTest.java b/src/test/java/org/orekit/attitudes/AttitudesSequenceTest.java index d245e93488..85766d44d7 100644 --- a/src/test/java/org/orekit/attitudes/AttitudesSequenceTest.java +++ b/src/test/java/org/orekit/attitudes/AttitudesSequenceTest.java @@ -23,7 +23,11 @@ import org.hipparchus.Field; import org.hipparchus.analysis.differentiation.Gradient; import org.hipparchus.analysis.differentiation.GradientField; -import org.hipparchus.geometry.euclidean.threed.*; +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.RotationOrder; +import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.ode.events.Action; import org.hipparchus.ode.nonstiff.AdaptiveStepsizeIntegrator; import org.hipparchus.ode.nonstiff.DormandPrince853Integrator; @@ -64,12 +68,18 @@ import org.orekit.propagation.events.EventsLogger; import org.orekit.propagation.events.handlers.ContinueOnEvent; import org.orekit.propagation.numerical.NumericalPropagator; -import org.orekit.propagation.sampling.FieldOrekitFixedStepHandler; -import org.orekit.propagation.sampling.OrekitFixedStepHandler; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; import org.orekit.time.TimeScalesFactory; -import org.orekit.utils.*; +import org.orekit.utils.AngularCoordinates; +import org.orekit.utils.AngularDerivativesFilter; +import org.orekit.utils.Constants; +import org.orekit.utils.ExtendedPVCoordinatesProvider; +import org.orekit.utils.FieldPVCoordinates; +import org.orekit.utils.FieldPVCoordinatesProvider; +import org.orekit.utils.IERSConventions; +import org.orekit.utils.PVCoordinates; +import org.orekit.utils.PVCoordinatesProvider; public class AttitudesSequenceTest { @@ -79,7 +89,7 @@ public class AttitudesSequenceTest { @Test public void testDayNightSwitch() { // Initial state definition : date, orbit - final AbsoluteDate initialDate = new AbsoluteDate(2004, 01, 01, 23, 30, 00.000, TimeScalesFactory.getUTC()); + final AbsoluteDate initialDate = new AbsoluteDate(2004, 1, 1, 23, 30, 00.000, TimeScalesFactory.getUTC()); final Vector3D position = new Vector3D(-6142438.668, 3492467.560, -25767.25680); final Vector3D velocity = new Vector3D(505.8479685, 942.7809215, 7435.922231); final Orbit initialOrbit = new KeplerianOrbit(new PVCoordinates(position, velocity), @@ -137,30 +147,28 @@ public Action eventOccurred(final SpacecraftState s, final EventDetector d, fina // Register the switching events to the propagator attitudesSequence.registerSwitchEvents(propagator); - propagator.setStepHandler(60.0, new OrekitFixedStepHandler() { - public void handleStep(SpacecraftState currentState) { - // the Earth position in spacecraft frame should be along spacecraft Z axis - // during night time and away from it during day time due to roll and pitch offsets - final Vector3D earth = currentState.toTransform().transformPosition(Vector3D.ZERO); - final double pointingOffset = Vector3D.angle(earth, Vector3D.PLUS_K); - - // the g function is the eclipse indicator, its an angle between Sun and Earth limb, - // positive when Sun is outside of Earth limb, negative when Sun is hidden by Earth limb - final double eclipseAngle = ed.g(currentState); - - if (currentState.getDate().durationFrom(lastChange) > 300) { - if (inEclipse) { - Assertions.assertTrue(eclipseAngle <= 0); - Assertions.assertEquals(0.0, pointingOffset, 1.0e-6); - } else { - Assertions.assertTrue(eclipseAngle >= 0); - Assertions.assertEquals(0.767215, pointingOffset, 1.0e-6); - } + propagator.setStepHandler(60.0, currentState -> { + // the Earth position in spacecraft frame should be along spacecraft Z axis + // during night time and away from it during day time due to roll and pitch offsets + final Vector3D earth = currentState.toTransform().transformPosition(Vector3D.ZERO); + final double pointingOffset = Vector3D.angle(earth, Vector3D.PLUS_K); + + // the g function is the eclipse indicator, its an angle between Sun and Earth limb, + // positive when Sun is outside of Earth limb, negative when Sun is hidden by Earth limb + final double eclipseAngle = ed.g(currentState); + + if (currentState.getDate().durationFrom(lastChange) > 300) { + if (inEclipse) { + Assertions.assertTrue(eclipseAngle <= 0); + Assertions.assertEquals(0.0, pointingOffset, 1.0e-6); } else { - // we are in transition - Assertions.assertTrue(pointingOffset <= 0.7672155, - pointingOffset + " " + (0.767215 - pointingOffset)); + Assertions.assertTrue(eclipseAngle >= 0); + Assertions.assertEquals(0.767215, pointingOffset, 1.0e-6); } + } else { + // we are in transition + Assertions.assertTrue(pointingOffset <= 0.7672155, + pointingOffset + " " + (0.767215 - pointingOffset)); } }); @@ -187,7 +195,7 @@ private > void doTestDayNightSwitchField(final { // Initial state definition : date, orbit - final FieldAbsoluteDate initialDate = new FieldAbsoluteDate<>(field, 2004, 01, 01, 23, 30, 00.000, TimeScalesFactory.getUTC()); + final FieldAbsoluteDate initialDate = new FieldAbsoluteDate<>(field, 2004, 1, 1, 23, 30, 00.000, TimeScalesFactory.getUTC()); final FieldVector3D position = new FieldVector3D<>(field, new Vector3D(-6142438.668, 3492467.560, -25767.25680)); final FieldVector3D velocity = new FieldVector3D<>(field, @@ -250,40 +258,38 @@ public Action eventOccurred(final SpacecraftState s, } // Propagator : consider the analytical Eckstein-Hechler model - final FieldPropagator propagator = new FieldEcksteinHechlerPropagator(initialOrbit, attitudesSequence, - Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS, - field.getZero().add(Constants.EIGEN5C_EARTH_MU), - Constants.EIGEN5C_EARTH_C20, - Constants.EIGEN5C_EARTH_C30, Constants.EIGEN5C_EARTH_C40, - Constants.EIGEN5C_EARTH_C50, Constants.EIGEN5C_EARTH_C60); + final FieldPropagator propagator = new FieldEcksteinHechlerPropagator<>(initialOrbit, attitudesSequence, + Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS, + field.getZero().add(Constants.EIGEN5C_EARTH_MU), + Constants.EIGEN5C_EARTH_C20, + Constants.EIGEN5C_EARTH_C30, Constants.EIGEN5C_EARTH_C40, + Constants.EIGEN5C_EARTH_C50, Constants.EIGEN5C_EARTH_C60); // Register the switching events to the propagator attitudesSequence.registerSwitchEvents(field, propagator); - propagator.setStepHandler(field.getZero().add(60.0), new FieldOrekitFixedStepHandler() { - public void handleStep(FieldSpacecraftState currentState) { - // the Earth position in spacecraft frame should be along spacecraft Z axis - // during night time and away from it during day time due to roll and pitch offsets - final FieldVector3D earth = currentState.toTransform().transformPosition(Vector3D.ZERO); - final T pointingOffset = FieldVector3D.angle(earth, Vector3D.PLUS_K); - - // the g function is the eclipse indicator, its an angle between Sun and Earth limb, - // positive when Sun is outside of Earth limb, negative when Sun is hidden by Earth limb - final double eclipseAngle = ed.g(currentState.toSpacecraftState()); - - if (currentState.getDate().durationFrom(lastChange).getReal() > 300) { - if (inEclipse) { - Assertions.assertTrue(eclipseAngle <= 0); - Assertions.assertEquals(0.0, pointingOffset.getReal(), 1.0e-6); - } else { - Assertions.assertTrue(eclipseAngle >= 0); - Assertions.assertEquals(0.767215, pointingOffset.getReal(), 1.0e-6); - } + propagator.setStepHandler(field.getZero().add(60.0), currentState -> { + // the Earth position in spacecraft frame should be along spacecraft Z axis + // during night time and away from it during day time due to roll and pitch offsets + final FieldVector3D earth = currentState.toTransform().transformPosition(Vector3D.ZERO); + final T pointingOffset = FieldVector3D.angle(earth, Vector3D.PLUS_K); + + // the g function is the eclipse indicator, its an angle between Sun and Earth limb, + // positive when Sun is outside of Earth limb, negative when Sun is hidden by Earth limb + final double eclipseAngle = ed.g(currentState.toSpacecraftState()); + + if (currentState.getDate().durationFrom(lastChange).getReal() > 300) { + if (inEclipse) { + Assertions.assertTrue(eclipseAngle <= 0); + Assertions.assertEquals(0.0, pointingOffset.getReal(), 1.0e-6); } else { - // we are in transition - Assertions.assertTrue(pointingOffset.getReal() <= 0.7672155, - pointingOffset.getReal() + " " + (0.767215 - pointingOffset.getReal())); + Assertions.assertTrue(eclipseAngle >= 0); + Assertions.assertEquals(0.767215, pointingOffset.getReal(), 1.0e-6); } + } else { + // we are in transition + Assertions.assertTrue(pointingOffset.getReal() <= 0.7672155, + pointingOffset.getReal() + " " + (0.767215 - pointingOffset.getReal())); } }); @@ -305,7 +311,7 @@ public void handleStep(FieldSpacecraftState currentState) { public void testBackwardPropagation() { // Initial state definition : date, orbit - final AbsoluteDate initialDate = new AbsoluteDate(2004, 01, 01, 23, 30, 00.000, TimeScalesFactory.getUTC()); + final AbsoluteDate initialDate = new AbsoluteDate(2004, 1, 1, 23, 30, 00.000, TimeScalesFactory.getUTC()); final Vector3D position = new Vector3D(-6142438.668, 3492467.560, -25767.25680); final Vector3D velocity = new Vector3D(505.8479685, 942.7809215, 7435.922231); final Orbit initialOrbit = new KeplerianOrbit(new PVCoordinates(position, velocity), @@ -361,15 +367,15 @@ public void testTooShortTransition() { Assertions.fail("an exception should have been thrown"); } catch (OrekitException oe) { Assertions.assertEquals(OrekitMessages.TOO_SHORT_TRANSITION_TIME_FOR_ATTITUDES_SWITCH, oe.getSpecifier()); - Assertions.assertEquals(transitionTime, ((Double) oe.getParts()[0]).doubleValue(), 1.0e-10); - Assertions.assertEquals(threshold, ((Double) oe.getParts()[1]).doubleValue(), 1.0e-10); + Assertions.assertEquals(transitionTime, (Double) oe.getParts()[0], 1.0e-10); + Assertions.assertEquals(threshold, (Double) oe.getParts()[1], 1.0e-10); } } @Test public void testOutOfSyncCalls() { // Initial state definition : date, orbit - final AbsoluteDate initialDate = new AbsoluteDate(2004, 01, 01, 23, 30, 00.000, TimeScalesFactory.getUTC()); + final AbsoluteDate initialDate = new AbsoluteDate(2004, 1, 1, 23, 30, 00.000, TimeScalesFactory.getUTC()); final Vector3D position = new Vector3D(-6142438.668, 3492467.560, -25767.25680); final Vector3D velocity = new Vector3D(505.8479685, 942.7809215, 7435.922231); final Orbit initialOrbit = new KeplerianOrbit(new PVCoordinates(position, velocity), @@ -445,7 +451,7 @@ public void testOutOfSyncCalls() { @Test public void testResetDuringTransitionForward() { // Initial state definition : date, orbit - final AbsoluteDate initialDate = new AbsoluteDate(2004, 01, 01, 23, 30, 00.000, TimeScalesFactory.getUTC()); + final AbsoluteDate initialDate = new AbsoluteDate(2004, 1, 1, 23, 30, 00.000, TimeScalesFactory.getUTC()); final Vector3D position = new Vector3D(-6142438.668, 3492467.560, -25767.25680); final Vector3D velocity = new Vector3D(505.8479685, 942.7809215, 7435.922231); final Orbit initialOrbit = new KeplerianOrbit(new PVCoordinates(position, velocity), @@ -513,7 +519,7 @@ public void testResetDuringTransitionForward() { @Test public void testResetDuringTransitionBackward() { // Initial state definition : date, orbit - final AbsoluteDate initialDate = new AbsoluteDate(2004, 01, 01, 23, 30, 00.000, TimeScalesFactory.getUTC()); + final AbsoluteDate initialDate = new AbsoluteDate(2004, 1, 1, 23, 30, 00.000, TimeScalesFactory.getUTC()); final Vector3D position = new Vector3D(-6142438.668, 3492467.560, -25767.25680); final Vector3D velocity = new Vector3D(505.8479685, 942.7809215, 7435.922231); final Orbit initialOrbit = new KeplerianOrbit(new PVCoordinates(position, velocity), @@ -695,21 +701,21 @@ public > FieldAttitude getAttitude(FieldPVC private static class Handler implements AttitudesSequence.SwitchHandler { - private AttitudeProvider expectedPrevious; - private AttitudeProvider expectedNext; - private List dates; + private final AttitudeProvider expectedPrevious; + private final AttitudeProvider expectedNext; + private final List dates; public Handler(final AttitudeProvider expectedPrevious, final AttitudeProvider expectedNext) { this.expectedPrevious = expectedPrevious; this.expectedNext = expectedNext; - this.dates = new ArrayList(); + this.dates = new ArrayList<>(); } @Override public void switchOccurred(AttitudeProvider previous, AttitudeProvider next, SpacecraftState state) { - Assertions.assertTrue(previous == expectedPrevious); - Assertions.assertTrue(next == expectedNext); + Assertions.assertSame(previous, expectedPrevious); + Assertions.assertSame(next, expectedNext); dates.add(state.getDate()); } diff --git a/src/test/java/org/orekit/attitudes/BodyCenterPointingTest.java b/src/test/java/org/orekit/attitudes/BodyCenterPointingTest.java index 37dcebb7fe..826198cf7b 100644 --- a/src/test/java/org/orekit/attitudes/BodyCenterPointingTest.java +++ b/src/test/java/org/orekit/attitudes/BodyCenterPointingTest.java @@ -18,6 +18,8 @@ import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; +import org.hipparchus.complex.Complex; +import org.hipparchus.complex.ComplexField; import org.hipparchus.fitting.PolynomialCurveFitter; import org.hipparchus.fitting.WeightedObservedPoint; import org.hipparchus.geometry.euclidean.threed.FieldRotation; @@ -65,7 +67,7 @@ import java.util.List; -public class BodyCenterPointingTest { +class BodyCenterPointingTest { // Computation date private AbsoluteDate date; @@ -82,10 +84,31 @@ public class BodyCenterPointingTest { // Earth center pointing attitude provider private BodyCenterPointing earthCenterAttitudeLaw; + @Test + void testGetPosition() { + // GIVEN (done in setup) + // WHEN + final Vector3D actualPosition = earthCenterAttitudeLaw.getTargetPosition(circ, date, circ.getFrame()); + // Check that target is on Earth surface + final Vector3D expectedPosition = earthCenterAttitudeLaw.getTargetPV(circ, date, circ.getFrame()).getPosition(); + Assertions.assertEquals(expectedPosition, actualPosition); + } + + @Test + void testGetPositionField() { + // GIVEN + final FieldCircularOrbit fieldCircularOrbit = new FieldCircularOrbit<>(ComplexField.getInstance(), circ); + // WHEN + final FieldVector3D actualPosition = earthCenterAttitudeLaw.getTargetPosition(fieldCircularOrbit, fieldCircularOrbit.getDate(), circ.getFrame()); + // Check that target is on Earth surface + final FieldVector3D expectedPosition = earthCenterAttitudeLaw.getTargetPV(fieldCircularOrbit, fieldCircularOrbit.getDate(), circ.getFrame()).getPosition(); + Assertions.assertEquals(0., expectedPosition.subtract(actualPosition).getNorm().getReal(), 1e-10); + } + /** Test if target is on Earth surface */ @Test - public void testTarget() { + void testTarget() { // Call get target method TimeStampedPVCoordinates target = earthCenterAttitudeLaw.getTargetPV(circ, date, circ.getFrame()); @@ -100,7 +123,7 @@ public void testTarget() { /** Test if body center belongs to the direction pointed by the satellite */ @Test - public void testBodyCenterInPointingDirection() { + void testBodyCenterInPointingDirection() { // Transform satellite position to position/velocity parameters in EME2000 frame PVCoordinates pvSatEME2000 = circ.getPVCoordinates(); @@ -131,7 +154,7 @@ public void testBodyCenterInPointingDirection() { } @Test - public void testQDot() { + void testQDot() { Utils.setDataRoot("regular-data"); final double ehMu = 3.9860047e14; @@ -191,7 +214,7 @@ public void testQDot() { } @Test - public void testSpin() { + void testSpin() { Utils.setDataRoot("regular-data"); final double ehMu = 3.9860047e14; @@ -238,7 +261,7 @@ public void testSpin() { } @Test - public void testTargetField() { + void testTargetField() { doTestTarget(Binary64Field.getInstance()); } @Test @@ -247,12 +270,12 @@ public void doxBodyCenterInPointingDirectionTest() { } @Test - public void testQDotField() { + void testQDotField() { doTestQDot(Binary64Field.getInstance()); } @Test - public void testSpinField() { + void testSpinField() { doTestSpin(Binary64Field.getInstance()); } diff --git a/src/test/java/org/orekit/attitudes/CelestialBodyPointingTest.java b/src/test/java/org/orekit/attitudes/CelestialBodyPointedTest.java similarity index 65% rename from src/test/java/org/orekit/attitudes/CelestialBodyPointingTest.java rename to src/test/java/org/orekit/attitudes/CelestialBodyPointedTest.java index 11ec8f58b2..9d04be1879 100644 --- a/src/test/java/org/orekit/attitudes/CelestialBodyPointingTest.java +++ b/src/test/java/org/orekit/attitudes/CelestialBodyPointedTest.java @@ -18,6 +18,9 @@ import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; +import org.hipparchus.complex.Complex; +import org.hipparchus.complex.ComplexField; +import org.hipparchus.geometry.euclidean.threed.FieldRotation; import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.Binary64Field; @@ -29,9 +32,7 @@ import org.orekit.bodies.CelestialBodyFactory; import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; -import org.orekit.orbits.FieldOrbit; -import org.orekit.orbits.KeplerianOrbit; -import org.orekit.orbits.Orbit; +import org.orekit.orbits.*; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; import org.orekit.time.AbsoluteDate; @@ -39,13 +40,67 @@ import org.orekit.time.FieldAbsoluteDate; import org.orekit.time.TimeComponents; import org.orekit.time.TimeScalesFactory; +import org.orekit.utils.Constants; import org.orekit.utils.PVCoordinates; import org.orekit.utils.PVCoordinatesProvider; -public class CelestialBodyPointingTest { +class CelestialBodyPointedTest { @Test - public void testSunPointing() { + void templateTestGetAttitudeRotationSameFrame() { + templateTestGetAttitudeRotation(FramesFactory.getEME2000()); + } + + @Test + void templateTestGetAttitudeRotationDifferentFrame() { + templateTestGetAttitudeRotation(FramesFactory.getGCRF()); + } + + void templateTestGetAttitudeRotation(final Frame frame) { + // GIVEN + final Frame celestialFrame = FramesFactory.getEME2000(); + final CelestialBodyPointed celestialBodyPointed = new CelestialBodyPointed(celestialFrame, + CelestialBodyFactory.getSun(), Vector3D.PLUS_K, Vector3D.PLUS_J, Vector3D.PLUS_K); + final PVCoordinates pv = + new PVCoordinates(new Vector3D(28812595.32120171334, 5948437.45881852374, 0.0), + new Vector3D(0, 0, 3680.853673522056)); + final Orbit orbit = new CartesianOrbit(pv, frame, AbsoluteDate.ARBITRARY_EPOCH, Constants.EIGEN5C_EARTH_MU); + // WHEN + final Rotation actualRotation = celestialBodyPointed.getAttitudeRotation(orbit, orbit.getDate(), frame); + // THEN + final Rotation expectedRotation = celestialBodyPointed.getAttitude(orbit, orbit.getDate(), frame).getRotation(); + Assertions.assertEquals(0., Rotation.distance(expectedRotation, actualRotation)); + } + + @Test + void testFieldGetAttitudeRotationSameFrame() { + templateTestFieldGetAttitudeRotation(FramesFactory.getEME2000()); + } + + @Test + void testFieldGetAttitudeRotationDifferentFrame() { + templateTestFieldGetAttitudeRotation(FramesFactory.getGCRF()); + } + + void templateTestFieldGetAttitudeRotation(final Frame frame) { + // GIVEN + final Frame celestialFrame = FramesFactory.getEME2000(); + final CelestialBodyPointed celestialBodyPointed = new CelestialBodyPointed(celestialFrame, + CelestialBodyFactory.getSun(), Vector3D.PLUS_K, Vector3D.PLUS_J, Vector3D.PLUS_K); + final PVCoordinates pv = + new PVCoordinates(new Vector3D(28812595.32120171334, 5948437.45881852374, 0.0), + new Vector3D(0, 0, 3680.853673522056)); + final Orbit orbit = new CartesianOrbit(pv, frame, AbsoluteDate.ARBITRARY_EPOCH, Constants.EIGEN5C_EARTH_MU); + final FieldOrbit fieldOrbit = new FieldCartesianOrbit<>(ComplexField.getInstance(), orbit); + // WHEN + final FieldRotation actualRotation = celestialBodyPointed.getAttitudeRotation(fieldOrbit, fieldOrbit.getDate(), frame); + // THEN + final FieldRotation expectedRotation = celestialBodyPointed.getAttitude(fieldOrbit, fieldOrbit.getDate(), frame).getRotation(); + Assertions.assertEquals(0., Rotation.distance(expectedRotation.toRotation(), actualRotation.toRotation())); + } + + @Test + void testSunPointing() { PVCoordinatesProvider sun = CelestialBodyFactory.getSun(); final Frame frame = FramesFactory.getGCRF(); diff --git a/src/test/java/org/orekit/attitudes/FrameAlignedProviderTest.java b/src/test/java/org/orekit/attitudes/FrameAlignedProviderTest.java index 5473a4adb3..cdabc8f477 100644 --- a/src/test/java/org/orekit/attitudes/FrameAlignedProviderTest.java +++ b/src/test/java/org/orekit/attitudes/FrameAlignedProviderTest.java @@ -211,7 +211,7 @@ public void testGetAttitudeField() { new FieldPVCoordinates<>(one, this.orbit0.getPVCoordinates()), eci, date, - one.multiply(orbit0.getMu())); + one.newInstance(orbit0.getMu())); // action + verify FieldAttitude actual = law.getAttitude(orbit, date, eci); diff --git a/src/test/java/org/orekit/attitudes/GroundPointingAttitudeModifierTest.java b/src/test/java/org/orekit/attitudes/GroundPointingAttitudeModifierTest.java new file mode 100644 index 0000000000..71408ab8ac --- /dev/null +++ b/src/test/java/org/orekit/attitudes/GroundPointingAttitudeModifierTest.java @@ -0,0 +1,148 @@ +/* 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.attitudes; + +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.frames.FramesFactory; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.*; + +class GroundPointingAttitudeModifierTest { + + @Test + void testGetTargetPosition() { + // GIVEN + final GroundPointing mockedLaw = Mockito.mock(GroundPointing.class); + final TestYawLaw law = new TestYawLaw(mockedLaw); + final PVCoordinatesProvider mockedPVProvider = Mockito.mock(PVCoordinatesProvider.class); + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final Frame frame = Mockito.mock(Frame.class); + final Vector3D expectedPosition = Vector3D.PLUS_J; + Mockito.when(mockedLaw.getTargetPosition(mockedPVProvider, date, frame)).thenReturn(expectedPosition); + // WHEN + final Vector3D actualPosition = law.getTargetPosition(mockedPVProvider, date, frame); + // THEN + Assertions.assertEquals(expectedPosition, actualPosition); + } + + @SuppressWarnings("unchecked") + @Test + void testGetTargetPositionField() { + // GIVEN + final GroundPointing mockedLaw = Mockito.mock(GroundPointing.class); + final TestYawLaw law = new TestYawLaw(mockedLaw); + final ComplexField field = ComplexField.getInstance(); + final FieldPVCoordinatesProvider mockedPVProvider = Mockito.mock(FieldPVCoordinatesProvider.class); + final FieldAbsoluteDate date = new FieldAbsoluteDate<>(field, AbsoluteDate.ARBITRARY_EPOCH); + final Frame frame = Mockito.mock(Frame.class); + final FieldVector3D expectedPosition = new FieldVector3D<>(field, Vector3D.PLUS_J); + Mockito.when(mockedLaw.getTargetPosition(mockedPVProvider, date, frame)).thenReturn(expectedPosition); + // WHEN + final FieldVector3D actualPosition = law.getTargetPosition(mockedPVProvider, date, frame); + // THEN + Assertions.assertEquals(expectedPosition, actualPosition); + } + + @Test + void testGetTargetPV() { + // GIVEN + final GroundPointing mockedLaw = Mockito.mock(GroundPointing.class); + final TestYawLaw law = new TestYawLaw(mockedLaw); + final PVCoordinatesProvider mockedPVProvider = Mockito.mock(PVCoordinatesProvider.class); + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final Frame frame = Mockito.mock(Frame.class); + final TimeStampedPVCoordinates expectedPvCoordinates = new TimeStampedPVCoordinates(date, new PVCoordinates(Vector3D.MINUS_I, + Vector3D.PLUS_J)); + Mockito.when(mockedLaw.getTargetPV(mockedPVProvider, date, frame)).thenReturn(expectedPvCoordinates); + // WHEN + final TimeStampedPVCoordinates actualPVCoordinates = law.getTargetPV(mockedPVProvider, date, frame); + // THEN + Assertions.assertEquals(expectedPvCoordinates.getPosition(), actualPVCoordinates.getPosition()); + Assertions.assertEquals(expectedPvCoordinates.getVelocity(), actualPVCoordinates.getVelocity()); + } + + @SuppressWarnings("unchecked") + @Test + void testGetTargetPVField() { + // GIVEN + final GroundPointing mockedLaw = Mockito.mock(GroundPointing.class); + final TestYawLaw law = new TestYawLaw(mockedLaw); + final ComplexField field = ComplexField.getInstance(); + final FieldPVCoordinatesProvider mockedPVProvider = Mockito.mock(FieldPVCoordinatesProvider.class); + final FieldAbsoluteDate date = new FieldAbsoluteDate<>(field, AbsoluteDate.ARBITRARY_EPOCH); + final Frame frame = Mockito.mock(Frame.class); + final TimeStampedFieldPVCoordinates expectedPV = new TimeStampedFieldPVCoordinates<>(field, + new TimeStampedPVCoordinates(date.toAbsoluteDate(), Vector3D.MINUS_I, Vector3D.MINUS_K, Vector3D.ZERO)); + Mockito.when(mockedLaw.getTargetPV(mockedPVProvider, date, frame)).thenReturn(expectedPV); + // WHEN + final TimeStampedFieldPVCoordinates actualPV = law.getTargetPV(mockedPVProvider, date, frame); + // THEN + Assertions.assertEquals(expectedPV.getPosition(), actualPV.getPosition()); + Assertions.assertEquals(expectedPV.getVelocity(), actualPV.getVelocity()); + } + + @Test + void testGetBaseState() { + // GIVEN + final GroundPointing mockedLaw = Mockito.mock(GroundPointing.class); + final TestYawLaw law = new TestYawLaw(mockedLaw); + final PVCoordinatesProvider mockedPVProvider = Mockito.mock(PVCoordinatesProvider.class); + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final Frame frame = Mockito.mock(Frame.class); + final Attitude mockedAttitude = Mockito.mock(Attitude.class); + Mockito.when(mockedLaw.getAttitude(mockedPVProvider, date, frame)).thenReturn(mockedAttitude); + // WHEN + final Attitude attitude = law.getBaseState(mockedPVProvider, date, frame); + // THEN + Assertions.assertEquals(mockedAttitude, attitude); + } + + @SuppressWarnings("unchecked") + @Test + void testGetBaseStateField() { + // GIVEN + final GroundPointing mockedLaw = Mockito.mock(GroundPointing.class); + final TestYawLaw law = new TestYawLaw(mockedLaw); + final ComplexField field = ComplexField.getInstance(); + final FieldPVCoordinatesProvider mockedPVProvider = Mockito.mock(FieldPVCoordinatesProvider.class); + final FieldAbsoluteDate date = new FieldAbsoluteDate<>(field, AbsoluteDate.ARBITRARY_EPOCH); + final Frame frame = Mockito.mock(Frame.class); + final FieldAttitude mockedAttitude = Mockito.mock(FieldAttitude.class); + Mockito.when(mockedLaw.getAttitude(mockedPVProvider, date, frame)).thenReturn(mockedAttitude); + // WHEN + final FieldAttitude attitude = law.getBaseState(mockedPVProvider, date, frame); + // THEN + Assertions.assertEquals(mockedAttitude, attitude); + } + + private static class TestYawLaw extends GroundPointingAttitudeModifier { + + protected TestYawLaw(final GroundPointing groundPointingLaw) { + super(FramesFactory.getEME2000(), Mockito.mock(Frame.class), groundPointingLaw); + } + + } + +} diff --git a/src/test/java/org/orekit/attitudes/GroundPointingTest.java b/src/test/java/org/orekit/attitudes/GroundPointingTest.java index 6912a5577c..1e7f082413 100644 --- a/src/test/java/org/orekit/attitudes/GroundPointingTest.java +++ b/src/test/java/org/orekit/attitudes/GroundPointingTest.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2024 Romain Serra +/* 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. @@ -38,8 +38,14 @@ import org.orekit.orbits.PositionAngleType; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; -import org.orekit.utils.*; - +import org.orekit.utils.Constants; +import org.orekit.utils.FieldPVCoordinates; +import org.orekit.utils.FieldPVCoordinatesProvider; +import org.orekit.utils.IERSConventions; +import org.orekit.utils.PVCoordinates; +import org.orekit.utils.PVCoordinatesProvider; +import org.orekit.utils.TimeStampedFieldPVCoordinates; +import org.orekit.utils.TimeStampedPVCoordinates; class GroundPointingTest { @@ -72,8 +78,9 @@ public TimeStampedPVCoordinates getTargetPV(PVCoordinatesProvider pvProv, Absolu } @Override - public > TimeStampedFieldPVCoordinates getTargetPV(FieldPVCoordinatesProvider pvProv, FieldAbsoluteDate date, Frame frame) { - return new TimeStampedFieldPVCoordinates(date, FieldPVCoordinates.getZero(date.getField())); + public > TimeStampedFieldPVCoordinates + getTargetPV(FieldPVCoordinatesProvider pvProv, FieldAbsoluteDate date, Frame frame) { + return new TimeStampedFieldPVCoordinates<>(date, FieldPVCoordinates.getZero(date.getField())); } } @@ -195,11 +202,11 @@ private > FieldEquinoctialOrbit convertToFi final EquinoctialOrbit orbit) { final T zero = field.getZero(); final T fieldSemiMajorAxis = zero.add(orbit.getA()); - final FieldAbsoluteDate fieldDate = new FieldAbsoluteDate(field, orbit.getDate()); + final FieldAbsoluteDate fieldDate = new FieldAbsoluteDate<>(field, orbit.getDate()); final PositionAngleType positionAngleType = PositionAngleType.MEAN; final T fieldAngle = zero.add(orbit.getL(positionAngleType)); return new FieldEquinoctialOrbit<>(fieldSemiMajorAxis, zero, zero, zero, zero, fieldAngle, positionAngleType, orbit.getFrame(), fieldDate, zero.add(orbit.getMu())); } -} \ No newline at end of file +} diff --git a/src/test/java/org/orekit/attitudes/LofOffsetPointingTest.java b/src/test/java/org/orekit/attitudes/LofOffsetPointingTest.java index a91096b0fc..a6ddcecff8 100644 --- a/src/test/java/org/orekit/attitudes/LofOffsetPointingTest.java +++ b/src/test/java/org/orekit/attitudes/LofOffsetPointingTest.java @@ -18,6 +18,8 @@ import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; +import org.hipparchus.complex.Complex; +import org.hipparchus.complex.ComplexField; import org.hipparchus.geometry.euclidean.threed.*; import org.hipparchus.util.Binary64Field; import org.hipparchus.util.FastMath; @@ -32,11 +34,7 @@ import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; import org.orekit.frames.LOFType; -import org.orekit.orbits.CircularOrbit; -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.FieldSpacecraftState; import org.orekit.propagation.Propagator; import org.orekit.propagation.SpacecraftState; @@ -52,7 +50,7 @@ import org.orekit.utils.TimeStampedPVCoordinates; -public class LofOffsetPointingTest { +class LofOffsetPointingTest { // Computation date private AbsoluteDate date; @@ -69,7 +67,7 @@ public class LofOffsetPointingTest { /** Test if both constructors are equivalent */ @Test - public void testLof() { + void testLof() { // Satellite position final CircularOrbit circ = @@ -81,7 +79,8 @@ public void testLof() { //************************ final LofOffset lofLaw = new LofOffset(circ.getFrame(), LOFType.LVLH_CCSDS); final LofOffsetPointing lofPointing = new LofOffsetPointing(circ.getFrame(), earthSpheric, lofLaw, Vector3D.PLUS_K); - final Rotation lofRot = lofPointing.getAttitude(circ, date, circ.getFrame()).getRotation(); + final Rotation + lofRot = lofPointing.getAttitude(circ, date, circ.getFrame()).getRotation(); // Compare to body center pointing law //************************************* @@ -100,7 +99,7 @@ public void testLof() { } @Test - public void testMiss() { + void testMiss() { final CircularOrbit circ = new CircularOrbit(7178000.0, 0.5e-4, -0.5e-4, FastMath.toRadians(0.), FastMath.toRadians(270.), FastMath.toRadians(5.300), PositionAngleType.MEAN, @@ -116,9 +115,9 @@ public void testMiss() { } @Test - public void testSpin() { + void testSpin() { - AbsoluteDate date = new AbsoluteDate(new DateComponents(1970, 01, 01), + AbsoluteDate date = new AbsoluteDate(new DateComponents(1970, 1, 1), new TimeComponents(3, 25, 45.6789), TimeScalesFactory.getUTC()); KeplerianOrbit orbit = @@ -161,8 +160,8 @@ public void testSpin() { } @Test - public void testTypesField() { - AbsoluteDate date = new AbsoluteDate(new DateComponents(1970, 01, 01), + void testTypesField() { + AbsoluteDate date = new AbsoluteDate(new DateComponents(1970, 1, 1), new TimeComponents(3, 25, 45.6789), TimeScalesFactory.getUTC()); KeplerianOrbit orbit = @@ -233,7 +232,7 @@ private > void checkField(final Field field } @Test - public void testGetAttitudeRotation() { + void testGetAttitudeRotation() { // GIVEN final CircularOrbit circ = new CircularOrbit(7178000.0, 0.5e-4, -0.5e-4, FastMath.toRadians(0.), FastMath.toRadians(270.), @@ -248,6 +247,39 @@ public void testGetAttitudeRotation() { Assertions.assertEquals(0., Rotation.distance(expectedRotation, actualRotation)); } + @Test + void testGetTargetPosition() { + // GIVEN + final CircularOrbit circ = + new CircularOrbit(7178000.0, 0.5e-4, -0.5e-4, FastMath.toRadians(0.), FastMath.toRadians(270.), + FastMath.toRadians(5.300), PositionAngleType.MEAN, + FramesFactory.getEME2000(), date, mu); + final LofOffset upsideDown = new LofOffset(circ.getFrame(), LOFType.QSW, RotationOrder.XYX, -1., 2., 3.); + final LofOffsetPointing pointing = new LofOffsetPointing(circ.getFrame(), earthSpheric, upsideDown, Vector3D.PLUS_K); + // WHEN + final Vector3D targetPosition = pointing.getTargetPosition(circ, circ.getDate(), circ.getFrame()); + // THEN + final Vector3D expectedPosition = pointing.getTargetPV(circ, circ.getDate(), circ.getFrame()).getPosition(); + Assertions.assertEquals(expectedPosition, targetPosition); + } + + @Test + void testGetTargetPositionField() { + // GIVEN + final CircularOrbit circ = + new CircularOrbit(7178000.0, 0.5e-4, -0.5e-4, FastMath.toRadians(0.), FastMath.toRadians(270.), + FastMath.toRadians(5.300), PositionAngleType.MEAN, + FramesFactory.getEME2000(), date, mu); + final LofOffset upsideDown = new LofOffset(circ.getFrame(), LOFType.QSW, RotationOrder.XYX, 0, 2,0.3); + final LofOffsetPointing pointing = new LofOffsetPointing(circ.getFrame(), earthSpheric, upsideDown, Vector3D.PLUS_K); + final FieldCircularOrbit fieldOrbit = new FieldCircularOrbit<>(ComplexField.getInstance(), circ); + // WHEN + final FieldVector3D targetPosition = pointing.getTargetPosition(fieldOrbit, fieldOrbit.getDate(), fieldOrbit.getFrame()); + // THEN + final FieldVector3D expectedPosition = pointing.getTargetPV(fieldOrbit, fieldOrbit.getDate(), fieldOrbit.getFrame()).getPosition(); + Assertions.assertEquals(0., expectedPosition.subtract(targetPosition).getNorm().getReal(), 1e-10); + } + @BeforeEach public void setUp() { try { @@ -255,7 +287,7 @@ public void setUp() { Utils.setDataRoot("regular-data"); // Computation date - date = new AbsoluteDate(new DateComponents(2008, 04, 07), + date = new AbsoluteDate(new DateComponents(2008, 4, 7), TimeComponents.H00, TimeScalesFactory.getUTC()); diff --git a/src/test/java/org/orekit/attitudes/LofOffsetTest.java b/src/test/java/org/orekit/attitudes/LofOffsetTest.java index 339b0353a8..abbc97550d 100644 --- a/src/test/java/org/orekit/attitudes/LofOffsetTest.java +++ b/src/test/java/org/orekit/attitudes/LofOffsetTest.java @@ -20,7 +20,11 @@ import org.hipparchus.Field; import org.hipparchus.analysis.differentiation.GradientField; import org.hipparchus.complex.ComplexField; -import org.hipparchus.geometry.euclidean.threed.*; +import org.hipparchus.geometry.euclidean.threed.FieldRotation; +import org.hipparchus.geometry.euclidean.threed.Rotation; +import org.hipparchus.geometry.euclidean.threed.RotationConvention; +import org.hipparchus.geometry.euclidean.threed.RotationOrder; +import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.Binary64Field; import org.hipparchus.util.FastMath; import org.junit.jupiter.api.AfterEach; @@ -71,6 +75,18 @@ public class LofOffsetTest { CircularOrbit orbit; PVCoordinates pvSatEME2000; + /** + * Testing of the getters. + */ + @Test + public void testGetters() { + final Rotation expectedRotation = new Rotation(RotationOrder.XYZ, RotationConvention.VECTOR_OPERATOR, 0, 0, 0).revert(); + final LofOffset lofOffset = new LofOffset(orbit.getFrame(), LOFType.LVLH_CCSDS); + Assertions.assertEquals(LOFType.LVLH_CCSDS, lofOffset.getLof()); + Assertions.assertEquals(orbit.getFrame(), lofOffset.getInertialFrame()); + Assertions.assertEquals(expectedRotation.getAngle(), lofOffset.getOffset().getAngle()); + } + /** Test is the lof offset is the one expected */ @Test @@ -172,7 +188,7 @@ public void testSpin() { final AttitudeProvider law = new LofOffset(orbit.getFrame(), LOFType.LVLH_CCSDS, RotationOrder.XYX, 0.1, 0.2, 0.3); - AbsoluteDate date = new AbsoluteDate(new DateComponents(1970, 01, 01), + AbsoluteDate date = new AbsoluteDate(new DateComponents(1970, 1, 1), new TimeComponents(3, 25, 45.6789), TimeScalesFactory.getUTC()); KeplerianOrbit orbit = @@ -211,7 +227,7 @@ public void testSpin() { @Test public void testAnglesSign() { - AbsoluteDate date = new AbsoluteDate(new DateComponents(1970, 01, 01), + AbsoluteDate date = new AbsoluteDate(new DateComponents(1970, 1, 1), new TimeComponents(3, 25, 45.6789), TimeScalesFactory.getUTC()); KeplerianOrbit orbit = @@ -246,7 +262,7 @@ public void testAnglesSign() { @Test public void testRetrieveAngles() { - AbsoluteDate date = new AbsoluteDate(new DateComponents(1970, 01, 01), + AbsoluteDate date = new AbsoluteDate(new DateComponents(1970, 1, 1), new TimeComponents(3, 25, 45.6789), TimeScalesFactory.getUTC()); KeplerianOrbit orbit = @@ -275,7 +291,7 @@ public void testRetrieveAngles() { @Test public void testTypesField() { - AbsoluteDate date = new AbsoluteDate(new DateComponents(1970, 01, 01), + AbsoluteDate date = new AbsoluteDate(new DateComponents(1970, 1, 1), new TimeComponents(3, 25, 45.6789), TimeScalesFactory.getUTC()); KeplerianOrbit orbit = @@ -302,9 +318,9 @@ private void checkSatVector(Orbit o, Attitude a, Vector3D satVector, Vector3D xLof = Vector3D.crossProduct(yLof, zLof); Assertions.assertTrue(Vector3D.dotProduct(xLof, o.getPVCoordinates().getVelocity()) > 0); Vector3D v = a.getRotation().applyInverseTo(satVector); - Assertions.assertEquals(expectedX, Vector3D.dotProduct(v, xLof), 1.0e-8); - Assertions.assertEquals(expectedY, Vector3D.dotProduct(v, yLof), 1.0e-8); - Assertions.assertEquals(expectedZ, Vector3D.dotProduct(v, zLof), 1.0e-8); + Assertions.assertEquals(expectedX, Vector3D.dotProduct(v, xLof), threshold); + Assertions.assertEquals(expectedY, Vector3D.dotProduct(v, yLof), threshold); + Assertions.assertEquals(expectedZ, Vector3D.dotProduct(v, zLof), threshold); } private > void checkField(final Field field, final AttitudeProvider provider, @@ -363,7 +379,7 @@ public void setUp() { Utils.setDataRoot("regular-data"); // Computation date - date = new AbsoluteDate(new DateComponents(2008, 04, 07), + date = new AbsoluteDate(new DateComponents(2008, 4, 7), TimeComponents.H00, TimeScalesFactory.getUTC()); diff --git a/src/test/java/org/orekit/attitudes/NadirPointingTest.java b/src/test/java/org/orekit/attitudes/NadirPointingTest.java index 415a6c7d2e..0aa70aaabd 100644 --- a/src/test/java/org/orekit/attitudes/NadirPointingTest.java +++ b/src/test/java/org/orekit/attitudes/NadirPointingTest.java @@ -18,6 +18,9 @@ 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.Rotation; import org.hipparchus.geometry.euclidean.threed.RotationConvention; import org.hipparchus.geometry.euclidean.threed.Vector3D; @@ -33,11 +36,7 @@ import org.orekit.errors.OrekitException; import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; -import org.orekit.orbits.CircularOrbit; -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.FieldSpacecraftState; import org.orekit.propagation.Propagator; import org.orekit.propagation.SpacecraftState; @@ -48,18 +47,13 @@ import org.orekit.time.TimeComponents; import org.orekit.time.TimeInterpolator; import org.orekit.time.TimeScalesFactory; -import org.orekit.utils.AngularCoordinates; -import org.orekit.utils.CartesianDerivativesFilter; -import org.orekit.utils.IERSConventions; -import org.orekit.utils.TimeStampedFieldPVCoordinates; -import org.orekit.utils.TimeStampedPVCoordinates; -import org.orekit.utils.TimeStampedPVCoordinatesHermiteInterpolator; +import org.orekit.utils.*; import java.util.ArrayList; import java.util.List; -public class NadirPointingTest { +class NadirPointingTest { // Computation date private AbsoluteDate date; @@ -70,11 +64,98 @@ public class NadirPointingTest { // Reference frame = ITRF private Frame itrf; + @Test + void testGetTargetPV() { + // GIVEN + final OneAxisEllipsoid earthShape = new OneAxisEllipsoid(6378136.460, 0., itrf); + final NadirPointing nadirAttitudeLaw = new NadirPointing(FramesFactory.getEME2000(), earthShape); + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final Frame frame = FramesFactory.getGCRF(); + final PVCoordinatesProvider provider = (date1, frame1) -> { + final double duration = date1.durationFrom(AbsoluteDate.FIFTIES_EPOCH); + final Vector3D position = new Vector3D(duration * duration / 2, 0., 0.); + final Vector3D velocity = new Vector3D(duration, 0., 0.); + final Vector3D acceleration = new Vector3D(1, 0, 0); + return new TimeStampedPVCoordinates(date1, position, velocity, acceleration); + }; + // WHEN + final TimeStampedPVCoordinates actualPV = nadirAttitudeLaw.getTargetPV(provider, date, frame); + // THEN + final PVCoordinatesProvider providerWithoutAcceleration = (date12, frame12) -> { + final TimeStampedPVCoordinates originalPV = provider.getPVCoordinates(date12, frame12); + return new TimeStampedPVCoordinates(date12, originalPV.getPosition(), originalPV.getVelocity()); + }; + final TimeStampedPVCoordinates pv = nadirAttitudeLaw.getTargetPV(providerWithoutAcceleration, date, frame); + Assertions.assertEquals(pv.getDate(), actualPV.getDate()); + final PVCoordinates relativePV = new PVCoordinates(pv, actualPV); + Assertions.assertEquals(0., relativePV.getPosition().getNorm(), 2e-9); + Assertions.assertEquals(0., relativePV.getVelocity().getNorm(), 1e-7); + } + + @Test + void testGetTargetPVViaInterpolationField() { + // GIVEN + final OneAxisEllipsoid earthShape = new OneAxisEllipsoid(6378136.460, 0., itrf); + final NadirPointing nadirAttitudeLaw = new NadirPointing(FramesFactory.getEME2000(), earthShape); + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final Frame frame = FramesFactory.getGCRF(); + final PVCoordinatesProvider provider = (dateIn, frameIn) -> new TimeStampedPVCoordinates(dateIn, + new Vector3D(dateIn.durationFrom(AbsoluteDate.FIFTIES_EPOCH), 0., 0.), Vector3D.PLUS_I); + final ComplexField field = ComplexField.getInstance(); + final FieldAbsoluteDate fieldDate = new FieldAbsoluteDate<>(field, date); + final FieldPVCoordinatesProvider fieldProvider = (dateIn, frameIn) -> new TimeStampedFieldPVCoordinates<>(dateIn, + new FieldVector3D<>(dateIn.durationFrom(AbsoluteDate.FIFTIES_EPOCH), field.getZero(), field.getZero()), + FieldVector3D.getPlusI(field), FieldVector3D.getZero(field)); + // WHEN + final TimeStampedFieldPVCoordinates actualPV = nadirAttitudeLaw.getTargetPV(fieldProvider, fieldDate, frame); + // THEN + final TimeStampedPVCoordinates pv = nadirAttitudeLaw.getTargetPV(provider, date, frame); + Assertions.assertEquals(pv.getDate(), actualPV.getDate().toAbsoluteDate()); + final PVCoordinates relativePV = new PVCoordinates(pv, actualPV.toPVCoordinates()); + final Vector3D positionDifference = relativePV.getPosition(); + Assertions.assertEquals(0., positionDifference.getNorm(), 1e-9); + final Vector3D velocityDifference = relativePV.getVelocity(); + Assertions.assertEquals(0., velocityDifference.getNorm(), 1e-6); + } + + @Test + void testGetTargetPosition() { + // GIVEN + final OneAxisEllipsoid earthShape = new OneAxisEllipsoid(6378136.460, 0., itrf); + final NadirPointing nadirAttitudeLaw = new NadirPointing(FramesFactory.getEME2000(), earthShape); + final CircularOrbit circ = + new CircularOrbit(7178000.0, 0.5e-4, -0.5e-4, FastMath.toRadians(50.), FastMath.toRadians(270.), + FastMath.toRadians(5.300), PositionAngleType.MEAN, + FramesFactory.getEME2000(), date, mu); + // WHEN + final Vector3D actualPosition = nadirAttitudeLaw.getTargetPosition(circ, circ.getDate(), circ.getFrame()); + // THEN + final Vector3D expectedPosition = nadirAttitudeLaw.getTargetPV(circ, circ.getDate(), circ.getFrame()).getPosition(); + Assertions.assertEquals(0., expectedPosition.subtract(actualPosition).getNorm(), 1e-9); + } + + @Test + void testGetTargetPositionField() { + // GIVEN + final OneAxisEllipsoid earthShape = new OneAxisEllipsoid(6378136.460, 0., itrf); + final NadirPointing nadirAttitudeLaw = new NadirPointing(FramesFactory.getEME2000(), earthShape); + final CircularOrbit circ = + new CircularOrbit(7178000.0, 0.5e-4, -0.5e-4, FastMath.toRadians(50.), FastMath.toRadians(270.), + FastMath.toRadians(5.300), PositionAngleType.MEAN, + FramesFactory.getEME2000(), date, mu); + final FieldCircularOrbit fieldOrbit = new FieldCircularOrbit<>(ComplexField.getInstance(), circ); + // WHEN + final FieldVector3D actualPosition = nadirAttitudeLaw.getTargetPosition(fieldOrbit, fieldOrbit.getDate(), fieldOrbit.getFrame()); + // THEN + final FieldVector3D expectedPosition = nadirAttitudeLaw.getTargetPV(fieldOrbit, fieldOrbit.getDate(), fieldOrbit.getFrame()).getPosition(); + Assertions.assertEquals(0., expectedPosition.subtract(actualPosition).getNorm().getReal(), 1e-9); + } + /** Test in the case of a spheric earth : nadir pointing shall be * the same as earth center pointing */ @Test - public void testSphericEarth() { + void testSphericEarth() { // Spheric earth shape OneAxisEllipsoid earthShape = new OneAxisEllipsoid(6378136.460, 0., itrf); @@ -112,7 +193,7 @@ public void testSphericEarth() { * - different from earth center pointing in any other case */ @Test - public void testNonSphericEarth() { + void testNonSphericEarth() { // Elliptic earth shape OneAxisEllipsoid earthShape = new OneAxisEllipsoid(6378136.460, 1 / 298.257222101, itrf); @@ -193,7 +274,7 @@ public void testNonSphericEarth() { but that's what is to test. */ @Test - public void testVertical() { + void testVertical() { // Elliptic earth shape OneAxisEllipsoid earthShape = new OneAxisEllipsoid(6378136.460, 1 / 298.257222101, itrf); @@ -237,7 +318,7 @@ public void testVertical() { /** Test the derivatives of the sliding target */ @Test - public void testSlidingDerivatives() { + void testSlidingDerivatives() { // Elliptic earth shape OneAxisEllipsoid earthShape = new OneAxisEllipsoid(6378136.460, 1 / 298.257222101, itrf); @@ -279,7 +360,7 @@ public void testSlidingDerivatives() { } @Test - public void testSpin() { + void testSpin() { // Elliptic earth shape OneAxisEllipsoid earthShape = new OneAxisEllipsoid(6378136.460, 1 / 298.257222101, itrf); diff --git a/src/test/java/org/orekit/attitudes/SpinStabilizedTest.java b/src/test/java/org/orekit/attitudes/SpinStabilizedTest.java index 8c2473056a..c7375f1ae4 100644 --- a/src/test/java/org/orekit/attitudes/SpinStabilizedTest.java +++ b/src/test/java/org/orekit/attitudes/SpinStabilizedTest.java @@ -18,6 +18,9 @@ import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; +import org.hipparchus.complex.Complex; +import org.hipparchus.complex.ComplexField; +import org.hipparchus.geometry.euclidean.threed.FieldRotation; import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.Binary64Field; @@ -29,10 +32,7 @@ import org.orekit.bodies.CelestialBodyFactory; import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; -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.FieldSpacecraftState; import org.orekit.propagation.Propagator; import org.orekit.propagation.SpacecraftState; @@ -43,13 +43,14 @@ import org.orekit.time.TimeComponents; import org.orekit.time.TimeScalesFactory; import org.orekit.utils.AngularCoordinates; +import org.orekit.utils.Constants; import org.orekit.utils.PVCoordinates; import org.orekit.utils.PVCoordinatesProvider; -public class SpinStabilizedTest { +class SpinStabilizedTest { @Test - public void testBBQMode() { + void testBBQMode() { PVCoordinatesProvider sun = CelestialBodyFactory.getSun(); AbsoluteDate date = new AbsoluteDate(new DateComponents(1970, 01, 01), new TimeComponents(3, 25, 45.6789), @@ -74,7 +75,44 @@ public void testBBQMode() { } @Test - public void testSpin() { + void testGetAttitudeRotation() { + // GIVEN + final Orbit orbit = getOrbit(); + final AttitudeProvider baseProvider = new FrameAlignedProvider(FramesFactory.getEME2000()); + final AbsoluteDate startDate = orbit.getDate().shiftedBy(-10.); + final SpinStabilized spinStabilized = new SpinStabilized(baseProvider, startDate, Vector3D.PLUS_K, 0.1); + // WHEN + final Rotation rotation = spinStabilized.getAttitudeRotation(orbit, orbit.getDate(), orbit.getFrame()); + // THEN + final Attitude attitude = spinStabilized.getAttitude(orbit, orbit.getDate(), orbit.getFrame()); + Assertions.assertEquals(0., Rotation.distance(rotation, attitude.getRotation())); + } + + @Test + void testFieldGetAttitudeRotation() { + // GIVEN + final CartesianOrbit orbit = getOrbit(); + final AbsoluteDate startDate = orbit.getDate().shiftedBy(-10.); + + final AttitudeProvider baseProvider = new FrameAlignedProvider(FramesFactory.getEME2000()); + final SpinStabilized spinStabilized = new SpinStabilized(baseProvider, startDate, Vector3D.PLUS_K, 0.1); + final FieldOrbit fieldOrbit = new FieldCircularOrbit<>(ComplexField.getInstance(), orbit); + // WHEN + final FieldRotation rotation = spinStabilized.getAttitudeRotation(fieldOrbit, fieldOrbit.getDate(), fieldOrbit.getFrame()); + // THEN + final FieldAttitude attitude = spinStabilized.getAttitude(fieldOrbit, fieldOrbit.getDate(), fieldOrbit.getFrame()); + Assertions.assertEquals(0., Rotation.distance(rotation.toRotation(), attitude.getRotation().toRotation())); + } + + private CartesianOrbit getOrbit() { + final PVCoordinates pv = + new PVCoordinates(new Vector3D(28812595.32012577, 5948437.4640250085, 0), + new Vector3D(0, 0, 3680.853673522056)); + return new CartesianOrbit(pv, FramesFactory.getGCRF(), AbsoluteDate.ARBITRARY_EPOCH, Constants.EIGEN5C_EARTH_MU); + } + + @Test + void testSpin() { AbsoluteDate date = new AbsoluteDate(new DateComponents(1970, 01, 01), new TimeComponents(3, 25, 45.6789), diff --git a/src/test/java/org/orekit/attitudes/TargetPointingTest.java b/src/test/java/org/orekit/attitudes/TargetPointingTest.java index 619be80f3f..b541aed1a3 100644 --- a/src/test/java/org/orekit/attitudes/TargetPointingTest.java +++ b/src/test/java/org/orekit/attitudes/TargetPointingTest.java @@ -18,10 +18,9 @@ import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; -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.Binary64Field; import org.hipparchus.util.FastMath; import org.junit.jupiter.api.AfterEach; @@ -36,11 +35,7 @@ import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; import org.orekit.frames.StaticTransform; -import org.orekit.orbits.CircularOrbit; -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.FieldSpacecraftState; import org.orekit.propagation.Propagator; import org.orekit.propagation.SpacecraftState; @@ -58,7 +53,7 @@ import org.orekit.utils.TimeStampedPVCoordinates; -public class TargetPointingTest { +class TargetPointingTest { // Computation date private AbsoluteDate date; @@ -75,7 +70,7 @@ public class TargetPointingTest { /** Test if both constructors are equivalent */ @Test - public void testConstructors() { + void testConstructors() { // Satellite position // ******************** @@ -116,7 +111,7 @@ public void testConstructors() { /** Test if geodetic constructor works */ @Test - public void testGeodeticConstructor() { + void testGeodeticConstructor() { // Satellite position // ******************** @@ -148,7 +143,7 @@ public void testGeodeticConstructor() { } @Test - public void testIssue115() { + void testIssue115() { // Satellite position // ******************** @@ -179,7 +174,7 @@ public void testIssue115() { } @Test - public void testWrongFrame() { + void testWrongFrame() { try { // in the following line, the frames have been intentionnally reversed new TargetPointing(itrf, FramesFactory.getEME2000(), @@ -195,7 +190,7 @@ public void testWrongFrame() { * satellite attitude is the same as nadir attitude at the same date, but different at a different date. */ @Test - public void testNadirTarget() { + void testNadirTarget() { // Elliptic earth shape OneAxisEllipsoid earthShape = new OneAxisEllipsoid(6378136.460, 1 / 298.257222101, itrf); @@ -262,7 +257,7 @@ public void testNadirTarget() { /** Test if defined target belongs to the direction pointed by the satellite */ @Test - public void testTargetInPointingDirection() { + void testTargetInPointingDirection() { // Create computation date AbsoluteDate date = new AbsoluteDate(new DateComponents(2008, 04, 07), @@ -314,7 +309,7 @@ public void testTargetInPointingDirection() { /** Test the difference between pointing over two longitudes separated by 5° */ @Test - public void testSlewedTarget() { + void testSlewedTarget() { // Spheric earth shape OneAxisEllipsoid earthShape = new OneAxisEllipsoid(6378136.460, 0., itrf); @@ -371,7 +366,7 @@ public void testSlewedTarget() { } @Test - public void testSpin() { + void testSpin() { Frame itrf = FramesFactory.getITRF(IERSConventions.IERS_2010, true); @@ -436,6 +431,38 @@ private > void checkField(final Field field } + + @Test + void testGetTargetPosition() { + // GIVEN + final CircularOrbit circOrbit = + new CircularOrbit(7178000.0, 1.e-5, 0., FastMath.toRadians(50.), 0., + FastMath.toRadians(90.), PositionAngleType.TRUE, FramesFactory.getEME2000(), date, mu); + final TargetPointing law = new TargetPointing(circOrbit.getFrame(), FramesFactory.getGTOD(false), + Vector3D.PLUS_J); + // WHEN + final Vector3D actualPosition = law.getTargetPosition(circOrbit, circOrbit.getDate(), circOrbit.getFrame()); + // THEN + final Vector3D expectedPosition = law.getTargetPV(circOrbit, circOrbit.getDate(), circOrbit.getFrame()).getPosition(); + Assertions.assertEquals(expectedPosition, actualPosition); + } + + @Test + void testGetTargetPositionField() { + // GIVEN + final CircularOrbit circOrbit = + new CircularOrbit(7178000.0, 1.e-5, 0., FastMath.toRadians(50.), 0., + FastMath.toRadians(90.), PositionAngleType.TRUE, FramesFactory.getEME2000(), date, mu); + final TargetPointing law = new TargetPointing(circOrbit.getFrame(), FramesFactory.getGTOD(false), + Vector3D.PLUS_J); + final FieldCircularOrbit fieldOrbit = new FieldCircularOrbit<>(ComplexField.getInstance(), circOrbit); + // WHEN + final FieldVector3D actualPosition = law.getTargetPosition(fieldOrbit, fieldOrbit.getDate(), fieldOrbit.getFrame()); + // THEN + final FieldVector3D expectedPosition = law.getTargetPV(fieldOrbit, fieldOrbit.getDate(), fieldOrbit.getFrame()).getPosition(); + Assertions.assertEquals(0., actualPosition.subtract(expectedPosition).getNorm().getReal(), 1e-10); + } + @BeforeEach public void setUp() { try { diff --git a/src/test/java/org/orekit/attitudes/YawCompensationTest.java b/src/test/java/org/orekit/attitudes/YawCompensationTest.java index 3d48537446..344ae980d4 100644 --- a/src/test/java/org/orekit/attitudes/YawCompensationTest.java +++ b/src/test/java/org/orekit/attitudes/YawCompensationTest.java @@ -18,10 +18,9 @@ import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; -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.Binary64Field; import org.hipparchus.util.FastMath; import org.junit.jupiter.api.AfterEach; @@ -35,11 +34,7 @@ import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; import org.orekit.frames.Transform; -import org.orekit.orbits.CircularOrbit; -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.FieldSpacecraftState; import org.orekit.propagation.Propagator; import org.orekit.propagation.SpacecraftState; @@ -63,7 +58,7 @@ import java.util.List; -public class YawCompensationTest { +class YawCompensationTest { // Computation date private AbsoluteDate date; @@ -80,7 +75,7 @@ public class YawCompensationTest { /** Test that pointed target remains the same with or without yaw compensation */ @Test - public void testTarget() { + void testTarget() { // Attitude laws // ************** @@ -111,7 +106,7 @@ public void testTarget() { /** Test the derivatives of the sliding target */ @Test - public void testSlidingDerivatives() { + void testSlidingDerivatives() { GroundPointing law = new YawCompensation(circOrbit.getFrame(), new NadirPointing(circOrbit.getFrame(), earthShape)); @@ -139,14 +134,14 @@ public void testSlidingDerivatives() { 3.0e-11 * reference.getVelocity().getNorm()); Assertions.assertEquals(0.0, Vector3D.distance(reference.getAcceleration(), target.getAcceleration()), - 7.0e-6 * reference.getAcceleration().getNorm()); + 1.0e-5 * reference.getAcceleration().getNorm()); } /** Test that pointed target motion is along -X sat axis */ @Test - public void testAlignment() { + void testAlignment() { GroundPointing notCompensated = new NadirPointing(circOrbit.getFrame(), earthShape); YawCompensation compensated = new YawCompensation(circOrbit.getFrame(), notCompensated); @@ -221,7 +216,7 @@ PVCoordinates toSpacecraft(PVCoordinates groundPoint, Orbit orbit, AttitudeProvi * and minimum yaw compensation is at maximum latitude. */ @Test - public void testCompensMinMax() { + void testCompensMinMax() { // Attitude laws // ************** @@ -300,7 +295,7 @@ public void testCompensMinMax() { /** Test that compensation rotation axis is Zsat, yaw axis */ @Test - public void testCompensAxis() { + void testCompensAxis() { // Attitude laws // ************** @@ -326,7 +321,7 @@ public void testCompensAxis() { } @Test - public void testSpin() { + void testSpin() { NadirPointing nadirLaw = new NadirPointing(circOrbit.getFrame(), earthShape); diff --git a/src/test/java/org/orekit/attitudes/YawSteeringTest.java b/src/test/java/org/orekit/attitudes/YawSteeringTest.java index 2aef27319f..bb0b1ff18a 100644 --- a/src/test/java/org/orekit/attitudes/YawSteeringTest.java +++ b/src/test/java/org/orekit/attitudes/YawSteeringTest.java @@ -33,11 +33,7 @@ import org.orekit.errors.OrekitException; import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; -import org.orekit.orbits.CircularOrbit; -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.FieldSpacecraftState; import org.orekit.propagation.Propagator; import org.orekit.propagation.SpacecraftState; @@ -61,7 +57,7 @@ import java.util.List; -public class YawSteeringTest { +class YawSteeringTest { // Computation date private AbsoluteDate date; @@ -76,7 +72,7 @@ public class YawSteeringTest { OneAxisEllipsoid earthShape; @Test - public void testTarget() { + void testTarget() { // Attitude laws // ************** @@ -105,7 +101,7 @@ public void testTarget() { } @Test - public void testSunAligned() { + void testSunAligned() { // Attitude laws // ************** @@ -127,7 +123,7 @@ public void testSunAligned() { } @Test - public void testCompensAxis() { + void testCompensAxis() { // Attitude laws // ************** @@ -158,7 +154,7 @@ public void testCompensAxis() { /** Test the derivatives of the sliding target */ @Test - public void testSlidingDerivatives() { + void testSlidingDerivatives() { GroundPointing law = new YawSteering(circOrbit.getFrame(), new NadirPointing(circOrbit.getFrame(), earthShape), @@ -193,7 +189,7 @@ public void testSlidingDerivatives() { } @Test - public void testSpin() { + void testSpin() { NadirPointing nadirLaw = new NadirPointing(circOrbit.getFrame(), earthShape); diff --git a/src/test/java/org/orekit/bodies/FieldGeodeticPointTest.java b/src/test/java/org/orekit/bodies/FieldGeodeticPointTest.java index 9a1370ca33..dd8e8bb018 100644 --- a/src/test/java/org/orekit/bodies/FieldGeodeticPointTest.java +++ b/src/test/java/org/orekit/bodies/FieldGeodeticPointTest.java @@ -19,6 +19,7 @@ import org.hipparchus.CalculusFieldElement; 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; import org.junit.jupiter.api.Test; @@ -32,16 +33,16 @@ public class FieldGeodeticPointTest { /** - * check {@link FieldGeodeticPoint#GeodeticPoint(CalculusFieldElement, CalculusFieldElement, CalculusFieldElement)} angle + * check {@link FieldGeodeticPoint#FieldGeodeticPoint(CalculusFieldElement, CalculusFieldElement, CalculusFieldElement)} angle * normalization. */ @Test public void testGeodeticPointAngleNormalization() { // action FieldGeodeticPoint point = - new FieldGeodeticPoint(new Binary64(FastMath.toRadians(135)), - new Binary64(FastMath.toRadians(90 - 360)), - new Binary64(0)); + new FieldGeodeticPoint<>(new Binary64(FastMath.toRadians(135)), + new Binary64(FastMath.toRadians(90 - 360)), + new Binary64(0)); // verify Assertions.assertEquals(FastMath.toRadians(45), point.getLatitude().getReal(), 1.0e-15); @@ -57,7 +58,7 @@ public void testGeodeticPointAngleNormalization() { } /** - * check {@link FieldGeodeticPoint#GeodeticPoint(CalculusFieldElement, CalculusFieldElement, CalculusFieldElement)} for + * check {@link FieldGeodeticPoint#FieldGeodeticPoint(CalculusFieldElement, CalculusFieldElement, CalculusFieldElement)} for * several different angles. */ @Test @@ -81,9 +82,9 @@ public void testGeodeticPoint() { for (double[] point : points) { // action FieldGeodeticPoint gp = - new FieldGeodeticPoint(new Binary64(point[0]), - new Binary64(point[1]), - Binary64.ZERO); + new FieldGeodeticPoint<>(new Binary64(point[0]), + new Binary64(point[1]), + Binary64.ZERO); Assertions.assertEquals(0, gp.getEast().crossProduct(gp.getNorth()).distance(gp.getZenith()).getReal(), 1.0e-15); Assertions.assertEquals(0, gp.getNorth().crossProduct(gp.getWest()).distance(gp.getZenith()).getReal(), 1.0e-15); Assertions.assertEquals(0, gp.getSouth().crossProduct(gp.getWest()).distance(gp.getNadir()).getReal(), 1.0e-15); @@ -104,32 +105,25 @@ public void testGeodeticPoint() { public void testEquals() { // setup FieldGeodeticPoint point = - new FieldGeodeticPoint(new Binary64(1), - new Binary64(2), - new Binary64(3)); + new FieldGeodeticPoint<>(new Binary64(1), + new Binary64(2), + new Binary64(3)); // actions + verify - Assertions.assertEquals(point, new FieldGeodeticPoint(new Binary64(1), - new Binary64(2), - new Binary64(3))); - Assertions.assertFalse(point.equals(new FieldGeodeticPoint(new Binary64(0), - new Binary64(2), - new Binary64(3)))); - Assertions.assertFalse(point.equals(new FieldGeodeticPoint(new Binary64(1), - new Binary64(0), - new Binary64(3)))); - Assertions.assertFalse(point.equals(new FieldGeodeticPoint(new Binary64(1), - new Binary64(2), - new Binary64(0)))); - Assertions.assertFalse(point.equals(new Object())); + Assertions.assertEquals(point, new FieldGeodeticPoint<>(Binary64Field.getInstance(), + new GeodeticPoint(1, 2, 3))); + Assertions.assertNotEquals(point, new FieldGeodeticPoint<>(new Binary64(0), new Binary64(2), new Binary64(3))); + Assertions.assertNotEquals(point, new FieldGeodeticPoint<>(new Binary64(1), new Binary64(0), new Binary64(3))); + Assertions.assertNotEquals(point,new FieldGeodeticPoint<>(new Binary64(1), new Binary64(2), new Binary64(0))); + Assertions.assertNotEquals(point, new Object()); Assertions.assertEquals(point.hashCode(), - new FieldGeodeticPoint(new Binary64(1), - new Binary64(2), - new Binary64(3)).hashCode()); + new FieldGeodeticPoint<>(new Binary64(1), + new Binary64(2), + new Binary64(3)).hashCode()); Assertions.assertNotEquals(point.hashCode(), - new FieldGeodeticPoint(new Binary64(1), - new Binary64(FastMath.nextUp(2)), - new Binary64(3)).hashCode()); + new FieldGeodeticPoint<>(new Binary64(1), + new Binary64(FastMath.nextUp(2)), + new Binary64(3)).hashCode()); } /** @@ -139,9 +133,9 @@ public void testEquals() { public void testToString() { // setup FieldGeodeticPoint point = - new FieldGeodeticPoint(new Binary64(FastMath.toRadians(30)), - new Binary64(FastMath.toRadians(60)), - new Binary64(90)); + new FieldGeodeticPoint<>(new Binary64(FastMath.toRadians(30)), + new Binary64(FastMath.toRadians(60)), + new Binary64(90)); // action String actual = point.toString(); diff --git a/src/test/java/org/orekit/bodies/PosVelChebyshevTest.java b/src/test/java/org/orekit/bodies/PosVelChebyshevTest.java index 97d941e5c6..dc0257f27a 100644 --- a/src/test/java/org/orekit/bodies/PosVelChebyshevTest.java +++ b/src/test/java/org/orekit/bodies/PosVelChebyshevTest.java @@ -1,3 +1,20 @@ +/* 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.bodies; import org.hipparchus.analysis.differentiation.Gradient; diff --git a/src/test/java/org/orekit/bodies/PredefinedIAUPolesTest.java b/src/test/java/org/orekit/bodies/PredefinedIAUPolesTest.java index 2cbc96aede..092ede0d91 100644 --- a/src/test/java/org/orekit/bodies/PredefinedIAUPolesTest.java +++ b/src/test/java/org/orekit/bodies/PredefinedIAUPolesTest.java @@ -82,50 +82,51 @@ public void testSun() throws UnsupportedEncodingException, IOException { public void testNaif() throws UnsupportedEncodingException, IOException { final TimeScale tdb = TimeScalesFactory.getTDB(); final InputStream inEntry = getClass().getResourceAsStream("/naif/IAU-pole-NAIF.txt"); - BufferedReader reader = new BufferedReader(new InputStreamReader(inEntry, StandardCharsets.UTF_8)); - for (String line = reader.readLine(); line != null; line = reader.readLine()) { - line = line.trim(); - if (!line.isEmpty() && !line.startsWith("#")) { - - // extract reference data from Naif - String[] fields = line.split("\\s+"); - final AbsoluteDate date1 = new AbsoluteDate(fields[0], tdb); - final AbsoluteDate date2 = new AbsoluteDate(AbsoluteDate.J2000_EPOCH, - Double.parseDouble(fields[1]), - tdb); - final EphemerisType type = EphemerisType.valueOf(fields[2]); - final double alphaRef = Double.parseDouble(fields[3]); - final double deltaRef = Double.parseDouble(fields[4]); - final double wRef = Double.parseDouble(fields[5]); - final double[][] m = new double[3][3]; - int index = 6; - for (int i = 0; i < 3; ++i) { - for (int j = 0; j < 3; ++j) { - // we transpose the matrix to get the transform - // from ICRF to body frame - m[j][i] = Double.parseDouble(fields[index++]); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(inEntry, StandardCharsets.UTF_8))) { + for (String line = reader.readLine(); line != null; line = reader.readLine()) { + line = line.trim(); + if (!line.isEmpty() && !line.startsWith("#")) { + + // extract reference data from Naif + String[] fields = line.split("\\s+"); + final AbsoluteDate date1 = new AbsoluteDate(fields[0], tdb); + final AbsoluteDate date2 = new AbsoluteDate(AbsoluteDate.J2000_EPOCH, + Double.parseDouble(fields[1]), + tdb); + final EphemerisType type = EphemerisType.valueOf(fields[2]); + final double alphaRef = Double.parseDouble(fields[3]); + final double deltaRef = Double.parseDouble(fields[4]); + final double wRef = Double.parseDouble(fields[5]); + final double[][] m = new double[3][3]; + int index = 6; + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + // we transpose the matrix to get the transform + // from ICRF to body frame + m[j][i] = Double.parseDouble(fields[index++]); + } } - } - Rotation rRef = new Rotation(m, 1.0e-10); - - // check pole - IAUPole iauPole = PredefinedIAUPoles.getIAUPole(type, timeScales); - Vector3D pole = iauPole.getPole(date2); - double w = iauPole.getPrimeMeridianAngle(date2); - Assertions.assertEquals(0.0, date2.durationFrom(date1), 8.0e-5); - Assertions.assertEquals(alphaRef, MathUtils.normalizeAngle(pole.getAlpha(), alphaRef), 1.8e-15); - Assertions.assertEquals(deltaRef, pole.getDelta(), 2.4e-13); - Assertions.assertEquals(wRef, MathUtils.normalizeAngle(w, wRef), 2.5e-12); - - // check matrix - Vector3D qNode = Vector3D.crossProduct(Vector3D.PLUS_K, pole); - if (qNode.getNormSq() < Precision.SAFE_MIN) { - qNode = Vector3D.PLUS_I; - } - final Rotation rotation = new Rotation(Vector3D.PLUS_K, wRef, RotationConvention.FRAME_TRANSFORM). - applyTo(new Rotation(pole, qNode, Vector3D.PLUS_K, Vector3D.PLUS_I)); - Assertions.assertEquals(0.0, Rotation.distance(rRef, rotation), 1.9e-15); + Rotation rRef = new Rotation(m, 1.0e-10); + + // check pole + IAUPole iauPole = PredefinedIAUPoles.getIAUPole(type, timeScales); + Vector3D pole = iauPole.getPole(date2); + double w = iauPole.getPrimeMeridianAngle(date2); + Assertions.assertEquals(0.0, date2.durationFrom(date1), 8.0e-5); + Assertions.assertEquals(alphaRef, MathUtils.normalizeAngle(pole.getAlpha(), alphaRef), 1.8e-15); + Assertions.assertEquals(deltaRef, pole.getDelta(), 2.4e-13); + Assertions.assertEquals(wRef, MathUtils.normalizeAngle(w, wRef), 2.5e-12); + + // check matrix + Vector3D qNode = Vector3D.crossProduct(Vector3D.PLUS_K, pole); + if (qNode.getNormSq() < Precision.SAFE_MIN) { + qNode = Vector3D.PLUS_I; + } + final Rotation rotation = new Rotation(Vector3D.PLUS_K, wRef, RotationConvention.FRAME_TRANSFORM). + applyTo(new Rotation(pole, qNode, Vector3D.PLUS_K, Vector3D.PLUS_I)); + Assertions.assertEquals(0.0, Rotation.distance(rRef, rotation), 1.9e-15); + } } } } diff --git a/src/test/java/org/orekit/bodies/SolarBodyTest.java b/src/test/java/org/orekit/bodies/SolarBodyTest.java index fa8617dadc..86357a02c4 100644 --- a/src/test/java/org/orekit/bodies/SolarBodyTest.java +++ b/src/test/java/org/orekit/bodies/SolarBodyTest.java @@ -70,37 +70,38 @@ public void testNaif() throws UnsupportedEncodingException, IOException { final Frame refFrame = FramesFactory.getICRF(); final TimeScale tdb = TimeScalesFactory.getTDB(); final InputStream inEntry = getClass().getResourceAsStream("/naif/DE431-ephemeris-NAIF.txt"); - BufferedReader reader = new BufferedReader(new InputStreamReader(inEntry, StandardCharsets.UTF_8)); - for (String line = reader.readLine(); line != null; line = reader.readLine()) { - line = line.trim(); - if (!line.isEmpty() && !line.startsWith("#")) { - - // extract reference data from Naif - String[] fields = line.split("\\s+"); - final AbsoluteDate date1 = new AbsoluteDate(fields[0], tdb); - final AbsoluteDate date2 = new AbsoluteDate(AbsoluteDate.J2000_EPOCH, - Double.parseDouble(fields[1]), - tdb); - String name = fields[2]; - final String barycenter = fields[3]; - final Vector3D pRef = new Vector3D(Double.parseDouble(fields[4]) * 1000.0, - Double.parseDouble(fields[5]) * 1000.0, - Double.parseDouble(fields[6]) * 1000.0); - final Vector3D vRef = new Vector3D(Double.parseDouble(fields[7]) * 1000.0, - Double.parseDouble(fields[8]) * 1000.0, - Double.parseDouble(fields[9]) * 1000.0); - - // check position-velocity - Assertions.assertEquals("BARYCENTER", barycenter); - if (name.equals("EARTH")) { - name = "EARTH-MOON BARYCENTER"; + try (BufferedReader reader = new BufferedReader(new InputStreamReader(inEntry, StandardCharsets.UTF_8))) { + for (String line = reader.readLine(); line != null; line = reader.readLine()) { + line = line.trim(); + if (!line.isEmpty() && !line.startsWith("#")) { + + // extract reference data from Naif + String[] fields = line.split("\\s+"); + final AbsoluteDate date1 = new AbsoluteDate(fields[0], tdb); + final AbsoluteDate date2 = new AbsoluteDate(AbsoluteDate.J2000_EPOCH, + Double.parseDouble(fields[1]), + tdb); + String name = fields[2]; + final String barycenter = fields[3]; + final Vector3D pRef = new Vector3D(Double.parseDouble(fields[4]) * 1000.0, + Double.parseDouble(fields[5]) * 1000.0, + Double.parseDouble(fields[6]) * 1000.0); + final Vector3D vRef = new Vector3D(Double.parseDouble(fields[7]) * 1000.0, + Double.parseDouble(fields[8]) * 1000.0, + Double.parseDouble(fields[9]) * 1000.0); + + // check position-velocity + Assertions.assertEquals("BARYCENTER", barycenter); + if (name.equals("EARTH")) { + name = "EARTH-MOON BARYCENTER"; + } + Assertions.assertEquals(0.0, date2.durationFrom(date1), 8.0e-5); + final PVCoordinates pv = CelestialBodyFactory.getBody(name).getPVCoordinates(date2, + refFrame); + + Assertions.assertEquals(0.0, Vector3D.distance(pRef, pv.getPosition()), 15.0); + Assertions.assertEquals(0.0, Vector3D.distance(vRef, pv.getVelocity()), 1.0e-5); } - Assertions.assertEquals(0.0, date2.durationFrom(date1), 8.0e-5); - final PVCoordinates pv = CelestialBodyFactory.getBody(name).getPVCoordinates(date2, - refFrame); - - Assertions.assertEquals(0.0, Vector3D.distance(pRef, pv.getPosition()), 15.0); - Assertions.assertEquals(0.0, Vector3D.distance(vRef, pv.getVelocity()), 1.0e-5); } } } diff --git a/src/test/java/org/orekit/data/AuthenticatorDialog.java b/src/test/java/org/orekit/data/AuthenticatorDialog.java index 03ccc06583..c1f0de4888 100644 --- a/src/test/java/org/orekit/data/AuthenticatorDialog.java +++ b/src/test/java/org/orekit/data/AuthenticatorDialog.java @@ -16,9 +16,14 @@ */ package org.orekit.data; -import javax.swing.*; -import java.awt.*; -import java.awt.event.ActionEvent; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JPasswordField; +import javax.swing.JTextField; +import javax.swing.Spring; +import javax.swing.SpringLayout; +import java.awt.Component; import java.awt.event.ActionListener; import java.net.Authenticator; import java.net.PasswordAuthentication; @@ -44,7 +49,7 @@ public class AuthenticatorDialog extends Authenticator { /** Simple constructor. */ public AuthenticatorDialog() { - userName = new String(); + userName = ""; password = new char[0]; } @@ -123,19 +128,17 @@ protected PasswordAuthentication getPasswordAuthentication() { SpringLayout.EAST, passwordField); dialog.pack(); - ActionListener al = new ActionListener() { - public void actionPerformed(ActionEvent e) { - if (e.getSource() == cancelButton) { - userName = new String(); - password = new char[0]; - } else { - userName = userNameField.getText(); - password = passwordField.getPassword(); - } - userNameField.setText(null); - passwordField.setText(null); - dialog.setVisible(false); + ActionListener al = e -> { + if (e.getSource() == cancelButton) { + userName = ""; + password = new char[0]; + } else { + userName = userNameField.getText(); + password = passwordField.getPassword(); } + userNameField.setText(null); + passwordField.setText(null); + dialog.setVisible(false); }; passwordField.addActionListener(al); okButton.addActionListener(al); @@ -147,7 +150,7 @@ public void actionPerformed(ActionEvent e) { // to prevent credentials lying around in memory PasswordAuthentication authentication = new PasswordAuthentication(userName, password); - userName = new String(); + userName = ""; password = new char[0]; return authentication; diff --git a/src/test/java/org/orekit/errors/OrekitMessagesTest.java b/src/test/java/org/orekit/errors/OrekitMessagesTest.java index f831cd5e2a..ffa5eac8b5 100644 --- a/src/test/java/org/orekit/errors/OrekitMessagesTest.java +++ b/src/test/java/org/orekit/errors/OrekitMessagesTest.java @@ -16,6 +16,8 @@ */ package org.orekit.errors; + +import org.hipparchus.exception.UTF8Control; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -26,18 +28,19 @@ public class OrekitMessagesTest { - private final String[] LANGUAGES_LIST = { "da", "de", "el", "en", "es", "fr", "gl", "it", "no", "ro" }; + private final String[] LANGUAGES_LIST = { "ca", "da", "de", "el", "en", "es", "fr", "gl", "it", "no", "ro" }; @Test public void testMessageNumber() { - Assertions.assertEquals(289, OrekitMessages.values().length); + Assertions.assertEquals(296, OrekitMessages.values().length); } @Test public void testAllKeysPresentInPropertiesFiles() { for (final String language : LANGUAGES_LIST) { ResourceBundle bundle = ResourceBundle.getBundle("assets/org/orekit/localization/OrekitMessages", - Locale.forLanguageTag(language), new OrekitMessages.UTF8Control()); + Locale.forLanguageTag(language), + new UTF8Control()); for (OrekitMessages message : OrekitMessages.values()) { final String messageKey = message.toString(); boolean keyPresent = false; @@ -55,7 +58,8 @@ public void testAllKeysPresentInPropertiesFiles() { public void testAllPropertiesCorrespondToKeys() { for (final String language : LANGUAGES_LIST) { ResourceBundle bundle = ResourceBundle.getBundle("assets/org/orekit/localization/OrekitMessages", - Locale.forLanguageTag(language), new OrekitMessages.UTF8Control()); + Locale.forLanguageTag(language), + new UTF8Control()); for (final Enumeration keys = bundle.getKeys(); keys.hasMoreElements();) { final String propertyKey = keys.nextElement(); try { @@ -85,7 +89,7 @@ public void testNoOpEnglishTranslation() { String translated = message.getLocalizedString(Locale.ENGLISH); // Check that the original message is not empty. - Assertions.assertFalse(message.getSourceString().length() == 0,message.name()); + Assertions.assertFalse(message.getSourceString().isEmpty(), message.name()); // Check that both texts are the same Assertions.assertEquals(message.getSourceString(), translated,message.name()); diff --git a/src/test/java/org/orekit/estimation/BrouwerLyddaneContext.java b/src/test/java/org/orekit/estimation/BrouwerLyddaneContext.java index 628e1f16bc..17fc70f59f 100644 --- a/src/test/java/org/orekit/estimation/BrouwerLyddaneContext.java +++ b/src/test/java/org/orekit/estimation/BrouwerLyddaneContext.java @@ -1,3 +1,20 @@ +/* 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.estimation; import org.hipparchus.geometry.euclidean.threed.Vector3D; @@ -9,6 +26,7 @@ import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; import org.orekit.frames.TopocentricFrame; import org.orekit.models.earth.displacement.StationDisplacement; +import org.orekit.models.earth.troposphere.TroposphericModelUtils; import org.orekit.orbits.CartesianOrbit; import org.orekit.orbits.Orbit; import org.orekit.orbits.PositionAngleType; @@ -72,6 +90,7 @@ GroundStation createStation(double latitudeInDegrees, double longitudeInDegrees, FastMath.toRadians(longitudeInDegrees), altitude); return new GroundStation(new TopocentricFrame(earth, gp, name), + TroposphericModelUtils.STANDARD_ATMOSPHERE_PROVIDER, ut1.getEOPHistory(), displacements); } diff --git a/src/test/java/org/orekit/estimation/BrouwerLyddaneEstimationTestUtils.java b/src/test/java/org/orekit/estimation/BrouwerLyddaneEstimationTestUtils.java index f8ff2acb1e..47fad19727 100644 --- a/src/test/java/org/orekit/estimation/BrouwerLyddaneEstimationTestUtils.java +++ b/src/test/java/org/orekit/estimation/BrouwerLyddaneEstimationTestUtils.java @@ -129,7 +129,7 @@ public static Propagator createPropagator(final Orbit initialOrbit, propagatorBuilder.getOrbitalParametersDrivers().getDrivers().get(i).setValue(orbitArray[i], initialOrbit.getDate()); } - return propagatorBuilder.buildPropagator(propagatorBuilder.getSelectedNormalizedParameters()); + return propagatorBuilder.buildPropagator(); } diff --git a/src/test/java/org/orekit/estimation/Context.java b/src/test/java/org/orekit/estimation/Context.java index fc410086e0..5f1696bedc 100644 --- a/src/test/java/org/orekit/estimation/Context.java +++ b/src/test/java/org/orekit/estimation/Context.java @@ -28,6 +28,7 @@ import org.orekit.forces.radiation.RadiationSensitive; import org.orekit.frames.TopocentricFrame; import org.orekit.models.earth.displacement.StationDisplacement; +import org.orekit.models.earth.troposphere.TroposphericModelUtils; import org.orekit.orbits.CartesianOrbit; import org.orekit.orbits.Orbit; import org.orekit.orbits.OrbitType; @@ -110,6 +111,7 @@ GroundStation createStation(double latitudeInDegrees, double longitudeInDegrees, FastMath.toRadians(longitudeInDegrees), altitude); return new GroundStation(new TopocentricFrame(earth, gp, name), + TroposphericModelUtils.STANDARD_ATMOSPHERE_PROVIDER, ut1.getEOPHistory(), displacements); } diff --git a/src/test/java/org/orekit/estimation/DSSTContext.java b/src/test/java/org/orekit/estimation/DSSTContext.java index 2fd1d65e86..7b9500ab64 100644 --- a/src/test/java/org/orekit/estimation/DSSTContext.java +++ b/src/test/java/org/orekit/estimation/DSSTContext.java @@ -27,6 +27,7 @@ import org.orekit.forces.radiation.RadiationSensitive; import org.orekit.frames.TopocentricFrame; import org.orekit.models.earth.displacement.StationDisplacement; +import org.orekit.models.earth.troposphere.TroposphericModelUtils; import org.orekit.orbits.EquinoctialOrbit; import org.orekit.propagation.PropagationType; import org.orekit.propagation.conversion.DSSTPropagatorBuilder; @@ -107,6 +108,7 @@ GroundStation createStation(double latitudeInDegrees, double longitudeInDegrees, FastMath.toRadians(longitudeInDegrees), altitude); return new GroundStation(new TopocentricFrame(earth, gp, name), + TroposphericModelUtils.STANDARD_ATMOSPHERE_PROVIDER, ut1.getEOPHistory(), displacements); } diff --git a/src/test/java/org/orekit/estimation/DSSTEstimationTestUtils.java b/src/test/java/org/orekit/estimation/DSSTEstimationTestUtils.java index 7ee2b3c7ea..89cb91bbb3 100644 --- a/src/test/java/org/orekit/estimation/DSSTEstimationTestUtils.java +++ b/src/test/java/org/orekit/estimation/DSSTEstimationTestUtils.java @@ -233,7 +233,7 @@ public static Propagator createPropagator(final Orbit initialOrbit, propagatorBuilder.getOrbitalParametersDrivers().getDrivers().get(i).setValue(orbitArray[i], initialOrbit.getDate()); } - return propagatorBuilder.buildPropagator(propagatorBuilder.getSelectedNormalizedParameters()); + return propagatorBuilder.buildPropagator(); } diff --git a/src/test/java/org/orekit/estimation/EcksteinHechlerContext.java b/src/test/java/org/orekit/estimation/EcksteinHechlerContext.java index 5b38830efd..b1e35a364f 100644 --- a/src/test/java/org/orekit/estimation/EcksteinHechlerContext.java +++ b/src/test/java/org/orekit/estimation/EcksteinHechlerContext.java @@ -25,6 +25,7 @@ import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; import org.orekit.frames.TopocentricFrame; import org.orekit.models.earth.displacement.StationDisplacement; +import org.orekit.models.earth.troposphere.TroposphericModelUtils; import org.orekit.orbits.CartesianOrbit; import org.orekit.orbits.Orbit; import org.orekit.orbits.PositionAngleType; @@ -87,6 +88,7 @@ GroundStation createStation(double latitudeInDegrees, double longitudeInDegrees, FastMath.toRadians(longitudeInDegrees), altitude); return new GroundStation(new TopocentricFrame(earth, gp, name), + TroposphericModelUtils.STANDARD_ATMOSPHERE_PROVIDER, ut1.getEOPHistory(), displacements); } diff --git a/src/test/java/org/orekit/estimation/EcksteinHechlerEstimationTestUtils.java b/src/test/java/org/orekit/estimation/EcksteinHechlerEstimationTestUtils.java index c6aaebba6d..2cb888e3d7 100644 --- a/src/test/java/org/orekit/estimation/EcksteinHechlerEstimationTestUtils.java +++ b/src/test/java/org/orekit/estimation/EcksteinHechlerEstimationTestUtils.java @@ -129,7 +129,7 @@ public static Propagator createPropagator(final Orbit initialOrbit, propagatorBuilder.getOrbitalParametersDrivers().getDrivers().get(i).setValue(orbitArray[i], initialOrbit.getDate()); } - return propagatorBuilder.buildPropagator(propagatorBuilder.getSelectedNormalizedParameters()); + return propagatorBuilder.buildPropagator(); } diff --git a/src/test/java/org/orekit/estimation/EphemerisContext.java b/src/test/java/org/orekit/estimation/EphemerisContext.java index bbb2be379e..c634438076 100644 --- a/src/test/java/org/orekit/estimation/EphemerisContext.java +++ b/src/test/java/org/orekit/estimation/EphemerisContext.java @@ -31,6 +31,7 @@ import org.orekit.frames.TopocentricFrame; import org.orekit.models.earth.displacement.StationDisplacement; import org.orekit.models.earth.displacement.TidalDisplacement; +import org.orekit.models.earth.troposphere.TroposphericModelUtils; import org.orekit.orbits.Orbit; import org.orekit.time.TimeScale; import org.orekit.time.TimeScalesFactory; @@ -76,6 +77,7 @@ GroundStation createStation(double latitudeInDegrees, double longitudeInDegrees, FastMath.toRadians(longitudeInDegrees), altitude); return new GroundStation(new TopocentricFrame(earth, gp, name), + TroposphericModelUtils.STANDARD_ATMOSPHERE_PROVIDER, ut1.getEOPHistory(), displacements); } diff --git a/src/test/java/org/orekit/estimation/EstimationTestUtils.java b/src/test/java/org/orekit/estimation/EstimationTestUtils.java index 36644b8577..96abe14ae6 100644 --- a/src/test/java/org/orekit/estimation/EstimationTestUtils.java +++ b/src/test/java/org/orekit/estimation/EstimationTestUtils.java @@ -16,6 +16,12 @@ */ package org.orekit.estimation; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import org.hipparchus.CalculusFieldElement; import org.hipparchus.geometry.euclidean.threed.FieldRotation; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; @@ -49,6 +55,7 @@ import org.orekit.frames.FieldTransform; import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; +import org.orekit.frames.TopocentricFrame; import org.orekit.frames.Transform; import org.orekit.frames.TransformProvider; import org.orekit.models.earth.displacement.StationDisplacement; @@ -58,6 +65,9 @@ import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.Propagator; import org.orekit.propagation.conversion.PropagatorBuilder; +import org.orekit.propagation.events.AbstractDetector; +import org.orekit.propagation.events.ElevationDetector; +import org.orekit.propagation.events.intervals.ElevationDetectionAdaptableIntervalFactory; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; import org.orekit.time.TimeScalesFactory; @@ -66,12 +76,6 @@ import org.orekit.utils.PVCoordinates; import org.orekit.utils.ParameterDriver; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - /** Utility class for orbit determination tests. */ public class EstimationTestUtils { @@ -275,7 +279,7 @@ public static Propagator createPropagator(final Orbit initialOrbit, propagatorBuilder.getOrbitalParametersDrivers().getDrivers().get(i).setValue(orbitArray[i]); } - return propagatorBuilder.buildPropagator(propagatorBuilder.getSelectedNormalizedParameters()); + return propagatorBuilder.buildPropagator(); } @@ -478,5 +482,21 @@ public static void checkKalmanFit(final Context context, final KalmanEstimator k } } + /** Get an elevation detector. + * @param topo ground station + * @param minElevation detection elevation + * @return elevation detector + */ + public static ElevationDetector getElevationDetector(final TopocentricFrame topo, final double minElevation) { + ElevationDetector detector = + new ElevationDetector(topo). + withThreshold(AbstractDetector.DEFAULT_THRESHOLD). + withMaxCheck(ElevationDetectionAdaptableIntervalFactory.getAdaptableInterval(topo, + ElevationDetectionAdaptableIntervalFactory.DEFAULT_ELEVATION_SWITCH, + 10.0)). + withConstantElevation(minElevation); + return detector; + } + } diff --git a/src/test/java/org/orekit/estimation/KeplerianContext.java b/src/test/java/org/orekit/estimation/KeplerianContext.java index 7a3d88049f..466a4a663e 100644 --- a/src/test/java/org/orekit/estimation/KeplerianContext.java +++ b/src/test/java/org/orekit/estimation/KeplerianContext.java @@ -1,3 +1,20 @@ +/* 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.estimation; import org.hipparchus.geometry.euclidean.threed.Vector3D; @@ -8,6 +25,7 @@ import org.orekit.estimation.measurements.GroundStation; import org.orekit.frames.TopocentricFrame; import org.orekit.models.earth.displacement.StationDisplacement; +import org.orekit.models.earth.troposphere.TroposphericModelUtils; import org.orekit.orbits.CartesianOrbit; import org.orekit.orbits.Orbit; import org.orekit.orbits.PositionAngleType; @@ -64,6 +82,7 @@ GroundStation createStation(double latitudeInDegrees, double longitudeInDegrees, FastMath.toRadians(longitudeInDegrees), altitude); return new GroundStation(new TopocentricFrame(earth, gp, name), + TroposphericModelUtils.STANDARD_ATMOSPHERE_PROVIDER, ut1.getEOPHistory(), displacements); } diff --git a/src/test/java/org/orekit/estimation/KeplerianEstimationTestUtils.java b/src/test/java/org/orekit/estimation/KeplerianEstimationTestUtils.java index 1250e4ae88..ae45d20fb8 100644 --- a/src/test/java/org/orekit/estimation/KeplerianEstimationTestUtils.java +++ b/src/test/java/org/orekit/estimation/KeplerianEstimationTestUtils.java @@ -116,7 +116,7 @@ public static Propagator createPropagator(final Orbit initialOrbit, propagatorBuilder.getOrbitalParametersDrivers().getDrivers().get(i).setValue(orbitArray[i], initialOrbit.getDate()); } - return propagatorBuilder.buildPropagator(propagatorBuilder.getSelectedNormalizedParameters()); + return propagatorBuilder.buildPropagator(); } diff --git a/src/test/java/org/orekit/estimation/StationDataProvider.java b/src/test/java/org/orekit/estimation/StationDataProvider.java index 39edb1af94..c741051ce7 100644 --- a/src/test/java/org/orekit/estimation/StationDataProvider.java +++ b/src/test/java/org/orekit/estimation/StationDataProvider.java @@ -1,3 +1,20 @@ +/* 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.estimation; import org.orekit.estimation.measurements.GroundStation; diff --git a/src/test/java/org/orekit/estimation/TLEContext.java b/src/test/java/org/orekit/estimation/TLEContext.java index 730800c668..6bde365772 100644 --- a/src/test/java/org/orekit/estimation/TLEContext.java +++ b/src/test/java/org/orekit/estimation/TLEContext.java @@ -24,6 +24,7 @@ import org.orekit.forces.gravity.potential.NormalizedSphericalHarmonicsProvider; import org.orekit.frames.TopocentricFrame; import org.orekit.models.earth.displacement.StationDisplacement; +import org.orekit.models.earth.troposphere.TroposphericModelUtils; import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.analytical.tle.TLE; import org.orekit.propagation.analytical.tle.generation.FixedPointTleGenerationAlgorithm; @@ -67,6 +68,7 @@ GroundStation createStation(double latitudeInDegrees, double longitudeInDegrees, FastMath.toRadians(longitudeInDegrees), altitude); return new GroundStation(new TopocentricFrame(earth, gp, name), + TroposphericModelUtils.STANDARD_ATMOSPHERE_PROVIDER, ut1.getEOPHistory(), displacements); } diff --git a/src/test/java/org/orekit/estimation/TLEEstimationTestUtils.java b/src/test/java/org/orekit/estimation/TLEEstimationTestUtils.java index 059c5f39fa..6a17f93480 100644 --- a/src/test/java/org/orekit/estimation/TLEEstimationTestUtils.java +++ b/src/test/java/org/orekit/estimation/TLEEstimationTestUtils.java @@ -191,7 +191,7 @@ public static Propagator createPropagator(final Orbit initialOrbit, propagatorBuilder.getOrbitalParametersDrivers().getDrivers().get(i).setValue(orbitArray[i], initialOrbit.getDate()); } - return propagatorBuilder.buildPropagator(propagatorBuilder.getSelectedNormalizedParameters()); + return propagatorBuilder.buildPropagator(); } diff --git a/src/test/java/org/orekit/estimation/UnscentedEstimationTestUtils.java b/src/test/java/org/orekit/estimation/UnscentedEstimationTestUtils.java index 0017401bf5..f940bdbc20 100644 --- a/src/test/java/org/orekit/estimation/UnscentedEstimationTestUtils.java +++ b/src/test/java/org/orekit/estimation/UnscentedEstimationTestUtils.java @@ -1,3 +1,20 @@ +/* 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.estimation; import java.util.Arrays; @@ -239,7 +256,7 @@ public static Propagator createPropagator(final Orbit initialOrbit, final Propag propagatorBuilder.getOrbitalParametersDrivers().getDrivers().get(i).setValue(orbitArray[i]); } - return propagatorBuilder.buildPropagator(propagatorBuilder.getSelectedNormalizedParameters()); + return propagatorBuilder.buildPropagator(); } diff --git a/src/test/java/org/orekit/estimation/common/AbstractOrbitDetermination.java b/src/test/java/org/orekit/estimation/common/AbstractOrbitDetermination.java index a2b5efdda3..c9c91497aa 100644 --- a/src/test/java/org/orekit/estimation/common/AbstractOrbitDetermination.java +++ b/src/test/java/org/orekit/estimation/common/AbstractOrbitDetermination.java @@ -73,8 +73,8 @@ import org.orekit.estimation.measurements.modifiers.AngularRadioRefractionModifier; import org.orekit.estimation.measurements.modifiers.Bias; import org.orekit.estimation.measurements.modifiers.DynamicOutlierFilter; -import org.orekit.estimation.measurements.modifiers.PhaseCentersRangeModifier; import org.orekit.estimation.measurements.modifiers.OutlierFilter; +import org.orekit.estimation.measurements.modifiers.PhaseCentersRangeModifier; import org.orekit.estimation.measurements.modifiers.RangeIonosphericDelayModifier; import org.orekit.estimation.measurements.modifiers.RangeRateIonosphericDelayModifier; import org.orekit.estimation.measurements.modifiers.RangeTroposphericDelayModifier; @@ -97,16 +97,17 @@ import org.orekit.files.ilrs.CRD.RangeMeasurement; import org.orekit.files.ilrs.CRDHeader; import org.orekit.files.ilrs.CRDHeader.RangeType; +import org.orekit.files.ilrs.CRDParser; import org.orekit.files.rinex.HatanakaCompressFilter; import org.orekit.files.rinex.observation.ObservationData; import org.orekit.files.rinex.observation.ObservationDataSet; import org.orekit.files.rinex.observation.RinexObservation; import org.orekit.files.rinex.observation.RinexObservationParser; -import org.orekit.files.ilrs.CRDParser; import org.orekit.files.sinex.SinexLoader; import org.orekit.files.sinex.Station; import org.orekit.forces.drag.DragSensitive; import org.orekit.forces.drag.IsotropicDrag; +import org.orekit.forces.gravity.potential.GravityFieldFactory; import org.orekit.forces.radiation.IsotropicRadiationSingleCoefficient; import org.orekit.forces.radiation.RadiationSensitive; import org.orekit.frames.EOPHistory; @@ -118,6 +119,8 @@ import org.orekit.gnss.antenna.FrequencyPattern; import org.orekit.models.AtmosphericRefractionModel; import org.orekit.models.earth.EarthITU453AtmosphereRefraction; +import org.orekit.models.earth.Geoid; +import org.orekit.models.earth.ReferenceEllipsoid; import org.orekit.models.earth.atmosphere.Atmosphere; import org.orekit.models.earth.atmosphere.DTM2000; import org.orekit.models.earth.atmosphere.data.MarshallSolarActivityFutureEstimation; @@ -131,15 +134,21 @@ import org.orekit.models.earth.ionosphere.KlobucharIonoCoefficientsLoader; import org.orekit.models.earth.ionosphere.KlobucharIonoModel; import org.orekit.models.earth.ionosphere.SingleLayerModelMappingFunction; -import org.orekit.models.earth.troposphere.DiscreteTroposphericModel; -import org.orekit.models.earth.troposphere.EstimatedTroposphericModel; +import org.orekit.models.earth.troposphere.EstimatedModel; import org.orekit.models.earth.troposphere.GlobalMappingFunctionModel; -import org.orekit.models.earth.troposphere.MappingFunction; import org.orekit.models.earth.troposphere.MendesPavlisModel; -import org.orekit.models.earth.troposphere.NiellMappingFunctionModel; import org.orekit.models.earth.troposphere.ModifiedSaastamoinenModel; -import org.orekit.models.earth.troposphere.TimeSpanEstimatedTroposphericModel; -import org.orekit.models.earth.weather.GlobalPressureTemperatureModel; +import org.orekit.models.earth.troposphere.NiellMappingFunctionModel; +import org.orekit.models.earth.troposphere.TimeSpanEstimatedModel; +import org.orekit.models.earth.troposphere.TroposphereMappingFunction; +import org.orekit.models.earth.troposphere.TroposphericModel; +import org.orekit.models.earth.troposphere.TroposphericModelUtils; +import org.orekit.models.earth.weather.ConstantPressureTemperatureHumidityProvider; +import org.orekit.models.earth.weather.GlobalPressureTemperature; +import org.orekit.models.earth.weather.PressureTemperature; +import org.orekit.models.earth.weather.PressureTemperatureHumidity; +import org.orekit.models.earth.weather.PressureTemperatureHumidityProvider; +import org.orekit.models.earth.weather.water.CIPM2007; import org.orekit.orbits.CartesianOrbit; import org.orekit.orbits.CircularOrbit; import org.orekit.orbits.EquinoctialOrbit; @@ -166,6 +175,7 @@ import org.orekit.utils.ParameterDriversList; import org.orekit.utils.ParameterDriversList.DelegatingDriver; import org.orekit.utils.TimeSpanMap.Span; +import org.orekit.utils.units.Unit; /** Base class for Orekit orbit determination tutorials. * @param type of the propagator builder @@ -960,9 +970,7 @@ protected Orbit runReference(final File input, final OrbitType orbitType, } // Build the reference propagator - final Propagator propagator = - propagatorBuilder.buildPropagator(propagatorBuilder. - getSelectedNormalizedParameters()); + final Propagator propagator = propagatorBuilder.buildPropagator(); // Propagate until last date and return the orbit return propagator.propagate(finalDate).getOrbit(); @@ -1390,7 +1398,7 @@ private Map createStationsData(final KeyValueFileParser stations = new HashMap(); - final boolean useTimeSpanTroposphericModel = parser.getBoolean(ParameterKey.USE_TIME_SPAN_TROPOSPHERIC_MODEL); + final boolean useTimeSpanModel = parser.getBoolean(ParameterKey.USE_TIME_SPAN_TROPOSPHERIC_MODEL); final String[] stationNames = parser.getStringArray(ParameterKey.GROUND_STATION_NAME); final double[] stationLatitudes = parser.getAngleArray(ParameterKey.GROUND_STATION_LATITUDE); final double[] stationLongitudes = parser.getAngleArray(ParameterKey.GROUND_STATION_LONGITUDE); @@ -1420,7 +1428,7 @@ private Map createStationsData(final KeyValueFileParser createStationsData(final KeyValueFileParser createStationsData(final KeyValueFileParser createStationsData(final KeyValueFileParser createStationsData(final KeyValueFileParser createStationsData(final KeyValueFileParser createStationsData(final KeyValueFileParser createStationsData(final KeyValueFileParser createStationsData(final KeyValueFileParser> readRinex(final DataSource source, final St weights.getRangeBaseWeight(), satellite); if (stationData.getIonosphericModel() != null) { final RangeIonosphericDelayModifier ionoModifier = new RangeIonosphericDelayModifier(stationData.getIonosphericModel(), - od.getObservationType().getFrequency(system).getMHzFrequency() * 1.0e6); + od.getObservationType().getFrequency(system).getFrequency()); range.addModifier(ionoModifier); } if (satAntennaRangeModifier != null) { @@ -2174,7 +2195,7 @@ private List> readRinex(final DataSource source, final St weights.getRangeRateBaseWeight(), false, satellite); if (stationData.getIonosphericModel() != null) { final RangeRateIonosphericDelayModifier ionoModifier = new RangeRateIonosphericDelayModifier(stationData.getIonosphericModel(), - od.getObservationType().getFrequency(system).getMHzFrequency() * 1.0e6, + od.getObservationType().getFrequency(system).getFrequency(), false); rangeRate.addModifier(ionoModifier); } @@ -2310,12 +2331,21 @@ private List> readCrd(final DataSource source, } // Tropospheric model - final DiscreteTroposphericModel model; + final TroposphericModel model; if (meteoData != null) { - model = new MendesPavlisModel(meteoData.getTemperature(), meteoData.getPressure() * 1000.0, - 0.01 * meteoData.getHumidity(), wavelength * 1.0e6); + final PressureTemperatureHumidity pth = + new PressureTemperatureHumidity(stationData.getStation().getBaseFrame().getPoint().getAltitude(), + Unit.BAR.toSI(meteoData.getPressure()), + meteoData.getTemperature(), + new CIPM2007(). + waterVaporPressure(Unit.BAR.toSI(meteoData.getPressure()), + meteoData.getTemperature(), + 0.01 * meteoData.getHumidity()), + Double.NaN, Double.NaN); + model = new MendesPavlisModel(new ConstantPressureTemperatureHumidityProvider(pth), + wavelength, Unit.METRE); } else { - model = MendesPavlisModel.getStandardModel(wavelength * 1.0e6); + model = MendesPavlisModel.getStandardModel(wavelength, Unit.METRE); } measurement.addModifier(new RangeTroposphericDelayModifier(model)); diff --git a/src/test/java/org/orekit/estimation/common/Iono.java b/src/test/java/org/orekit/estimation/common/Iono.java index 7b3318e6ad..2d740d9458 100644 --- a/src/test/java/org/orekit/estimation/common/Iono.java +++ b/src/test/java/org/orekit/estimation/common/Iono.java @@ -96,7 +96,7 @@ private void ensureFrequencyAndDateSupported(final Frequency frequency, final Da final IonosphericModel model = new KlobucharIonoModel(loader.getAlpha(), loader.getBeta()); // scale for current frequency - final double f = frequency.getMHzFrequency() * 1.0e6; + final double f = frequency.getFrequency(); // create modifiers rangeModifiers.get(frequency).put(dc, new RangeIonosphericDelayModifier(model, f)); diff --git a/src/test/java/org/orekit/estimation/iod/AbstractIodTest.java b/src/test/java/org/orekit/estimation/iod/AbstractIodTest.java index 94c89df4a3..bc5aa1d8ca 100644 --- a/src/test/java/org/orekit/estimation/iod/AbstractIodTest.java +++ b/src/test/java/org/orekit/estimation/iod/AbstractIodTest.java @@ -31,6 +31,7 @@ import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; import org.orekit.frames.TopocentricFrame; +import org.orekit.models.earth.troposphere.TroposphericModelUtils; import org.orekit.orbits.Orbit; import org.orekit.propagation.Propagator; import org.orekit.propagation.SpacecraftState; @@ -79,9 +80,11 @@ public void setUp() { // The ground station is set to Austin, Texas, U.S.A final OneAxisEllipsoid body = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, Constants.WGS84_EARTH_FLATTENING, itrf); - this.observer = new GroundStation( - new TopocentricFrame(body, new GeodeticPoint(FastMath.toRadians(40), FastMath.toRadians(-110), - 2000.0), "")); + this.observer = new GroundStation(new TopocentricFrame(body, + new GeodeticPoint(FastMath.toRadians(40), + FastMath.toRadians(-110), + 2000.0), ""), + TroposphericModelUtils.STANDARD_ATMOSPHERE_PROVIDER); this.observer.getPrimeMeridianOffsetDriver().setReferenceDate(AbsoluteDate.J2000_EPOCH); this.observer.getPolarOffsetXDriver().setReferenceDate(AbsoluteDate.J2000_EPOCH); this.observer.getPolarOffsetYDriver().setReferenceDate(AbsoluteDate.J2000_EPOCH); @@ -101,7 +104,7 @@ protected AngularAzEl getAzEl(final Propagator prop, final AbsoluteDate date) { final AngularAzEl azEl = new AngularAzEl(observer, date, new double[] { 0.0, 0.0 }, new double[] { 1.0, 1.0 }, new double[] { 1.0, 1.0 }, satellite); - EstimatedMeasurementBase estimated = azEl.estimateWithoutDerivatives(0, 0, new SpacecraftState[] {prop.propagate(date)}); + EstimatedMeasurementBase estimated = azEl.estimateWithoutDerivatives(new SpacecraftState[] {prop.propagate(date)}); return new AngularAzEl(observer, date, estimated.getEstimatedValue(), azEl.getBaseWeight(), azEl.getTheoreticalStandardDeviation(), satellite); } @@ -132,7 +135,7 @@ private Vector3D getEstimatedLineOfSight(final AngularRaDec raDec, final PVCoord final TimeStampedPVCoordinates satPV = pvProvider.getPVCoordinates(date, outputFrame); final AbsolutePVCoordinates satPVInGCRF = new AbsolutePVCoordinates(outputFrame, satPV); final SpacecraftState[] satState = new SpacecraftState[] { new SpacecraftState(satPVInGCRF) }; - final double[] angular = raDec.estimateWithoutDerivatives(0, 0, satState).getEstimatedValue(); + final double[] angular = raDec.estimateWithoutDerivatives(satState).getEstimatedValue(); // Rotate LOS from RADEC reference frame to output frame return raDec.getReferenceFrame().getStaticTransformTo(outputFrame, date) diff --git a/src/test/java/org/orekit/estimation/iod/IodLaplaceTest.java b/src/test/java/org/orekit/estimation/iod/IodLaplaceTest.java index 0a7352938f..1daf77c905 100644 --- a/src/test/java/org/orekit/estimation/iod/IodLaplaceTest.java +++ b/src/test/java/org/orekit/estimation/iod/IodLaplaceTest.java @@ -29,6 +29,7 @@ import org.orekit.estimation.measurements.GroundStation; import org.orekit.estimation.measurements.ObservableSatellite; import org.orekit.frames.TopocentricFrame; +import org.orekit.models.earth.troposphere.TroposphericModelUtils; import org.orekit.orbits.KeplerianOrbit; import org.orekit.orbits.Orbit; import org.orekit.orbits.PositionAngleType; @@ -54,7 +55,8 @@ public void observerOverride() { final OneAxisEllipsoid body = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, Constants.WGS84_EARTH_FLATTENING, itrf); this.observer = new GroundStation( - new TopocentricFrame(body, new GeodeticPoint(0.528253, -1.705768, 0.0), "Austin")); + new TopocentricFrame(body, new GeodeticPoint(0.528253, -1.705768, 0.0), "Austin"), + TroposphericModelUtils.STANDARD_ATMOSPHERE_PROVIDER); this.observer.getPrimeMeridianOffsetDriver().setReferenceDate(AbsoluteDate.J2000_EPOCH); this.observer.getPolarOffsetXDriver().setReferenceDate(AbsoluteDate.J2000_EPOCH); this.observer.getPolarOffsetYDriver().setReferenceDate(AbsoluteDate.J2000_EPOCH); diff --git a/src/test/java/org/orekit/estimation/leastsquares/BatchLSEstimatorTest.java b/src/test/java/org/orekit/estimation/leastsquares/BatchLSEstimatorTest.java index 4839fd9cf9..fc01d7067f 100644 --- a/src/test/java/org/orekit/estimation/leastsquares/BatchLSEstimatorTest.java +++ b/src/test/java/org/orekit/estimation/leastsquares/BatchLSEstimatorTest.java @@ -734,8 +734,8 @@ public void evaluationPerformed(int iterationsCount, int evaluationscount, before.getPosition()), 1.0e-3); Assertions.assertEquals(0.0010514, Vector3D.distance(closeOrbit.getPVCoordinates().getVelocity(), before.getPVCoordinates().getVelocity()), 1.0e-6); - EstimationTestUtils.checkFit(context, estimator, 4, 5, - 0.0, 4.7e-06, + EstimationTestUtils.checkFit(context, estimator, 4, 6, + 0.0, 5.3e-06, 0.0, 1.4e-05, 0.0, 8.8e-07, 0.0, 3.6e-10); @@ -751,7 +751,7 @@ public void evaluationPerformed(int iterationsCount, int evaluationscount, closeOrbit.getDate(), closeOrbit.getMu()); Assertions.assertEquals(0.0, Vector3D.distance(closeOrbit.getPosition(), - determined.getPosition()), 2.7e-6); + determined.getPosition()), 5.3e-6); Assertions.assertEquals(0.0, Vector3D.distance(closeOrbit.getPVCoordinates().getVelocity(), determined.getPVCoordinates().getVelocity()), 2.9e-9); diff --git a/src/test/java/org/orekit/estimation/leastsquares/BrouwerLyddaneBatchLSEstimatorTest.java b/src/test/java/org/orekit/estimation/leastsquares/BrouwerLyddaneBatchLSEstimatorTest.java index 2edac4037b..c818b30926 100644 --- a/src/test/java/org/orekit/estimation/leastsquares/BrouwerLyddaneBatchLSEstimatorTest.java +++ b/src/test/java/org/orekit/estimation/leastsquares/BrouwerLyddaneBatchLSEstimatorTest.java @@ -144,7 +144,7 @@ public void testKeplerRange() { BrouwerLyddaneContext context = BrouwerLyddaneEstimationTestUtils.eccentricContext("regular-data:potential:tides"); final BrouwerLyddanePropagatorBuilder propagatorBuilder = - context.createBuilder(PositionAngleType.MEAN, true, 1.0); + context.createBuilder(PositionAngleType.TRUE, true, 1.0); // create perfect range measurements final Propagator propagator = BrouwerLyddaneEstimationTestUtils.createPropagator(context.initialOrbit, @@ -165,10 +165,10 @@ public void testKeplerRange() { estimator.setMaxEvaluations(20); BrouwerLyddaneEstimationTestUtils.checkFit(context, estimator, 1, 2, - 0.0, 1.1e-4, - 0.0, 1.8e-4, - 0.0, 1.4e-5, - 0.0, 1.3e-8); + 0.0, 3.2e-2, + 0.0, 5.8e-2, + 0.0, 5e-3, + 0.0, 2.8e-6); } @@ -210,11 +210,11 @@ public void testKeplerRangeWithOnBoardAntennaOffset() { estimator.setMaxIterations(10); estimator.setMaxEvaluations(20); - BrouwerLyddaneEstimationTestUtils.checkFit(context, estimator, 1, 2, - 0.0, 1.2e-4, - 0.0, 2.6e-4, - 0.0, 1.4e-5, - 0.0, 1.3e-8); + 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); } @@ -251,15 +251,15 @@ public void testKeplerRangeRate() { for (final ObservedMeasurement rangerate : measurements) { estimator.addMeasurement(rangerate); } - estimator.setParametersConvergenceThreshold(1.0e-3); + estimator.setParametersConvergenceThreshold(1.0e-2); estimator.setMaxIterations(10); estimator.setMaxEvaluations(20); - BrouwerLyddaneEstimationTestUtils.checkFit(context, estimator, 1, 2, - 0.0, 7.9e-8, - 0.0, 1.1e-7, - 0.0, 1.5e-5, - 0.0, 1.4e-8); + 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); } @Test diff --git a/src/test/java/org/orekit/estimation/leastsquares/DSSTBatchLSEstimatorTest.java b/src/test/java/org/orekit/estimation/leastsquares/DSSTBatchLSEstimatorTest.java index 9cc6039b3a..8c7f3ca45d 100644 --- a/src/test/java/org/orekit/estimation/leastsquares/DSSTBatchLSEstimatorTest.java +++ b/src/test/java/org/orekit/estimation/leastsquares/DSSTBatchLSEstimatorTest.java @@ -216,8 +216,8 @@ public void evaluationPerformed(int iterationsCount, int evaluationscount, DSSTEstimationTestUtils.checkFit(context, estimator, 2, 3, 0.0, 3.1e-6, 0.0, 5.7e-6, - 0.0, 1.3e-6, - 0.0, 5.2e-10); + 0.0, 1.5e-6, + 0.0, 6.1e-10); // after the call to estimate, the parameters lacking a user-specified reference date // got a default one @@ -483,10 +483,10 @@ public void testKeplerRangeAndRangeRate() { // we have low correlation between the two types of measurement. We can expect a good estimate. DSSTEstimationTestUtils.checkFit(context, estimator, 1, 3, - 0.0, 4.9e-7, + 0.0, 5.1e-7, 0.0, 1.6e-6, 0.0, 4.4e-8, - 0.0, 2.0e-11); + 0.0, 2.2e-11); } @Test diff --git a/src/test/java/org/orekit/estimation/leastsquares/DSSTOrbitDeterminationTest.java b/src/test/java/org/orekit/estimation/leastsquares/DSSTOrbitDeterminationTest.java index f5c457824a..2c48206f9c 100644 --- a/src/test/java/org/orekit/estimation/leastsquares/DSSTOrbitDeterminationTest.java +++ b/src/test/java/org/orekit/estimation/leastsquares/DSSTOrbitDeterminationTest.java @@ -24,7 +24,6 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.orekit.KeyValueFileParser; -import org.orekit.OrekitMatchers; import org.orekit.Utils; import org.orekit.attitudes.AttitudeProvider; import org.orekit.bodies.CelestialBody; @@ -239,12 +238,12 @@ public void testLageos2() //test //definition of the accuracy for the test - final double distanceAccuracy = 76.46; - final double velocityAccuracy = 1.58e-1; + final double distanceAccuracy = 46.6; + final double velocityAccuracy = 1.13e-1; //test on the convergence - final int numberOfIte = 6; - final int numberOfEval = 6; + final int numberOfIte = 5; + final int numberOfEval = 5; Assertions.assertEquals(numberOfIte, odLageos2.getNumberOfIteration()); Assertions.assertEquals(numberOfEval, odLageos2.getNumberOfEvaluation()); @@ -253,22 +252,20 @@ public void testLageos2() final Vector3D estimatedPos = odLageos2.getEstimatedPV().getPosition(); final Vector3D estimatedVel = odLageos2.getEstimatedPV().getVelocity(); - // Ref position from "lageos2_cpf_160212_5441.jax" - final Vector3D refPos = new Vector3D(-2551060.861, 9748629.197, -6528045.767); - final Vector3D refVel = new Vector3D(-4595.833, 1029.893, 3382.441); - MatcherAssert.assertThat(estimatedPos, - OrekitMatchers.vectorCloseTo(refPos, distanceAccuracy)); + // 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); //test on statistic for the range residuals final long nbRange = 95; - final double[] RefStatRange = { -29.030, 59.098, 0.0, 14.968 }; + final double[] RefStatRange = { -28.374, 58.620, 0.0, 14.877 }; Assertions.assertEquals(nbRange, odLageos2.getRangeStat().getN()); MatcherAssert.assertThat(odLageos2.getRangeStat().getMin(), Matchers.greaterThan(RefStatRange[0])); - Assertions.assertEquals(RefStatRange[0], odLageos2.getRangeStat().getMin(), 2.0e-2); - Assertions.assertEquals(RefStatRange[1], odLageos2.getRangeStat().getMax(), 1.0e-2); + Assertions.assertEquals(RefStatRange[0], odLageos2.getRangeStat().getMin(), 1.0e-3); + 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); @@ -316,8 +313,8 @@ public void testGNSS() //test //definition of the accuracy for the test - final double distanceAccuracy = 6.95; - final double velocityAccuracy = 2.46e-3; + final double distanceAccuracy = 6.05; + final double velocityAccuracy = 2.48e-3; //test on the convergence final int numberOfIte = 3; @@ -336,7 +333,7 @@ public void testGNSS() //test on statistic for the range residuals final long nbRange = 4009; - final double[] RefStatRange = { -3.497, 2.594, 0.0, 0.837 }; + final double[] RefStatRange = { -3.480, 2.609, 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); diff --git a/src/test/java/org/orekit/estimation/leastsquares/KeplerianBatchLSEstimatorTest.java b/src/test/java/org/orekit/estimation/leastsquares/KeplerianBatchLSEstimatorTest.java index a6fadc124b..7818d9a3c6 100644 --- a/src/test/java/org/orekit/estimation/leastsquares/KeplerianBatchLSEstimatorTest.java +++ b/src/test/java/org/orekit/estimation/leastsquares/KeplerianBatchLSEstimatorTest.java @@ -46,13 +46,13 @@ import java.util.ArrayList; import java.util.List; -public class KeplerianBatchLSEstimatorTest { +class KeplerianBatchLSEstimatorTest { /** * Perfect PV measurements with a perfect start */ @Test - public void testPV() { + void testPV() { KeplerianContext context = KeplerianEstimationTestUtils.eccentricContext("regular-data:potential:tides"); @@ -93,7 +93,7 @@ public void testPV() { /** Test PV measurements generation and backward propagation in least-square orbit determination. */ @Test - public void testKeplerPVBackward() { + void testKeplerPVBackward() { KeplerianContext context = KeplerianEstimationTestUtils.eccentricContext("regular-data:potential:tides"); @@ -138,7 +138,7 @@ public void testKeplerPVBackward() { * Perfect range measurements with a perfect start */ @Test - public void testKeplerRange() { + void testKeplerRange() { KeplerianContext context = KeplerianEstimationTestUtils.eccentricContext("regular-data:potential:tides"); @@ -164,10 +164,10 @@ public void testKeplerRange() { estimator.setMaxEvaluations(20); KeplerianEstimationTestUtils.checkFit(context, estimator, 1, 4, - 0.0, 8.4e-7, + 0.0, 8.5e-7, 0.0, 2.0e-6, 0.0, 1.9e-8, - 0.0, 8.3e-12); + 0.0, 9.0e-12); } @@ -175,7 +175,7 @@ public void testKeplerRange() { * Perfect range measurements with a perfect start and an on-board antenna range offset */ @Test - public void testKeplerRangeWithOnBoardAntennaOffset() { + void testKeplerRangeWithOnBoardAntennaOffset() { KeplerianContext context = KeplerianEstimationTestUtils.eccentricContext("regular-data:potential:tides"); @@ -209,11 +209,11 @@ public void testKeplerRangeWithOnBoardAntennaOffset() { estimator.setMaxIterations(10); estimator.setMaxEvaluations(20); - KeplerianEstimationTestUtils.checkFit(context, estimator, 1, 13, + KeplerianEstimationTestUtils.checkFit(context, estimator, 1, 12, 0.0, 5.9e-5, 0.0, 1.5e-4, - 0.0, 2.5e-9, - 0.0, 2.8e-12); + 0.0, 4.3e-9, + 0.0, 4.2e-12); } @@ -221,7 +221,7 @@ public void testKeplerRangeWithOnBoardAntennaOffset() { * Perfect range rate measurements with a perfect start */ @Test - public void testKeplerRangeRate() { + void testKeplerRangeRate() { KeplerianContext context = KeplerianEstimationTestUtils.eccentricContext("regular-data:potential:tides"); @@ -262,7 +262,7 @@ public void testKeplerRangeRate() { } @Test - public void testWrappedException() { + void testWrappedException() { KeplerianContext context = KeplerianEstimationTestUtils.eccentricContext("regular-data:potential:tides"); diff --git a/src/test/java/org/orekit/estimation/leastsquares/NumericalOrbitDeterminationTest.java b/src/test/java/org/orekit/estimation/leastsquares/NumericalOrbitDeterminationTest.java index e793635309..6336f235b4 100644 --- a/src/test/java/org/orekit/estimation/leastsquares/NumericalOrbitDeterminationTest.java +++ b/src/test/java/org/orekit/estimation/leastsquares/NumericalOrbitDeterminationTest.java @@ -274,7 +274,7 @@ public void testGNSS() //definition of the accuracy for the test final double distanceAccuracy = 1.06; - final double velocityAccuracy = 2.26e-3; + final double velocityAccuracy = 2.27e-3; //test on the convergence final int numberOfIte = 2; @@ -294,7 +294,7 @@ public void testGNSS() //test on statistic for the range residuals final long nbRangeInit = 8981; final long nbRangeExcluded = 305; - final double[] RefStatRange = { -3.847, 8.307, 0.0, 0.873 }; + final double[] RefStatRange = { -3.853, 8.307, 0.0, 0.873 }; Assertions.assertEquals(nbRangeInit - nbRangeExcluded, odGNSS.getRangeStat().getN()); Assertions.assertEquals(RefStatRange[0], odGNSS.getRangeStat().getMin(), 1.0e-3); Assertions.assertEquals(RefStatRange[1], odGNSS.getRangeStat().getMax(), 1.0e-3); @@ -332,8 +332,8 @@ public void testW3B() //test on the estimated position and velocity final Vector3D estimatedPos = odsatW3.getEstimatedPV().getPosition(); final Vector3D estimatedVel = odsatW3.getEstimatedPV().getVelocity(); - final Vector3D refPos = new Vector3D(-40541446.255, -9905357.41, 206777.413); - final Vector3D refVel = new Vector3D(759.0685, -1476.5156, 54.793); + final Vector3D refPos = new Vector3D(-40541446.236, -9905357.943, 206777.082); + final Vector3D refVel = new Vector3D(759.0685, -1476.5156, 54.7931); Assertions.assertEquals(0.0, Vector3D.distance(refPos, estimatedPos), distanceAccuracy); Assertions.assertEquals(0.0, Vector3D.distance(refVel, estimatedVel), velocityAccuracy); @@ -346,12 +346,12 @@ public void testW3B() propagatorParameters.getDrivers().get(3).getValue(), propagatorParameters.getDrivers().get(5).getValue()); //Assertions.assertEquals(7.215e-6, leakAcceleration.getNorm(), 1.0e-8); - Assertions.assertEquals(8.002e-6, leakAcceleration0.getNorm(), 1.0e-8); + Assertions.assertEquals(8.005e-6, leakAcceleration0.getNorm(), 1.0e-8); final Vector3D leakAcceleration1 = new Vector3D(propagatorParameters.getDrivers().get(2).getValue(), propagatorParameters.getDrivers().get(4).getValue(), propagatorParameters.getDrivers().get(6).getValue()); - Assertions.assertEquals(3.058e-10, leakAcceleration1.getNorm(), 1.0e-12); + Assertions.assertEquals(3.060e-10, leakAcceleration1.getNorm(), 1.0e-12); //test on measurements parameters final List list = new ArrayList(); @@ -360,35 +360,35 @@ public void testW3B() //station CastleRock final double[] CastleAzElBias = { 0.062701342, -0.003613508 }; - final double CastleRangeBias = 11274.4677; + final double CastleRangeBias = 11274.4195; Assertions.assertEquals(CastleAzElBias[0], FastMath.toDegrees(list.get(0).getValue()), angleAccuracy); Assertions.assertEquals(CastleAzElBias[1], FastMath.toDegrees(list.get(1).getValue()), angleAccuracy); Assertions.assertEquals(CastleRangeBias, list.get(2).getValue(), distanceAccuracy); //station Fucino final double[] FucAzElBias = { -0.053526137, 0.075483886 }; - final double FucRangeBias = 13467.8256; + final double FucRangeBias = 13467.8361; Assertions.assertEquals(FucAzElBias[0], FastMath.toDegrees(list.get(3).getValue()), angleAccuracy); Assertions.assertEquals(FucAzElBias[1], FastMath.toDegrees(list.get(4).getValue()), angleAccuracy); Assertions.assertEquals(FucRangeBias, list.get(5).getValue(), distanceAccuracy); //station Kumsan final double[] KumAzElBias = { -0.023574208, -0.054520756 }; - final double KumRangeBias = 13512.57594; + final double KumRangeBias = 13512.4898; Assertions.assertEquals(KumAzElBias[0], FastMath.toDegrees(list.get(6).getValue()), angleAccuracy); Assertions.assertEquals(KumAzElBias[1], FastMath.toDegrees(list.get(7).getValue()), angleAccuracy); Assertions.assertEquals(KumRangeBias, list.get(8).getValue(), distanceAccuracy); //station Pretoria final double[] PreAzElBias = { 0.030201539, 0.009747877 }; - final double PreRangeBias = 13594.11889; + final double PreRangeBias = 13594.1758; Assertions.assertEquals(PreAzElBias[0], FastMath.toDegrees(list.get( 9).getValue()), angleAccuracy); Assertions.assertEquals(PreAzElBias[1], FastMath.toDegrees(list.get(10).getValue()), angleAccuracy); Assertions.assertEquals(PreRangeBias, list.get(11).getValue(), distanceAccuracy); //station Uralla final double[] UraAzElBias = { 0.167814449, -0.12305252 }; - final double UraRangeBias = 13450.26738; + final double UraRangeBias = 13450.2320; Assertions.assertEquals(UraAzElBias[0], FastMath.toDegrees(list.get(12).getValue()), angleAccuracy); Assertions.assertEquals(UraAzElBias[1], FastMath.toDegrees(list.get(13).getValue()), angleAccuracy); Assertions.assertEquals(UraRangeBias, list.get(14).getValue(), distanceAccuracy); diff --git a/src/test/java/org/orekit/estimation/leastsquares/TLEOrbitDeterminationTest.java b/src/test/java/org/orekit/estimation/leastsquares/TLEOrbitDeterminationTest.java index 372d559e4b..27b395df9e 100644 --- a/src/test/java/org/orekit/estimation/leastsquares/TLEOrbitDeterminationTest.java +++ b/src/test/java/org/orekit/estimation/leastsquares/TLEOrbitDeterminationTest.java @@ -222,7 +222,7 @@ public void testGNSS() //test on statistic for the range residuals final long nbRange = 8211; - final double[] RefStatRange = { -14.448, 18.736, 0.132, 6.323 }; + final double[] RefStatRange = { -14.448, 18.706, 0.132, 6.322 }; 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); diff --git a/src/test/java/org/orekit/estimation/measurements/AngularAzElTest.java b/src/test/java/org/orekit/estimation/measurements/AngularAzElTest.java index f45aeae569..c293f35a00 100644 --- a/src/test/java/org/orekit/estimation/measurements/AngularAzElTest.java +++ b/src/test/java/org/orekit/estimation/measurements/AngularAzElTest.java @@ -37,13 +37,13 @@ import org.orekit.utils.ParameterFunction; import org.orekit.utils.StateFunction; -public class AngularAzElTest { +class AngularAzElTest { /** Compare observed values and estimated values. * Both are calculated with a different algorithm */ @Test - public void testValues() { + void testValues() { Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides"); @@ -72,7 +72,7 @@ public void testValues() { SpacecraftState state = propagator.propagate(datemeas); // Estimate the AZEL value - final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }); + final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(new SpacecraftState[] { state }); // Store the difference between estimated and observed values in the stats azDiffStat.addValue(FastMath.abs(estimated.getEstimatedValue()[0] - measurement.getObservedValue()[0])); @@ -93,7 +93,7 @@ public void testValues() { * finite differences calculation as a reference */ @Test - public void testStateDerivatives() { + void testStateDerivatives() { Context context = EstimationTestUtils.geoStationnaryContext("regular-data:potential:tides"); @@ -136,7 +136,8 @@ public void testStateDerivatives() { final AbsoluteDate datemeas = measurement.getDate(); SpacecraftState state = propagator.propagate(datemeas); final Vector3D stationP = stationParameter.getOffsetToInertial(state.getFrame(), datemeas, false).transformPosition(Vector3D.ZERO); - final double meanDelay = AbstractMeasurement.signalTimeOfFlight(state.getPVCoordinates(), stationP, datemeas); + final double meanDelay = AbstractMeasurement.signalTimeOfFlight(state.getPVCoordinates(), stationP, + datemeas, state.getFrame()); final AbsoluteDate date = measurement.getDate().shiftedBy(-0.75 * meanDelay); state = propagator.propagate(date); @@ -149,7 +150,7 @@ public void testStateDerivatives() { Differentiation.differentiate(new StateFunction() { public double[] value(final SpacecraftState state) { return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). + estimateWithoutDerivatives(new SpacecraftState[] { state }). getEstimatedValue(); } }, measurement.getDimension(), propagator.getAttitudeProvider(), OrbitType.CARTESIAN, @@ -191,7 +192,7 @@ public double[] value(final SpacecraftState state) { Assertions.assertEquals(0.0, new Median().evaluate(AzerrorsV), 6.1e-5); // median errors on Elevation - Assertions.assertEquals(0.0, new Median().evaluate(ElerrorsP), 7.4e-11); + Assertions.assertEquals(0.0, new Median().evaluate(ElerrorsP), 7.5e-11); Assertions.assertEquals(0.0, new Median().evaluate(ElerrorsV), 2.3e-5); } @@ -199,7 +200,7 @@ public double[] value(final SpacecraftState state) { * finite differences calculation as a reference */ @Test - public void testParameterDerivatives() { + void testParameterDerivatives() { Context context = EstimationTestUtils.geoStationnaryContext("regular-data:potential:tides"); @@ -237,7 +238,8 @@ public void testParameterDerivatives() { final AbsoluteDate datemeas = measurement.getDate(); final SpacecraftState stateini = propagator.propagate(datemeas); final Vector3D stationP = stationParameter.getOffsetToInertial(stateini.getFrame(), datemeas, false).transformPosition(Vector3D.ZERO); - final double meanDelay = AbstractMeasurement.signalTimeOfFlight(stateini.getPVCoordinates(), stationP, datemeas); + final double meanDelay = AbstractMeasurement.signalTimeOfFlight(stateini.getPVCoordinates(), stationP, + datemeas, stateini.getFrame()); final AbsoluteDate date = measurement.getDate().shiftedBy(-0.75 * meanDelay); final SpacecraftState state = propagator.propagate(date); @@ -258,7 +260,7 @@ public void testParameterDerivatives() { @Override public double value(final ParameterDriver parameterDriver, AbsoluteDate date) { return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). + estimateWithoutDerivatives(new SpacecraftState[] { state }). getEstimatedValue()[k]; } }, 3, 50.0 * drivers[i].getScale()); diff --git a/src/test/java/org/orekit/estimation/measurements/AngularRaDecTest.java b/src/test/java/org/orekit/estimation/measurements/AngularRaDecTest.java index a952322bdc..01f8f35203 100644 --- a/src/test/java/org/orekit/estimation/measurements/AngularRaDecTest.java +++ b/src/test/java/org/orekit/estimation/measurements/AngularRaDecTest.java @@ -16,7 +16,6 @@ */ package org.orekit.estimation.measurements; -import java.util.Collections; import java.util.List; import org.hipparchus.geometry.euclidean.threed.Vector3D; @@ -31,26 +30,35 @@ import org.orekit.estimation.Context; import org.orekit.estimation.EstimationTestUtils; import org.orekit.estimation.measurements.generation.AngularRaDecBuilder; -import org.orekit.frames.*; +import org.orekit.frames.Frame; +import org.orekit.frames.FramesFactory; +import org.orekit.frames.ITRFVersion; +import org.orekit.frames.StaticTransform; +import org.orekit.frames.TopocentricFrame; +import org.orekit.models.earth.troposphere.TroposphericModelUtils; import org.orekit.orbits.CartesianOrbit; import org.orekit.orbits.OrbitType; import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.Propagator; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.conversion.NumericalPropagatorBuilder; -import org.orekit.propagation.sampling.OrekitStepInterpolator; import org.orekit.time.AbsoluteDate; import org.orekit.time.DateComponents; import org.orekit.time.TimeScalesFactory; -import org.orekit.utils.*; +import org.orekit.utils.Constants; +import org.orekit.utils.Differentiation; +import org.orekit.utils.IERSConventions; +import org.orekit.utils.PVCoordinates; +import org.orekit.utils.ParameterDriver; +import org.orekit.utils.ParameterFunction; -public class AngularRaDecTest { +class AngularRaDecTest { /** Test the values of radec measurements. * Added after bug 473 was reported by John Grimes. */ @Test - public void testBug473OnValues() { + void testBug473OnValues() { Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides"); @@ -79,8 +87,7 @@ public void testBug473OnValues() { SpacecraftState state = propagator.propagate(datemeas); // Estimate the RADEC value - final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(0, 0, - new SpacecraftState[] { state }); + final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(new SpacecraftState[] { state }); // Store the difference between estimated and observed values in the stats raDiffStat.addValue(FastMath.abs(estimated.getEstimatedValue()[0] - measurement.getObservedValue()[0])); @@ -99,7 +106,7 @@ public void testBug473OnValues() { * finite differences calculation as a reference */ @Test - public void testStateDerivatives() { + void testStateDerivatives() { Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides"); @@ -142,7 +149,8 @@ public void testStateDerivatives() { final AbsoluteDate datemeas = measurement.getDate(); SpacecraftState state = propagator.propagate(datemeas); final Vector3D stationP = stationParameter.getOffsetToInertial(state.getFrame(), datemeas, false).transformPosition(Vector3D.ZERO); - final double meanDelay = AbstractMeasurement.signalTimeOfFlight(state.getPVCoordinates(), stationP, datemeas); + final double meanDelay = AbstractMeasurement.signalTimeOfFlight(state.getPVCoordinates(), stationP, + datemeas, state.getFrame()); final AbsoluteDate date = measurement.getDate().shiftedBy(-0.75 * meanDelay); state = propagator.propagate(date); @@ -152,14 +160,12 @@ public void testStateDerivatives() { // compute a reference value using finite differences final double[][] finiteDifferencesJacobian = - Differentiation.differentiate(new StateFunction() { - public double[] value(final SpacecraftState state) { - return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). - getEstimatedValue(); - } - }, measurement.getDimension(), propagator.getAttitudeProvider(), OrbitType.CARTESIAN, - PositionAngleType.TRUE, 250.0, 4).value(state); + Differentiation.differentiate(state1 -> measurement. + estimateWithoutDerivatives(new SpacecraftState[] { + state1 + }). + getEstimatedValue(), measurement.getDimension(), propagator.getAttitudeProvider(), OrbitType.CARTESIAN, + PositionAngleType.TRUE, 250.0, 4).value(state); Assertions.assertEquals(finiteDifferencesJacobian.length, jacobian.length); Assertions.assertEquals(finiteDifferencesJacobian[0].length, jacobian[0].length); @@ -197,7 +203,7 @@ public double[] value(final SpacecraftState state) { Assertions.assertEquals(0.0, new Median().evaluate(RaerrorsV), 2.2e-5); // median errors on declination - Assertions.assertEquals(0.0, new Median().evaluate(DecerrorsP), 1.9e-11); + Assertions.assertEquals(0.0, new Median().evaluate(DecerrorsP), 2.2e-11); Assertions.assertEquals(0.0, new Median().evaluate(DecerrorsV), 9.0e-6); // Test measurement type @@ -208,7 +214,7 @@ public double[] value(final SpacecraftState state) { * finite differences calculation as a reference */ @Test - public void testParameterDerivatives() { + void testParameterDerivatives() { Context context = EstimationTestUtils.geoStationnaryContext("regular-data:potential:tides"); @@ -246,7 +252,8 @@ public void testParameterDerivatives() { final AbsoluteDate datemeas = measurement.getDate(); final SpacecraftState stateini = propagator.propagate(datemeas); final Vector3D stationP = stationParameter.getOffsetToInertial(stateini.getFrame(), datemeas, false).transformPosition(Vector3D.ZERO); - final double meanDelay = AbstractMeasurement.signalTimeOfFlight(stateini.getPVCoordinates(), stationP, datemeas); + final double meanDelay = AbstractMeasurement.signalTimeOfFlight(stateini.getPVCoordinates(), stationP, + datemeas, stateini.getFrame()); final AbsoluteDate date = measurement.getDate().shiftedBy(-0.75 * meanDelay); final SpacecraftState state = propagator.propagate(date); @@ -267,7 +274,7 @@ public void testParameterDerivatives() { @Override public double value(final ParameterDriver parameterDriver, AbsoluteDate date) { return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). + estimateWithoutDerivatives(new SpacecraftState[] { state }). getEstimatedValue()[k]; } }, 3, 50.0 * drivers[i].getScale()); @@ -285,29 +292,18 @@ public double value(final ParameterDriver parameterDriver, AbsoluteDate date) { * values. */ @Test - public void testIssue1026() { + void testIssue1026() { - //Context context = EstimationTestUtils.eccentricContext("regular-data/de431-ephemerides"); Utils.setDataRoot("regular-data"); - final double[] pos = {Constants.EGM96_EARTH_EQUATORIAL_RADIUS + 5e5, 1000., 0.}; + final double[] pos = { Constants.EGM96_EARTH_EQUATORIAL_RADIUS + 5e5, 1000., 0.}; final double[] vel = {0., 10., 0.}; final PVCoordinates pvCoordinates = new PVCoordinates(new Vector3D(pos[0], pos[1], pos[2]), - new Vector3D(vel[0], vel[1], vel[2])); + new Vector3D(vel[0], vel[1], vel[2])); final AbsoluteDate epoch = new AbsoluteDate(new DateComponents(2000, 1, 1), TimeScalesFactory.getUTC()); final Frame gcrf = FramesFactory.getGCRF(); - final CartesianOrbit orbit = new CartesianOrbit(pvCoordinates, gcrf, - epoch, Constants.EGM96_EARTH_MU); + final CartesianOrbit orbit = new CartesianOrbit(pvCoordinates, gcrf, epoch, Constants.EGM96_EARTH_MU); final SpacecraftState spacecraftState = new SpacecraftState(orbit); - final OrekitStepInterpolator fakeInterpolator = new OrekitStepInterpolator() { - public OrekitStepInterpolator restrictStep(SpacecraftState newPreviousState, SpacecraftState newCurrentState) { return null; } - public boolean isPreviousStateInterpolated() { return false; } - public boolean isForward() { return true; } - public boolean isCurrentStateInterpolated() { return false; } - public SpacecraftState getPreviousState() { return spacecraftState; } - public SpacecraftState getInterpolatedState(AbsoluteDate date) { return spacecraftState; } - public SpacecraftState getCurrentState() { return spacecraftState; } - }; final OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.IERS2010_EARTH_EQUATORIAL_RADIUS, Constants.IERS2010_EARTH_FLATTENING, @@ -315,7 +311,8 @@ public void testIssue1026() { final GeodeticPoint point = new GeodeticPoint(0., 0., 100.); final TopocentricFrame baseFrame = new TopocentricFrame(earth, point, "name"); - final GroundStation station = new GroundStation(baseFrame); + final GroundStation station = new GroundStation(baseFrame, + TroposphericModelUtils.STANDARD_ATMOSPHERE_PROVIDER); final Frame[] frames = {FramesFactory.getEME2000(), FramesFactory.getGCRF(), FramesFactory.getICRF(), FramesFactory.getTOD(false)}; final double[][] raDec = new double[frames.length][]; @@ -325,8 +322,8 @@ public void testIssue1026() { final AngularRaDecBuilder builder = new AngularRaDecBuilder(null, station, frames[i], new double[]{1., 1.}, new double[]{1., 1.}, os); builder.init(spacecraftState.getDate(), spacecraftState.getDate()); - final double[] moreRaDec = builder.build(spacecraftState.getDate(), - Collections.singletonMap(os, fakeInterpolator)).getObservedValue(); + final double[] moreRaDec = builder.build(spacecraftState.getDate(), new SpacecraftState[] { spacecraftState }) + .getObservedValue(); // convert in common frame final StaticTransform transform = frames[i].getStaticTransformTo(orbit.getFrame(), epoch); final Vector3D transformedLoS = transform.transformVector(new Vector3D(moreRaDec[0], moreRaDec[1])); diff --git a/src/test/java/org/orekit/estimation/measurements/BistaticRangeRateTest.java b/src/test/java/org/orekit/estimation/measurements/BistaticRangeRateTest.java index bcba7855b6..2e9bef435e 100644 --- a/src/test/java/org/orekit/estimation/measurements/BistaticRangeRateTest.java +++ b/src/test/java/org/orekit/estimation/measurements/BistaticRangeRateTest.java @@ -71,7 +71,7 @@ public void testValues() { SpacecraftState state = propagator.propagate(datemeas); // Estimate the measurement value - final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }); + final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(new SpacecraftState[] { state }); // Store the difference between estimated and observed values in the stats diffStat.addValue(FastMath.abs(estimated.getEstimatedValue()[0] - measurement.getObservedValue()[0])); @@ -119,7 +119,7 @@ public void testStateDerivatives() { Differentiation.differentiate(new StateFunction() { public double[] value(final SpacecraftState state) { return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). + estimateWithoutDerivatives(new SpacecraftState[] { state }). getEstimatedValue(); } }, 1, propagator.getAttitudeProvider(), @@ -184,7 +184,7 @@ public void testStateDerivativesWithModifier() { Differentiation.differentiate(new StateFunction() { public double[] value(final SpacecraftState state) { return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). + estimateWithoutDerivatives(new SpacecraftState[] { state }). getEstimatedValue(); } }, 1, propagator.getAttitudeProvider(), @@ -275,7 +275,7 @@ public void testParameterDerivatives() { @Override public double value(final ParameterDriver parameterDriver, AbsoluteDate date) { return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). + estimateWithoutDerivatives(new SpacecraftState[] { state }). getEstimatedValue()[0]; } }, 3, 20.0 * drivers[i].getScale()); @@ -364,7 +364,7 @@ public void testParameterDerivativesWithModifier() { @Override public double value(final ParameterDriver parameterDriver, AbsoluteDate date) { return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). + estimateWithoutDerivatives(new SpacecraftState[] { state }). getEstimatedValue()[0]; } }, 3, 20.0 * drivers[i].getScale()); diff --git a/src/test/java/org/orekit/estimation/measurements/BistaticRangeTest.java b/src/test/java/org/orekit/estimation/measurements/BistaticRangeTest.java index 289c72cf42..84799eaf6c 100644 --- a/src/test/java/org/orekit/estimation/measurements/BistaticRangeTest.java +++ b/src/test/java/org/orekit/estimation/measurements/BistaticRangeTest.java @@ -71,7 +71,7 @@ public void testValues() { SpacecraftState state = propagator.propagate(datemeas); // Estimate the measurement value - final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }); + final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(new SpacecraftState[] { state }); // Store the difference between estimated and observed values in the stats diffStat.addValue(FastMath.abs(estimated.getEstimatedValue()[0] - measurement.getObservedValue()[0])); @@ -120,7 +120,7 @@ public void testStateDerivatives() { Differentiation.differentiate(new StateFunction() { public double[] value(final SpacecraftState state) { return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). + estimateWithoutDerivatives(new SpacecraftState[] { state }). getEstimatedValue(); } }, 1, propagator.getAttitudeProvider(), @@ -185,7 +185,7 @@ public void testStateDerivativesWithModifier() { Differentiation.differentiate(new StateFunction() { public double[] value(final SpacecraftState state) { return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). + estimateWithoutDerivatives(new SpacecraftState[] { state }). getEstimatedValue(); } }, 1, propagator.getAttitudeProvider(), @@ -276,7 +276,7 @@ public void testParameterDerivatives() { @Override public double value(final ParameterDriver parameterDriver, final AbsoluteDate date) { return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). + estimateWithoutDerivatives(new SpacecraftState[] { state }). getEstimatedValue()[0]; } }, 3, 20.0 * drivers[i].getScale()); diff --git a/src/test/java/org/orekit/estimation/measurements/ComparableMeasurementTest.java b/src/test/java/org/orekit/estimation/measurements/ComparableMeasurementTest.java index ff2e96303f..78ee853a9e 100644 --- a/src/test/java/org/orekit/estimation/measurements/ComparableMeasurementTest.java +++ b/src/test/java/org/orekit/estimation/measurements/ComparableMeasurementTest.java @@ -25,6 +25,7 @@ import org.orekit.bodies.OneAxisEllipsoid; import org.orekit.frames.FramesFactory; import org.orekit.frames.TopocentricFrame; +import org.orekit.models.earth.troposphere.TroposphericModelUtils; import org.orekit.time.AbsoluteDate; import org.orekit.utils.Constants; import org.orekit.utils.IERSConventions; @@ -46,7 +47,8 @@ public void testDefaultCompareToIssue538() { OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, Constants.WGS84_EARTH_FLATTENING, FramesFactory.getITRF(IERSConventions.IERS_2010, false)); TopocentricFrame stationFrame = new TopocentricFrame(earth, new GeodeticPoint(FastMath.toRadians(45.0), FastMath.toRadians(0.0), 0.0), "station"); - GroundStation station = new GroundStation(stationFrame); + GroundStation station = new GroundStation(stationFrame, + TroposphericModelUtils.STANDARD_ATMOSPHERE_PROVIDER); AbsoluteDate date = AbsoluteDate.J2000_EPOCH; // Create a Range and Azel at same date, diff --git a/src/test/java/org/orekit/estimation/measurements/DSSTPVTest.java b/src/test/java/org/orekit/estimation/measurements/DSSTPVTest.java index 95b4bf5ba7..66bd676f0d 100644 --- a/src/test/java/org/orekit/estimation/measurements/DSSTPVTest.java +++ b/src/test/java/org/orekit/estimation/measurements/DSSTPVTest.java @@ -70,7 +70,7 @@ public void testStateDerivatives() { Differentiation.differentiate(new StateFunction() { public double[] value(final SpacecraftState state) { return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). + estimateWithoutDerivatives(new SpacecraftState[] { state }). getEstimatedValue(); } }, measurement.getDimension(), diff --git a/src/test/java/org/orekit/estimation/measurements/GroundStationTest.java b/src/test/java/org/orekit/estimation/measurements/GroundStationTest.java index 91beeb5377..864a7ff2f4 100644 --- a/src/test/java/org/orekit/estimation/measurements/GroundStationTest.java +++ b/src/test/java/org/orekit/estimation/measurements/GroundStationTest.java @@ -46,6 +46,7 @@ import org.orekit.frames.StaticTransform; import org.orekit.frames.TopocentricFrame; import org.orekit.frames.Transform; +import org.orekit.models.earth.troposphere.TroposphericModelUtils; import org.orekit.orbits.OrbitType; import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.Propagator; @@ -94,6 +95,7 @@ public void testEstimateClockOffset() throws IOException, ClassNotFoundException final String changedSuffix = "-changed"; final GroundStation changed = new GroundStation(new TopocentricFrame(parent, base.getPoint(), base.getName() + changedSuffix), + TroposphericModelUtils.STANDARD_ATMOSPHERE_PROVIDER, context.ut1.getEOPHistory(), context.stations.get(0).getDisplacements()); @@ -174,6 +176,7 @@ public void testEstimateStationPosition() throws IOException, ClassNotFoundExcep parent.getBodyFrame(), null), base.getName() + movedSuffix), + TroposphericModelUtils.STANDARD_ATMOSPHERE_PROVIDER, context.ut1.getEOPHistory(), context.stations.get(0).getDisplacements()); @@ -212,7 +215,7 @@ public void testEstimateStationPosition() throws IOException, ClassNotFoundExcep Assertions.assertEquals(deltaTopo.getY(), moved.getNorthOffsetDriver().getValue(), 6.2e-7); Assertions.assertEquals(deltaTopo.getZ(), moved.getZenithOffsetDriver().getValue(), 2.6e-7); - GeodeticPoint result = moved.getOffsetGeodeticPoint(null); + GeodeticPoint result = moved.getOffsetGeodeticPoint((AbsoluteDate) null); GeodeticPoint reference = context.stations.get(0).getBaseFrame().getPoint(); Assertions.assertEquals(reference.getLatitude(), result.getLatitude(), 3.3e-14); @@ -1277,7 +1280,8 @@ public void testNoReferenceDateGradient() { FramesFactory.getITRF(IERSConventions.IERS_2010, true)); final GroundStation station = new GroundStation(new TopocentricFrame(earth, new GeodeticPoint(0.1, 0.2, 100), - "dummy")); + "dummy"), + TroposphericModelUtils.STANDARD_ATMOSPHERE_PROVIDER); try { station.getOffsetToInertial(eme2000, date, false); Assertions.fail("an exception should have been thrown"); @@ -1324,7 +1328,8 @@ private void doTestCartesianDerivatives(double latitude, double longitude, doubl FramesFactory.getITRF(IERSConventions.IERS_2010, true)); final GroundStation station = new GroundStation(new TopocentricFrame(earth, new GeodeticPoint(latitude, longitude, altitude), - "dummy")); + "dummy"), + TroposphericModelUtils.STANDARD_ATMOSPHERE_PROVIDER); final GradientField gradientField = GradientField.getField(parameterPattern.length); ParameterDriver[] selectedDrivers = new ParameterDriver[parameterPattern.length]; UnivariateDifferentiableVectorFunction[] dFCartesian = new UnivariateDifferentiableVectorFunction[parameterPattern.length]; @@ -1433,7 +1438,8 @@ private void doTestAngularDerivatives(double latitude, double longitude, double FramesFactory.getITRF(IERSConventions.IERS_2010, true)); final GroundStation station = new GroundStation(new TopocentricFrame(earth, new GeodeticPoint(latitude, longitude, altitude), - "dummy")); + "dummy"), + TroposphericModelUtils.STANDARD_ATMOSPHERE_PROVIDER); ParameterDriver[] selectedDrivers = new ParameterDriver[parameterPattern.length]; UnivariateDifferentiableVectorFunction[] dFAngular = new UnivariateDifferentiableVectorFunction[parameterPattern.length]; final ParameterDriver[] allDrivers = selectAllDrivers(station); diff --git a/src/test/java/org/orekit/estimation/measurements/InterSatellitesRangeMeasurementCreator.java b/src/test/java/org/orekit/estimation/measurements/InterSatellitesRangeMeasurementCreator.java index 12431ca34c..f5fee33db7 100644 --- a/src/test/java/org/orekit/estimation/measurements/InterSatellitesRangeMeasurementCreator.java +++ b/src/test/java/org/orekit/estimation/measurements/InterSatellitesRangeMeasurementCreator.java @@ -76,7 +76,7 @@ public void init(final SpacecraftState s0, final AbsoluteDate t, final double st public void handleStep(final SpacecraftState currentState) { try { final AbsoluteDate date = currentState.getDate(); - final Vector3D position = currentState.toTransform().toStaticTransform().getInverse().transformPosition(antennaPhaseCenter1); + final Vector3D position = currentState.toStaticTransform().getInverse().transformPosition(antennaPhaseCenter1); final double remoteClk = remote.getClockOffsetDriver().getValue(date); final double localClk = local.getClockOffsetDriver().getValue(date); final double deltaD = Constants.SPEED_OF_LIGHT * (localClk - remoteClk); @@ -106,13 +106,13 @@ public double value(final double x) { // generate a two-way measurement final double upLinkDelay = solver.solve(1000, new UnivariateFunction() { public double value(final double x) { - final Vector3D self = currentState.shiftedBy(-downLinkDelay - x).toTransform().toStaticTransform().getInverse().transformPosition(antennaPhaseCenter1); + final Vector3D self = currentState.shiftedBy(-downLinkDelay - x).toStaticTransform().getInverse().transformPosition(antennaPhaseCenter1); final double d = Vector3D.distance(otherAtTransit, self); return d - x * Constants.SPEED_OF_LIGHT; } }, -1.0, 1.0); final Vector3D selfAtEmission = - currentState.shiftedBy(-downLinkDelay - upLinkDelay).toTransform().toStaticTransform().getInverse().transformPosition(antennaPhaseCenter1); + currentState.shiftedBy(-downLinkDelay - upLinkDelay).toStaticTransform().getInverse().transformPosition(antennaPhaseCenter1); final double upLinkDistance = Vector3D.distance(otherAtTransit, selfAtEmission); addMeasurement(new InterSatellitesRange(local, remote, true, date, 0.5 * (downLinkDistance + upLinkDistance), 1.0, 10)); diff --git a/src/test/java/org/orekit/estimation/measurements/InterSatellitesRangeTest.java b/src/test/java/org/orekit/estimation/measurements/InterSatellitesRangeTest.java index 5d15c179b8..9605cb560e 100644 --- a/src/test/java/org/orekit/estimation/measurements/InterSatellitesRangeTest.java +++ b/src/test/java/org/orekit/estimation/measurements/InterSatellitesRangeTest.java @@ -172,8 +172,7 @@ void genericTestValues(final boolean printResults) { // Values of the Range & errors final double RangeObserved = measurement.getObservedValue()[0]; - final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(0, 0, - new SpacecraftState[] { + final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(new SpacecraftState[] { state, ephemeris.propagate(state.getDate()) }); @@ -330,7 +329,7 @@ void genericTestStateDerivatives(final boolean printResults, final int index, public double[] value(final SpacecraftState state) { final SpacecraftState[] s = states.clone(); s[index] = state; - return measurement.estimateWithoutDerivatives(0, 0, s).getEstimatedValue(); + return measurement.estimateWithoutDerivatives(s).getEstimatedValue(); } }, measurement.getDimension(), propagator.getAttitudeProvider(), OrbitType.CARTESIAN, PositionAngleType.TRUE, 2.0, 3).value(states[index]); @@ -518,7 +517,7 @@ void genericTestParameterDerivatives(final double refErrorsMedian, final double @Override public double value(final ParameterDriver parameterDriver, final AbsoluteDate date) { return measurement. - estimateWithoutDerivatives(0, 0, states). + estimateWithoutDerivatives(states). getEstimatedValue()[0]; } }, 3, 20.0 * drivers[i].getScale()); diff --git a/src/test/java/org/orekit/estimation/measurements/OneWayRangeMeasurementCreator.java b/src/test/java/org/orekit/estimation/measurements/OneWayRangeMeasurementCreator.java index 1dd7788e19..9056515bad 100644 --- a/src/test/java/org/orekit/estimation/measurements/OneWayRangeMeasurementCreator.java +++ b/src/test/java/org/orekit/estimation/measurements/OneWayRangeMeasurementCreator.java @@ -86,7 +86,7 @@ public void handleStep(final SpacecraftState currentState) { for (final GroundStation station : context.stations) { final AbsoluteDate date = currentState.getDate(); final Frame inertial = currentState.getFrame(); - final Vector3D position = currentState.toTransform().toStaticTransform().getInverse().transformPosition(satelliteMeanPosition); + final Vector3D position = currentState.toStaticTransform().getInverse().transformPosition(satelliteMeanPosition); if (station.getBaseFrame().getTrackingCoordinates(position, inertial, date).getElevation() > FastMath.toRadians(30.0)) { final UnivariateSolver solver = new BracketingNthOrderBrentSolver(1.0e-12, 5); diff --git a/src/test/java/org/orekit/estimation/measurements/PVTest.java b/src/test/java/org/orekit/estimation/measurements/PVTest.java index 4caa165b66..9890373273 100644 --- a/src/test/java/org/orekit/estimation/measurements/PVTest.java +++ b/src/test/java/org/orekit/estimation/measurements/PVTest.java @@ -74,7 +74,7 @@ public void testValues() { SpacecraftState state = propagator.propagate(datemeas); // Estimate the PV value - final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }); + final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(new SpacecraftState[] { state }); // Store the difference between estimated and observed values in the stats for (int i = 0; i < 6; i++) { @@ -133,7 +133,7 @@ public void testStateDerivatives() { Differentiation.differentiate(new StateFunction() { public double[] value(final SpacecraftState state) { return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). + estimateWithoutDerivatives(new SpacecraftState[] { state }). getEstimatedValue(); } }, measurement.getDimension(), diff --git a/src/test/java/org/orekit/estimation/measurements/PositionTest.java b/src/test/java/org/orekit/estimation/measurements/PositionTest.java index 5e288392e0..67f2ab462e 100644 --- a/src/test/java/org/orekit/estimation/measurements/PositionTest.java +++ b/src/test/java/org/orekit/estimation/measurements/PositionTest.java @@ -74,7 +74,7 @@ public void testValues() { SpacecraftState state = propagator.propagate(datemeas); // Estimate the position value - final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }); + final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(new SpacecraftState[] { state }); // Store the difference between estimated and observed values in the stats for (int i = 0; i < 3; i++) { @@ -127,7 +127,7 @@ public void testStateDerivatives() { Differentiation.differentiate(new StateFunction() { public double[] value(final SpacecraftState state) { return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). + estimateWithoutDerivatives(new SpacecraftState[] { state }). getEstimatedValue(); } }, measurement.getDimension(), diff --git a/src/test/java/org/orekit/estimation/measurements/QuadraticClockModelTest.java b/src/test/java/org/orekit/estimation/measurements/QuadraticClockModelTest.java new file mode 100644 index 0000000000..34dab126af --- /dev/null +++ b/src/test/java/org/orekit/estimation/measurements/QuadraticClockModelTest.java @@ -0,0 +1,220 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.estimation.measurements; + +import org.hipparchus.analysis.differentiation.Gradient; +import org.hipparchus.analysis.differentiation.GradientField; +import org.hipparchus.util.Binary64; +import org.hipparchus.util.Binary64Field; +import org.hipparchus.util.FastMath; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.ParameterDriver; +import org.orekit.utils.TimeSpanMap.Span; + +import java.util.HashMap; +import java.util.Map; + +public class QuadraticClockModelTest { + + @Test + public void testValue() { + final AbsoluteDate t0 = AbsoluteDate.GALILEO_EPOCH; + final QuadraticClockModel clock = new QuadraticClockModel(t0, + FastMath.scalb(1.0, -8), + FastMath.scalb(1.0, -9), + FastMath.scalb(1.0, -10)); + Assertions.assertEquals(1.00 / 256.0, clock.getOffset(t0).getOffset(), 1.0e-15); + Assertions.assertEquals(1.75 / 256.0, clock.getOffset(t0.shiftedBy(1.0)).getOffset(), 1.0e-15); + Assertions.assertEquals(3.00 / 256.0, clock.getOffset(t0.shiftedBy(2.0)).getOffset(), 1.0e-15); + } + + @Test + public void testValueField() { + final AbsoluteDate t0 = AbsoluteDate.GALILEO_EPOCH; + final QuadraticClockModel clock = new QuadraticClockModel(t0, + FastMath.scalb(1.0, -8), + FastMath.scalb(1.0, -9), + FastMath.scalb(1.0, -10)); + final FieldAbsoluteDate t064 = new FieldAbsoluteDate<>(Binary64Field.getInstance(), t0); + Assertions.assertEquals(1.00 / 256.0, clock.getOffset(t064).getOffset().getReal(), 1.0e-15); + Assertions.assertEquals(1.75 / 256.0, clock.getOffset(t064.shiftedBy(1.0)).getOffset().getReal(), 1.0e-15); + Assertions.assertEquals(3.00 / 256.0, clock.getOffset(t064.shiftedBy(2.0)).getOffset().getReal(), 1.0e-15); + } + + @Test + public void testRate() { + final AbsoluteDate t0 = AbsoluteDate.GALILEO_EPOCH; + final QuadraticClockModel clock = new QuadraticClockModel(t0, + FastMath.scalb(1.0, -8), + FastMath.scalb(1.0, -9), + FastMath.scalb(1.0, -10)); + Assertions.assertEquals(1.00 / 512, clock.getOffset(t0).getRate(), 1.0e-15); + Assertions.assertEquals(2.00 / 512, clock.getOffset(t0.shiftedBy(1.0)).getRate(), 1.0e-15); + Assertions.assertEquals(3.00 / 512, clock.getOffset(t0.shiftedBy(2.0)).getRate(), 1.0e-15); + } + + @Test + public void testRateField() { + final AbsoluteDate t0 = AbsoluteDate.GALILEO_EPOCH; + final QuadraticClockModel clock = new QuadraticClockModel(t0, + FastMath.scalb(1.0, -8), + FastMath.scalb(1.0, -9), + FastMath.scalb(1.0, -10)); + final FieldAbsoluteDate t064 = new FieldAbsoluteDate<>(Binary64Field.getInstance(), t0); + Assertions.assertEquals(1.00 / 512, clock.getOffset(t064).getRate().getReal(), 1.0e-15); + Assertions.assertEquals(2.00 / 512, clock.getOffset(t064.shiftedBy(1.0)).getRate().getReal(), 1.0e-15); + Assertions.assertEquals(3.00 / 512, clock.getOffset(t064.shiftedBy(2.0)).getRate().getReal(), 1.0e-15); + } + + @Test + public void testAcceleration() { + final AbsoluteDate t0 = AbsoluteDate.GALILEO_EPOCH; + final QuadraticClockModel clock = new QuadraticClockModel(t0, + FastMath.scalb(1.0, -8), + FastMath.scalb(1.0, -9), + FastMath.scalb(1.0, -10)); + Assertions.assertEquals(2.00 / 1024, clock.getOffset(t0).getAcceleration(), 1.0e-15); + Assertions.assertEquals(2.00 / 1024, clock.getOffset(t0.shiftedBy(1.0)).getAcceleration(), 1.0e-15); + Assertions.assertEquals(2.00 / 1024, clock.getOffset(t0.shiftedBy(2.0)).getAcceleration(), 1.0e-15); + } + + @Test + public void testAccelerationField() { + final AbsoluteDate t0 = AbsoluteDate.GALILEO_EPOCH; + final QuadraticClockModel clock = new QuadraticClockModel(t0, + FastMath.scalb(1.0, -8), + FastMath.scalb(1.0, -9), + FastMath.scalb(1.0, -10)); + final FieldAbsoluteDate t064 = new FieldAbsoluteDate<>(Binary64Field.getInstance(), t0); + Assertions.assertEquals(2.00 / 1024, clock.getOffset(t064).getAcceleration().getReal(), 1.0e-15); + Assertions.assertEquals(2.00 / 1024, clock.getOffset(t064.shiftedBy(1.0)).getAcceleration().getReal(), 1.0e-15); + Assertions.assertEquals(2.00 / 1024, clock.getOffset(t064.shiftedBy(2.0)).getAcceleration().getReal(), 1.0e-15); + } + + @Test + public void testValidity() { + final AbsoluteDate t0 = AbsoluteDate.GALILEO_EPOCH; + final QuadraticClockModel clock = new QuadraticClockModel(t0, + FastMath.scalb(1.0, -8), + FastMath.scalb(1.0, -9), + FastMath.scalb(1.0, -10)); + Assertions.assertEquals(AbsoluteDate.PAST_INFINITY, clock.getValidityStart()); + Assertions.assertEquals(AbsoluteDate.FUTURE_INFINITY, clock.getValidityEnd()); + } + + @Test + public void testSafeReferenceDate() { + final ParameterDriver a0 = new ParameterDriver("a0", 0.0, 1.0, + Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); + final ParameterDriver a1 = new ParameterDriver("a1", 0.0, 1.0, + Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); + final ParameterDriver a2 = new ParameterDriver("a2", 0.0, 1.0, + Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); + final QuadraticClockModel clock = new QuadraticClockModel(a0, a1, a2); + + // OK to have no reference date if a1 and a2 are both zero + a0.setValue(0.125); + Assertions.assertEquals(0.125, clock.getOffset(AbsoluteDate.GALILEO_EPOCH).getOffset()); + + // not OK to have no reference date if a1 is non zero + a1.setValue(1.0); + try { + clock.getOffset(AbsoluteDate.GALILEO_EPOCH); + Assertions.fail("an exception should have been thrown"); + } catch (OrekitException oe) { + Assertions.assertEquals(OrekitMessages.NO_REFERENCE_DATE_FOR_PARAMETER, oe.getSpecifier()); + Assertions.assertEquals(a0.getName(), oe.getParts()[0]); + } + + // not OK to have no reference date if a2 is non zero + a1.setValue(0.0); + a2.setValue(1.0); + try { + clock.getOffset(AbsoluteDate.GALILEO_EPOCH); + Assertions.fail("an exception should have been thrown"); + } catch (OrekitException oe) { + Assertions.assertEquals(OrekitMessages.NO_REFERENCE_DATE_FOR_PARAMETER, oe.getSpecifier()); + Assertions.assertEquals(a0.getName(), oe.getParts()[0]); + } + + // back to OK if we reset drift and acceleration + a2.setValue(0); + Assertions.assertEquals(0.125, clock.getOffset(AbsoluteDate.GALILEO_EPOCH).getOffset()); + + } + + @Test + public void testGradient() { + final AbsoluteDate t0 = AbsoluteDate.GALILEO_EPOCH; + final ParameterDriver a0 = new ParameterDriver("a0", 0.0, 1.0, + Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); + final ParameterDriver a1 = new ParameterDriver("a1", 0.0, 1.0, + Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); + final ParameterDriver a2 = new ParameterDriver("a2", 0.0, 1.0, + Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); + final QuadraticClockModel clock = new QuadraticClockModel(a0, a1, a2); + + int nbParams = 0; + final Map indices = new HashMap<>(); + a0.setValue(FastMath.scalb(1.0, -8)); + a0.setReferenceDate(t0); + a0.setSelected(true); + for (Span span = a0.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + indices.put(span.getData(), nbParams++); + } + a1.setValue(FastMath.scalb(1.0, -9)); + a1.setReferenceDate(t0); + a1.setSelected(true); + for (Span span = a1.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + indices.put(span.getData(), nbParams++); + } + a2.setValue(FastMath.scalb(1.0, -10)); + a2.setReferenceDate(t0); + a2.setSelected(true); + for (Span span = a2.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + indices.put(span.getData(), nbParams++); + } + + QuadraticFieldClockModel gradientModel = clock.toGradientModel(nbParams, indices, t0); + final FieldAbsoluteDate t0g = new FieldAbsoluteDate<>(GradientField.getField(nbParams), t0); + + final Gradient g0 = gradientModel.getOffset(t0g).getOffset(); + Assertions.assertEquals(1.00 / 256, g0.getValue(), 1.0e-15); + Assertions.assertEquals(1.00, g0.getPartialDerivative(0), 1.0e-15); + Assertions.assertEquals(0.00, g0.getPartialDerivative(1), 1.0e-15); + Assertions.assertEquals(0.00, g0.getPartialDerivative(2), 1.0e-15); + + final Gradient g1 = gradientModel.getOffset(t0g.shiftedBy(1.0)).getOffset(); + Assertions.assertEquals(1.75 / 256, g1.getValue(), 1.0e-15); + Assertions.assertEquals(1.00, g1.getPartialDerivative(0), 1.0e-15); + Assertions.assertEquals(1.00, g1.getPartialDerivative(1), 1.0e-15); + Assertions.assertEquals(1.00, g1.getPartialDerivative(2), 1.0e-15); + + final Gradient g2 = gradientModel.getOffset(t0g.shiftedBy(2.0)).getOffset(); + Assertions.assertEquals(3.00 / 256, g2.getValue(), 1.0e-15); + Assertions.assertEquals(1.00, g2.getPartialDerivative(0), 1.0e-15); + Assertions.assertEquals(2.00, g2.getPartialDerivative(1), 1.0e-15); + Assertions.assertEquals(4.00, g2.getPartialDerivative(2), 1.0e-15); + + } + +} diff --git a/src/test/java/org/orekit/estimation/measurements/QuadraticFieldClockModelTest.java b/src/test/java/org/orekit/estimation/measurements/QuadraticFieldClockModelTest.java new file mode 100644 index 0000000000..45eb307bd6 --- /dev/null +++ b/src/test/java/org/orekit/estimation/measurements/QuadraticFieldClockModelTest.java @@ -0,0 +1,65 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.estimation.measurements; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.util.Binary64Field; +import org.hipparchus.util.FastMath; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; + + +public class QuadraticFieldClockModelTest { + + @Test + public void testValueField() { + doTestValueField(Binary64Field.getInstance()); + } + + @Test + public void testRateField() { + doTestRateField(Binary64Field.getInstance()); + } + + private > void doTestValueField(final Field field) { + final FieldAbsoluteDate t0 = new FieldAbsoluteDate<>(field, AbsoluteDate.GALILEO_EPOCH); + final QuadraticFieldClockModel clock = + new QuadraticFieldClockModel<>(t0, + field.getZero().newInstance(FastMath.scalb(1.0, -8)), + field.getZero().newInstance(FastMath.scalb(1.0, -9)), + field.getZero().newInstance(FastMath.scalb(1.0, -10))); + Assertions.assertEquals(1.00 / 256.0, clock.getOffset(t0).getOffset().getReal(), 1.0e-15); + Assertions.assertEquals(1.75 / 256.0, clock.getOffset(t0.shiftedBy(1.0)).getOffset().getReal(), 1.0e-15); + Assertions.assertEquals(3.00 / 256.0, clock.getOffset(t0.shiftedBy(2.0)).getOffset().getReal(), 1.0e-15); + } + + private > void doTestRateField(final Field field) { + final FieldAbsoluteDate t0 = new FieldAbsoluteDate<>(field, AbsoluteDate.GALILEO_EPOCH); + final QuadraticFieldClockModel clock = + new QuadraticFieldClockModel<>(t0, + field.getZero().newInstance(FastMath.scalb(1.0, -8)), + field.getZero().newInstance(FastMath.scalb(1.0, -9)), + field.getZero().newInstance(FastMath.scalb(1.0, -10))); + Assertions.assertEquals(1.00 / 512, clock.getOffset(t0).getRate().getReal(), 1.0e-15); + Assertions.assertEquals(2.00 / 512, clock.getOffset(t0.shiftedBy(1.0)).getRate().getReal(), 1.0e-15); + Assertions.assertEquals(3.00 / 512, clock.getOffset(t0.shiftedBy(2.0)).getRate().getReal(), 1.0e-15); + } + +} diff --git a/src/test/java/org/orekit/estimation/measurements/Range2Test.java b/src/test/java/org/orekit/estimation/measurements/Range2Test.java index 8e9730b6eb..466b505d9f 100644 --- a/src/test/java/org/orekit/estimation/measurements/Range2Test.java +++ b/src/test/java/org/orekit/estimation/measurements/Range2Test.java @@ -106,12 +106,12 @@ public void testStateDerivativesWithModifier() { } // Run test boolean isModifier = true; - double refErrorsPMedian = 6.3e-10; - double refErrorsPMean = 4.2e-09; - double refErrorsPMax = 2.4e-07; - double refErrorsVMedian = 1.4e-04; - double refErrorsVMean = 9.6e-04; - double refErrorsVMax = 5.2e-02; + double refErrorsPMedian = 5.5e-10; + double refErrorsPMean = 4.6e-09; + double refErrorsPMax = 2.1e-07; + double refErrorsVMedian = 1.3e-04; + double refErrorsVMean = 9.4e-04; + double refErrorsVMax = 4.8e-02; this.genericTestStateDerivatives(isModifier, printResults, refErrorsPMedian, refErrorsPMean, refErrorsPMax, refErrorsVMedian, refErrorsVMean, refErrorsVMax); @@ -212,8 +212,7 @@ void genericTestValues(final boolean printResults) // Values of the Range & errors final double RangeObserved = measurement.getObservedValue()[0]; - final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(0, 0, - new SpacecraftState[] { state }); + final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(new SpacecraftState[] { state }); final TimeStampedPVCoordinates[] participants = estimated.getParticipants(); Assertions.assertEquals(2, participants.length); @@ -354,7 +353,7 @@ void genericTestStateDerivatives(final boolean isModifier, final boolean printRe jacobianRef = Differentiation.differentiate(new StateFunction() { public double[] value(final SpacecraftState state) { return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). + estimateWithoutDerivatives(new SpacecraftState[] { state }). getEstimatedValue(); } }, measurement.getDimension(), propagator.getAttitudeProvider(), @@ -524,7 +523,7 @@ void genericTestParameterDerivatives(final boolean isModifier, final boolean pri @Override public double value(final ParameterDriver parameterDriver, final AbsoluteDate date) { return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). + estimateWithoutDerivatives(new SpacecraftState[] { state }). getEstimatedValue()[0]; } }, 3, 20.0 * drivers[i].getScale()); diff --git a/src/test/java/org/orekit/estimation/measurements/RangeAnalytic.java b/src/test/java/org/orekit/estimation/measurements/RangeAnalytic.java index ab5287a766..71522d29f8 100644 --- a/src/test/java/org/orekit/estimation/measurements/RangeAnalytic.java +++ b/src/test/java/org/orekit/estimation/measurements/RangeAnalytic.java @@ -104,7 +104,8 @@ protected EstimatedMeasurement theoreticalEvaluationAnalytic(final int it // Downlink time of flight final double tauD = signalTimeOfFlight(state.getPVCoordinates(), stationDownlink.getPosition(), - downlinkDate); + downlinkDate, + state.getFrame()); final double delta = downlinkDate.durationFrom(state.getDate()); final double dt = delta - tauD; @@ -120,7 +121,7 @@ protected EstimatedMeasurement theoreticalEvaluationAnalytic(final int it transformPVCoordinates(new TimeStampedPVCoordinates(transitDate, PVCoordinates.ZERO)); // Uplink time of flight - final double tauU = signalTimeOfFlight(stationAtTransitDate, transitP, transitDate); + final double tauU = signalTimeOfFlight(stationAtTransitDate, transitP, transitDate, state.getFrame()); final double tau = tauD + tauU; // Real date and position of station at signal departure @@ -309,7 +310,8 @@ protected EstimatedMeasurement theoreticalEvaluationValidation(final int // the same as state) // Downlink delay - final Gradient tauD = signalTimeOfFlight(pvaDS, stationDownlink.getPosition(), downlinkDateDS); + final Gradient tauD = signalTimeOfFlight(pvaDS, stationDownlink.getPosition(), + downlinkDateDS, state.getFrame()); // Transit state final double delta = downlinkDate.durationFrom(state.getDate()); @@ -324,7 +326,8 @@ protected EstimatedMeasurement theoreticalEvaluationValidation(final int stationDownlink.shiftedBy(tauD.negate()); // Uplink delay final Gradient tauU = - signalTimeOfFlight(stationAtTransitDate, transitStateDS.getPosition(), transitStateDS.getDate()); + signalTimeOfFlight(stationAtTransitDate, transitStateDS.getPosition(), + transitStateDS.getDate(), state.getFrame()); // Prepare the evaluation final EstimatedMeasurement estimated = @@ -371,7 +374,8 @@ protected EstimatedMeasurement theoreticalEvaluationValidation(final int transformPVCoordinates(PVCoordinates.ZERO); // Downlink time of flight from spacecraft to station - final double td = signalTimeOfFlight(state.getPVCoordinates(), QDownlink.getPosition(), downlinkDate); + final double td = signalTimeOfFlight(state.getPVCoordinates(), QDownlink.getPosition(), downlinkDate, + state.getFrame()); final double dt = delta - td; // Transit state position @@ -394,7 +398,7 @@ protected EstimatedMeasurement theoreticalEvaluationValidation(final int transformPVCoordinates(new TimeStampedPVCoordinates(transitT, PVCoordinates.ZERO)); // Uplink time of flight - final double tu = signalTimeOfFlight(QAtTransitDate, transitP, transitT); + final double tu = signalTimeOfFlight(QAtTransitDate, transitP, transitT, state.getFrame()); // Total time of flight final double t = td + tu; diff --git a/src/test/java/org/orekit/estimation/measurements/RangeAnalyticTest.java b/src/test/java/org/orekit/estimation/measurements/RangeAnalyticTest.java index d7763ac961..f42ab2941e 100644 --- a/src/test/java/org/orekit/estimation/measurements/RangeAnalyticTest.java +++ b/src/test/java/org/orekit/estimation/measurements/RangeAnalyticTest.java @@ -392,7 +392,7 @@ void genericTestStateDerivatives(final boolean isModifier, final boolean isFinit jacobianRef = Differentiation.differentiate(new StateFunction() { public double[] value(final SpacecraftState state) { return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). + estimateWithoutDerivatives(new SpacecraftState[] { state }). getEstimatedValue(); } }, measurement.getDimension(), propagator.getAttitudeProvider(), diff --git a/src/test/java/org/orekit/estimation/measurements/RangeRateTest.java b/src/test/java/org/orekit/estimation/measurements/RangeRateTest.java index 26f85760e2..8451fd83a8 100644 --- a/src/test/java/org/orekit/estimation/measurements/RangeRateTest.java +++ b/src/test/java/org/orekit/estimation/measurements/RangeRateTest.java @@ -25,7 +25,7 @@ import org.orekit.estimation.Context; import org.orekit.estimation.EstimationTestUtils; import org.orekit.estimation.measurements.modifiers.RangeRateTroposphericDelayModifier; -import org.orekit.models.earth.troposphere.EstimatedTroposphericModel; +import org.orekit.models.earth.troposphere.EstimatedModel; import org.orekit.models.earth.troposphere.GlobalMappingFunctionModel; import org.orekit.models.earth.troposphere.ModifiedSaastamoinenModel; import org.orekit.orbits.OrbitType; @@ -76,7 +76,7 @@ public void testValuesOneWay() { SpacecraftState state = propagator.propagate(datemeas); // Estimate the AZEL value - final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }); + final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(new SpacecraftState[] { state }); // Store the difference between estimated and observed values in the stats diffStat.addValue(FastMath.abs(estimated.getEstimatedValue()[0] - measurement.getObservedValue()[0])); @@ -124,7 +124,7 @@ public void testValuesTwoWays() { SpacecraftState state = propagator.propagate(datemeas); // Estimate the AZEL value - final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }); + final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(new SpacecraftState[] { state }); // Store the difference between estimated and observed values in the stats diffStat.addValue(FastMath.abs(estimated.getEstimatedValue()[0] - measurement.getObservedValue()[0])); @@ -176,7 +176,7 @@ public void testStateDerivativesOneWay() { Differentiation.differentiate(new StateFunction() { public double[] value(final SpacecraftState state) { return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). + estimateWithoutDerivatives(new SpacecraftState[] { state }). getEstimatedValue(); } }, 1, propagator.getAttitudeProvider(), @@ -242,7 +242,7 @@ public void testStateDerivativesTwoWays() { Differentiation.differentiate(new StateFunction() { public double[] value(final SpacecraftState state) { return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). + estimateWithoutDerivatives(new SpacecraftState[] { state }). getEstimatedValue(); } }, 1, propagator.getAttitudeProvider(), @@ -338,7 +338,7 @@ public void testParameterDerivativesOneWay() { @Override public double value(final ParameterDriver parameterDriver, AbsoluteDate date) { return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). + estimateWithoutDerivatives(new SpacecraftState[] { state }). getEstimatedValue()[0]; } }, 3, 20.0 * drivers[i].getScale()); @@ -420,7 +420,7 @@ public void testParameterDerivativesTwoWays() { @Override public double value(final ParameterDriver parameterDriver, AbsoluteDate date) { return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). + estimateWithoutDerivatives(new SpacecraftState[] { state }). getEstimatedValue()[0]; } }, 3, 20.0 * drivers[i].getScale()); @@ -495,7 +495,7 @@ public double[] value(final SpacecraftState state) { } } - Assertions.assertEquals(0, maxRelativeError, 1.4e-7); + Assertions.assertEquals(0, maxRelativeError, 1.5e-7); } @@ -532,7 +532,7 @@ public void testStateDerivativesWithEstimatedModifier() { // Add modifiers if test implies it final GlobalMappingFunctionModel mappingFunction = new GlobalMappingFunctionModel(); - final EstimatedTroposphericModel tropoModel = new EstimatedTroposphericModel(mappingFunction, 5.0); + final EstimatedModel tropoModel = new EstimatedModel(mappingFunction, 5.0); final RangeRateTroposphericDelayModifier modifier = new RangeRateTroposphericDelayModifier(tropoModel, true); ((RangeRate) measurement).addModifier(modifier); @@ -566,7 +566,7 @@ public double[] value(final SpacecraftState state) { } } - Assertions.assertEquals(0, maxRelativeError, 3.1e-7); + Assertions.assertEquals(0, maxRelativeError, 3.4e-7); } @@ -644,7 +644,7 @@ public void testParameterDerivativesWithModifier() { @Override public double value(final ParameterDriver parameterDriver, AbsoluteDate date) { return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). + estimateWithoutDerivatives(new SpacecraftState[] { state }). getEstimatedValue()[0]; } }, 3, 20.0 * drivers[i].getScale()); @@ -689,7 +689,7 @@ public void testParameterDerivativesWithEstimatedModifier() { // Add modifiers if test implies it final GlobalMappingFunctionModel mappingFunction = new GlobalMappingFunctionModel(); - final EstimatedTroposphericModel tropoModel = new EstimatedTroposphericModel(mappingFunction, 10.0); + final EstimatedModel tropoModel = new EstimatedModel(mappingFunction, 10.0); final List parameters = tropoModel.getParametersDrivers(); for (ParameterDriver driver : parameters) { @@ -724,7 +724,7 @@ public void testParameterDerivativesWithEstimatedModifier() { @Override public double value(final ParameterDriver parameterDriver, AbsoluteDate date) { return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). + estimateWithoutDerivatives(new SpacecraftState[] { state }). getEstimatedValue()[0]; } }, 3, 0.1 * drivers[i].getScale()); diff --git a/src/test/java/org/orekit/estimation/measurements/RangeTest.java b/src/test/java/org/orekit/estimation/measurements/RangeTest.java index 269584b72e..b518da30e5 100644 --- a/src/test/java/org/orekit/estimation/measurements/RangeTest.java +++ b/src/test/java/org/orekit/estimation/measurements/RangeTest.java @@ -31,9 +31,9 @@ import org.orekit.estimation.Context; import org.orekit.estimation.EstimationTestUtils; import org.orekit.estimation.measurements.modifiers.RangeTroposphericDelayModifier; -import org.orekit.models.earth.troposphere.EstimatedTroposphericModel; -import org.orekit.models.earth.troposphere.NiellMappingFunctionModel; +import org.orekit.models.earth.troposphere.EstimatedModel; import org.orekit.models.earth.troposphere.ModifiedSaastamoinenModel; +import org.orekit.models.earth.troposphere.NiellMappingFunctionModel; import org.orekit.orbits.OrbitType; import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.Propagator; @@ -47,14 +47,14 @@ import org.orekit.utils.StateFunction; import org.orekit.utils.TimeStampedPVCoordinates; -public class RangeTest { +class RangeTest { /** * Test the values of the range comparing the observed values and the estimated values * Both are calculated with a different algorithm */ @Test - public void testValues() { + void testValues() { boolean printResults = false; if (printResults) { System.out.println("\nTest Range Values\n"); @@ -68,7 +68,7 @@ public void testValues() { * finite differences calculation as a reference */ @Test - public void testStateDerivatives() { + void testStateDerivatives() { boolean printResults = false; if (printResults) { @@ -76,8 +76,8 @@ public void testStateDerivatives() { } // Run test boolean isModifier = false; - double refErrorsPMedian = 6.0e-10; - double refErrorsPMean = 3.0e-09; + double refErrorsPMedian = 6.7e-10; + double refErrorsPMean = 3.1e-09; double refErrorsPMax = 1.0e-07; double refErrorsVMedian = 2.1e-04; double refErrorsVMean = 1.3e-03; @@ -92,7 +92,7 @@ public void testStateDerivatives() { * finite differences calculation as a reference */ @Test - public void testStateDerivativesWithModifier() { + void testStateDerivativesWithModifier() { boolean printResults = false; if (printResults) { @@ -100,9 +100,9 @@ public void testStateDerivativesWithModifier() { } // Run test boolean isModifier = true; - double refErrorsPMedian = 7.5e-10; - double refErrorsPMean = 3.2e-09; - double refErrorsPMax = 9.2e-08; + double refErrorsPMedian = 7.9e-10; + double refErrorsPMean = 2.6e-09; + double refErrorsPMax = 9.3e-08; double refErrorsVMedian = 2.1e-04; double refErrorsVMean = 1.3e-03; double refErrorsVMax = 5.2e-02; @@ -116,7 +116,7 @@ public void testStateDerivativesWithModifier() { * finite differences calculation as a reference */ @Test - public void testParameterDerivatives() { + void testParameterDerivatives() { // Print the results ? boolean printResults = false; @@ -139,7 +139,7 @@ public void testParameterDerivatives() { * finite differences calculation as a reference */ @Test - public void testParameterDerivativesWithModifier() { + void testParameterDerivativesWithModifier() { // Print the results ? boolean printResults = false; @@ -162,7 +162,7 @@ public void testParameterDerivativesWithModifier() { * finite differences calculation as a reference */ @Test - public void testParameterDerivativesWithEstimatedModifier() { + void testParameterDerivativesWithEstimatedModifier() { // Print the results ? boolean printResults = false; @@ -174,7 +174,7 @@ public void testParameterDerivativesWithEstimatedModifier() { boolean isModifier = true; double refErrorsMedian = 1.2e-9; double refErrorsMean = 1.9e-9; - double refErrorsMax = 6.6e-9; + double refErrorsMax = 6.9e-9; this.genericTestEstimatedParameterDerivatives(isModifier, printResults, refErrorsMedian, refErrorsMean, refErrorsMax); @@ -184,6 +184,7 @@ public void testParameterDerivativesWithEstimatedModifier() { * Generic test function for values of the range * @param printResults Print the results ? */ + @SuppressWarnings("deprecation") void genericTestValues(final boolean printResults) { Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides"); @@ -232,8 +233,7 @@ void genericTestValues(final boolean printResults) { // Values of the Range & errors final double RangeObserved = measurement.getObservedValue()[0]; - final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(0, 0, - new SpacecraftState[] { state }); + final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(new SpacecraftState[] { state }); final TimeStampedPVCoordinates[] participants = estimated.getParticipants(); Assertions.assertEquals(3, participants.length); @@ -251,6 +251,17 @@ void genericTestValues(final boolean printResults) { absoluteErrors.add(absoluteError); relativeErrors.add(FastMath.abs(absoluteError)/FastMath.abs(RangeObserved)); + // test deprecated method (just for test coverage) + // here, the frame is not the same in both calls, but results should be the same as both are inertial frames + Assertions.assertEquals(AbstractMeasurement.signalTimeOfFlight(estimated.getParticipants()[0], + estimated.getParticipants()[1].getPosition(), + estimated.getParticipants()[1].getDate()), + AbstractMeasurement.signalTimeOfFlight(estimated.getParticipants()[0], + estimated.getParticipants()[1].getPosition(), + estimated.getParticipants()[1].getDate(), + state.getFrame()), + 1.0e-6); + // Print results on console ? if (printResults) { final AbsoluteDate measurementDate = measurement.getDate(); @@ -374,7 +385,7 @@ void genericTestStateDerivatives(final boolean isModifier, final boolean printRe jacobianRef = Differentiation.differentiate(new StateFunction() { public double[] value(final SpacecraftState state) { return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). + estimateWithoutDerivatives(new SpacecraftState[] { state }). getEstimatedValue(); } }, measurement.getDimension(), propagator.getAttitudeProvider(), @@ -544,7 +555,7 @@ void genericTestParameterDerivatives(final boolean isModifier, final boolean pri @Override public double value(final ParameterDriver parameterDriver, final AbsoluteDate date) { return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). + estimateWithoutDerivatives(new SpacecraftState[] { state }). getEstimatedValue()[0]; } }, 3, 20.0 * drivers[i].getScale()); @@ -642,14 +653,14 @@ void genericTestEstimatedParameterDerivatives(final boolean isModifier, final bo // Add modifiers if test implies it final NiellMappingFunctionModel mappingFunction = new NiellMappingFunctionModel(); - final EstimatedTroposphericModel tropoModel = new EstimatedTroposphericModel(mappingFunction, 5.0); + final EstimatedModel tropoModel = new EstimatedModel(mappingFunction, 5.0); final List parameters = tropoModel.getParametersDrivers(); for (ParameterDriver driver : parameters) { driver.setSelected(true); } - parameters.get(0).setName(stationName + "/" + EstimatedTroposphericModel.TOTAL_ZENITH_DELAY); + parameters.get(0).setName(stationName + "/" + EstimatedModel.TOTAL_ZENITH_DELAY); final RangeTroposphericDelayModifier modifier = new RangeTroposphericDelayModifier(tropoModel); if (isModifier) { ((Range) measurement).addModifier(modifier); @@ -686,7 +697,7 @@ void genericTestEstimatedParameterDerivatives(final boolean isModifier, final bo @Override public double value(final ParameterDriver parameterDriver, final AbsoluteDate date) { return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). + estimateWithoutDerivatives(new SpacecraftState[] { state }). getEstimatedValue()[0]; } }, 3, 0.1 * drivers[i].getScale()); diff --git a/src/test/java/org/orekit/estimation/measurements/TDOATest.java b/src/test/java/org/orekit/estimation/measurements/TDOATest.java index ac9afd9682..1d76c73adc 100644 --- a/src/test/java/org/orekit/estimation/measurements/TDOATest.java +++ b/src/test/java/org/orekit/estimation/measurements/TDOATest.java @@ -70,7 +70,7 @@ public void testValues() { SpacecraftState state = propagator.propagate(measurement.getDate()); // Estimate the measurement value - final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }); + final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(new SpacecraftState[] { state }); // Store the difference between estimated and observed values in the stats diffStat.addValue(FastMath.abs(estimated.getEstimatedValue()[0] - measurement.getObservedValue()[0])); @@ -120,7 +120,7 @@ public void testStateDerivatives() { Differentiation.differentiate(new StateFunction() { public double[] value(final SpacecraftState state) { return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). + estimateWithoutDerivatives(new SpacecraftState[] { state }). getEstimatedValue(); } }, 1, propagator.getAttitudeProvider(), OrbitType.CARTESIAN, PositionAngleType.TRUE, 15.0, 3).value(state); @@ -186,7 +186,7 @@ public void testStateDerivativesWithModifier() { Differentiation.differentiate(new StateFunction() { public double[] value(final SpacecraftState state) { return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). + estimateWithoutDerivatives(new SpacecraftState[] { state }). getEstimatedValue(); } }, 1, propagator.getAttitudeProvider(), @@ -280,7 +280,7 @@ public void testParameterDerivatives() { @Override public double value(final ParameterDriver parameterDriver, AbsoluteDate date) { return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). + estimateWithoutDerivatives(new SpacecraftState[] { state }). getEstimatedValue()[0]; } }, 3, 20.0 * drivers[i].getScale()); @@ -369,7 +369,7 @@ public void testParameterDerivativesWithModifier() { @Override public double value(final ParameterDriver parameterDriver, AbsoluteDate date) { return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). + estimateWithoutDerivatives(new SpacecraftState[] { state }). getEstimatedValue()[0]; } }, 3, 20.0 * drivers[i].getScale()); @@ -378,7 +378,7 @@ public double value(final ParameterDriver parameterDriver, AbsoluteDate date) { } } - Assertions.assertEquals(0, maxRelativeError, 9.0e-7); + Assertions.assertEquals(0, maxRelativeError, 2.5e-6); } diff --git a/src/test/java/org/orekit/estimation/measurements/TLEPVTest.java b/src/test/java/org/orekit/estimation/measurements/TLEPVTest.java index bd208b4e79..b02a85a760 100644 --- a/src/test/java/org/orekit/estimation/measurements/TLEPVTest.java +++ b/src/test/java/org/orekit/estimation/measurements/TLEPVTest.java @@ -73,7 +73,7 @@ public void testStateDerivatives() { Differentiation.differentiate(new StateFunction() { public double[] value(final SpacecraftState state) { return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). + estimateWithoutDerivatives(new SpacecraftState[] { state }). getEstimatedValue(); } }, measurement.getDimension(), diff --git a/src/test/java/org/orekit/estimation/measurements/TurnAroundRangeAnalytic.java b/src/test/java/org/orekit/estimation/measurements/TurnAroundRangeAnalytic.java index c956af17d3..72ac2a1d06 100644 --- a/src/test/java/org/orekit/estimation/measurements/TurnAroundRangeAnalytic.java +++ b/src/test/java/org/orekit/estimation/measurements/TurnAroundRangeAnalytic.java @@ -127,7 +127,8 @@ protected EstimatedMeasurement theoreticalEvaluationAnalytic(fi PVCoordinates.ZERO)); // Downlink time of flight from primary station at t to spacecraft at t' - final double tMd = signalTimeOfFlight(state.getPVCoordinates(), primaryArrival.getPosition(), measurementDate); + final double tMd = signalTimeOfFlight(state.getPVCoordinates(), primaryArrival.getPosition(), + measurementDate, state.getFrame()); // Time difference between t (date of the measurement) and t' (date tagged in spacecraft state) // (if state has already been set up to pre-compensate propagation delay, delta = primaryTauD + secondaryTauU) @@ -146,7 +147,8 @@ protected EstimatedMeasurement theoreticalEvaluationAnalytic(fi // Uplink time of flight from secondary station to transit state leg2 final double tSu = signalTimeOfFlight(QsecondaryTransitLeg2PV, transitStateLeg2.getPosition(), - transitDateLeg2); + transitDateLeg2, + state.getFrame()); // Total time of flight for leg 2 final double t2 = tMd + tSu; @@ -166,7 +168,8 @@ protected EstimatedMeasurement theoreticalEvaluationAnalytic(fi PVCoordinates.ZERO)); // Dowlink time of flight from transitStateLeg1 to secondary station at secondaryStationArrivalDate - final double tSd = signalTimeOfFlight(transitStateLeg2.getPVCoordinates(), secondaryRebound.getPosition(), secondaryStationArrivalDate); + final double tSd = signalTimeOfFlight(transitStateLeg2.getPVCoordinates(), secondaryRebound.getPosition(), + secondaryStationArrivalDate, state.getFrame()); // Transit state from which the satellite reflected the signal from primary to secondary station final SpacecraftState transitStateLeg1 = state.shiftedBy(delta -tMd -tSu -tSd); @@ -181,7 +184,8 @@ protected EstimatedMeasurement theoreticalEvaluationAnalytic(fi // Uplink time of flight from primary station to transit state leg1 final double tMu = signalTimeOfFlight(QPrimaryTransitLeg1PV, transitStateLeg1.getPosition(), - transitDateLeg1); + transitDateLeg1, + state.getFrame()); final AbsoluteDate emissionDate = transitDateLeg1.shiftedBy(-tMu); final TimeStampedPVCoordinates primaryDeparture = primaryTopoToInertTransitLeg1.shiftedBy(emissionDate.durationFrom(primaryTopoToInertTransitLeg1.getDate())). @@ -603,7 +607,7 @@ protected EstimatedMeasurement theoreticalEvaluationValidation( final FieldVector3D QPrimary = primaryToInert.transformPosition(zero); // Compute propagation times - final Gradient primaryTauD = signalTimeOfFlight(pvaDS, QPrimary, measurementDateDS); + final Gradient primaryTauD = signalTimeOfFlight(pvaDS, QPrimary, measurementDateDS, state.getFrame()); // Elapsed time between state date t' and signal arrival to the transit state of the 2nd leg final Gradient dtLeg2 = primaryTauD.negate().add(delta); @@ -629,7 +633,8 @@ protected EstimatedMeasurement theoreticalEvaluationValidation( final Gradient secondaryTauU = signalTimeOfFlight(QsecondaryApprox, transitStateLeg2PV.getPosition(), - transitStateLeg2PV.getDate()); + transitStateLeg2PV.getDate(), + state.getFrame()); // Total time of flight for leg 2 final Gradient tauLeg2 = primaryTauD.add(secondaryTauU); @@ -646,7 +651,8 @@ protected EstimatedMeasurement theoreticalEvaluationValidation( final FieldVector3D Qsecondary = secondaryToInert.transformPosition(zero); // Downlink time of flight from transitStateLeg1 to secondary station at rebound date - final Gradient secondaryTauD = signalTimeOfFlight(transitStateLeg2PV, Qsecondary, reboundDateDS); + final Gradient secondaryTauD = signalTimeOfFlight(transitStateLeg2PV, Qsecondary, + reboundDateDS, state.getFrame()); // Elapsed time between state date t' and signal arrival to the transit state of the 1st leg @@ -669,8 +675,9 @@ protected EstimatedMeasurement theoreticalEvaluationValidation( // Uplink time of flight from primary station to transit state of leg1 final Gradient primaryTauU = signalTimeOfFlight(QPrimaryApprox, - transitStateLeg1PV.getPosition(), - transitStateLeg1PV.getDate()); + transitStateLeg1PV.getPosition(), + transitStateLeg1PV.getDate(), + state.getFrame()); // Total time of flight for leg 1 final Gradient tauLeg1 = secondaryTauD.add(primaryTauU); @@ -738,7 +745,8 @@ protected EstimatedMeasurement theoreticalEvaluationValidation( transformPVCoordinates(new TimeStampedPVCoordinates(measurementDate, PVCoordinates.ZERO)); // Downlink time of flight from primary station at t to spacecraft at t' - final double tMd = signalTimeOfFlight(state.getPVCoordinates(), QMt.getPosition(), measurementDate); + final double tMd = signalTimeOfFlight(state.getPVCoordinates(), QMt.getPosition(), + measurementDate, state.getFrame()); // Transit state from which the satellite reflected the signal from secondary to primary station final SpacecraftState state2 = state.shiftedBy(delta - tMd); @@ -753,7 +761,8 @@ protected EstimatedMeasurement theoreticalEvaluationValidation( // Uplink time of flight from secondary station to transit state leg2 final double tSu = signalTimeOfFlight(QSdate2PV, state2.getPosition(), - transitDateLeg2); + transitDateLeg2, + state.getFrame()); // Total time of flight for leg 2 final double t2 = tMd + tSu; @@ -771,7 +780,7 @@ protected EstimatedMeasurement theoreticalEvaluationValidation( final Vector3D QSA = secondaryTopoToInertArrivalDate.transformPosition(Vector3D.ZERO); // Dowlink time of flight from transitStateLeg1 to secondary station at secondaryStationArrivalDate - final double tSd = signalTimeOfFlight(state2.getPVCoordinates(), QSA, tQSA); + final double tSd = signalTimeOfFlight(state2.getPVCoordinates(), QSA, tQSA, state2.getFrame()); // Transit state from which the satellite reflected the signal from primary to secondary station @@ -787,7 +796,8 @@ protected EstimatedMeasurement theoreticalEvaluationValidation( // Uplink time of flight from primary station to transit state leg1 final double tMu = signalTimeOfFlight(QMdate1PV, state1.getPosition(), - transitDateLeg1); + transitDateLeg1, + state1.getFrame()); // Total time of flight for leg 1 final double t1 = tSd + tMu; diff --git a/src/test/java/org/orekit/estimation/measurements/TurnAroundRangeAnalyticTest.java b/src/test/java/org/orekit/estimation/measurements/TurnAroundRangeAnalyticTest.java index ae02fd42a5..a6ef7767fd 100644 --- a/src/test/java/org/orekit/estimation/measurements/TurnAroundRangeAnalyticTest.java +++ b/src/test/java/org/orekit/estimation/measurements/TurnAroundRangeAnalyticTest.java @@ -213,7 +213,7 @@ public void testParameterDerivativesWithModifierFiniteDifferences() { boolean isModifier = true; boolean isFiniteDifferences = true; genericTestParameterDerivatives(isModifier, isFiniteDifferences, printResults, - 3.0e-06, 5.9e-06, 1.3e-04, 2.9e-6, 5.0e-6, 3.9e-5); + 2.8e-06, 5.9e-06, 1.2e-04, 4.3e-6, 2.3e-5, 2.2e-4); } @@ -221,8 +221,7 @@ public void testParameterDerivativesWithModifierFiniteDifferences() { * Generic test function for values of the TAR * @param printResults Print the results ? */ - void genericTestValues(final boolean printResults) - { + void genericTestValues(final boolean printResults) { Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides"); //Context context = EstimationTestUtils.geoStationnaryContext(); @@ -396,7 +395,7 @@ void genericTestStateDerivatives(final boolean isModifier, final boolean isFinit jacobianRef = Differentiation.differentiate(new StateFunction() { public double[] value(final SpacecraftState state) { return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). + estimateWithoutDerivatives(new SpacecraftState[] { state }). getEstimatedValue(); } }, measurement.getDimension(), propagator.getAttitudeProvider(), @@ -599,7 +598,7 @@ void genericTestParameterDerivatives(final boolean isModifier, final boolean isF @Override public double value(final ParameterDriver parameterDriver, AbsoluteDate date) { return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). + estimateWithoutDerivatives(new SpacecraftState[] { state }). getEstimatedValue()[0]; } }, 3, 20.0 * drivers[i].getScale()); diff --git a/src/test/java/org/orekit/estimation/measurements/TurnAroundRangeMeasurementCreator.java b/src/test/java/org/orekit/estimation/measurements/TurnAroundRangeMeasurementCreator.java index 6e0aca378d..6e7660ce5e 100644 --- a/src/test/java/org/orekit/estimation/measurements/TurnAroundRangeMeasurementCreator.java +++ b/src/test/java/org/orekit/estimation/measurements/TurnAroundRangeMeasurementCreator.java @@ -108,7 +108,7 @@ public void handleStep(final SpacecraftState currentState) { final GroundStation secondaryStation = entry.getValue(); final AbsoluteDate date = currentState.getDate(); final Frame inertial = currentState.getFrame(); - final Vector3D position = currentState.toTransform().toStaticTransform().getInverse().transformPosition(antennaPhaseCenter); + final Vector3D position = currentState.toStaticTransform().getInverse().transformPosition(antennaPhaseCenter); // Create a TAR measurement only if elevation for both stations is higher than elevationMin° if ((primaryStation.getBaseFrame().getTrackingCoordinates(position, inertial, date).getElevation() > FastMath.toRadians(30.0))&& @@ -130,7 +130,7 @@ public void handleStep(final SpacecraftState currentState) { final double secondaryTauD = solver.solve(1000, new UnivariateFunction() { public double value(final double x) { final SpacecraftState transitState = currentState.shiftedBy(-x); - final double d = Vector3D.distance(transitState.toTransform().toStaticTransform().getInverse().transformPosition(antennaPhaseCenter), + final double d = Vector3D.distance(transitState.toStaticTransform().getInverse().transformPosition(antennaPhaseCenter), secondaryStationPosition); return d - x * Constants.SPEED_OF_LIGHT; } @@ -142,7 +142,7 @@ public double value(final double x) { final double secondaryTauU = solver.solve(1000, new UnivariateFunction() { public double value(final double x) { final SpacecraftState transitState = currentState.shiftedBy(+x); - final double d = Vector3D.distance(transitState.toTransform().toStaticTransform().getInverse().transformPosition(antennaPhaseCenter), + final double d = Vector3D.distance(transitState.toStaticTransform().getInverse().transformPosition(antennaPhaseCenter), secondaryStationPosition); return d - x * Constants.SPEED_OF_LIGHT; } @@ -154,11 +154,11 @@ public double value(final double x) { // Transit state position & date for the 1st leg of the signal path final SpacecraftState S1 = currentState.shiftedBy(-secondaryTauD); - final Vector3D P1 = S1.toTransform().toStaticTransform().getInverse().transformPosition(antennaPhaseCenter); + final Vector3D P1 = S1.toStaticTransform().getInverse().transformPosition(antennaPhaseCenter); final AbsoluteDate T1 = date.shiftedBy(-secondaryTauD); // Transit state position & date for the 2nd leg of the signal path - final Vector3D P2 = currentState.shiftedBy(+secondaryTauU).toTransform().toStaticTransform().getInverse().transformPosition(antennaPhaseCenter); + final Vector3D P2 = currentState.shiftedBy(+secondaryTauU).toStaticTransform().getInverse().transformPosition(antennaPhaseCenter); final AbsoluteDate T2 = date.shiftedBy(+secondaryTauU); @@ -182,7 +182,7 @@ public double value(final double x) { // Primary station uplink delay - from primary station to P1 // Here the state date is known. Thus we can use the function "signalTimeOfFlight" // of the AbstractMeasurement class - final double primaryTauU = AbstractMeasurement.signalTimeOfFlight(primaryStationAtReception, P1, T1); + final double primaryTauU = AbstractMeasurement.signalTimeOfFlight(primaryStationAtReception, P1, T1, inertial); final AbsoluteDate primaryEmissionDate = T1.shiftedBy(-primaryTauU); diff --git a/src/test/java/org/orekit/estimation/measurements/TurnAroundRangeTest.java b/src/test/java/org/orekit/estimation/measurements/TurnAroundRangeTest.java index 4e76b105f4..fce108676d 100644 --- a/src/test/java/org/orekit/estimation/measurements/TurnAroundRangeTest.java +++ b/src/test/java/org/orekit/estimation/measurements/TurnAroundRangeTest.java @@ -154,12 +154,12 @@ public void testParameterDerivativesWithModifier() { } // Run test boolean isModifier = true; - double refErrorQMMedian = 2.6e-6; - double refErrorQMMean = 2.5e-6; - double refErrorQMMax = 5.6e-6; - double refErrorQSMedian = 3.7e-7; - double refErrorQSMean = 3.6e-7; - double refErrorQSMax = 7.7e-7; + double refErrorQMMedian = 1.3e-8; + double refErrorQMMean = 8.9e-8; + double refErrorQMMax = 5.1e-6; + double refErrorQSMedian = 3.4e-8; + double refErrorQSMean = 1.9e-5; + double refErrorQSMax = 2.2e-4; this.genericTestParameterDerivatives(isModifier, printResults, refErrorQMMedian, refErrorQMMean, refErrorQMMax, refErrorQSMedian, refErrorQSMean, refErrorQSMax); @@ -209,7 +209,7 @@ void genericTestValues(final boolean printResults) { // Values of the TAR & errors final double TARobserved = measurement.getObservedValue()[0]; - final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }); + final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(new SpacecraftState[] { state }); final double TARestimated = estimated.getEstimatedValue()[0]; final TimeStampedPVCoordinates[] participants = estimated.getParticipants(); @@ -333,7 +333,7 @@ void genericTestStateDerivatives(final boolean isModifier, final boolean printRe jacobianRef = Differentiation.differentiate(new StateFunction() { public double[] value(final SpacecraftState state) { return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). + estimateWithoutDerivatives(new SpacecraftState[] { state }). getEstimatedValue(); } }, measurement.getDimension(), propagator.getAttitudeProvider(), @@ -505,7 +505,7 @@ void genericTestParameterDerivatives(final boolean isModifier, final boolean pri @Override public double value(final ParameterDriver parameterDriver, AbsoluteDate date) { return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). + estimateWithoutDerivatives(new SpacecraftState[] { state }). getEstimatedValue()[0]; } }, 3, 20.0 * drivers[i].getScale()); diff --git a/src/test/java/org/orekit/estimation/measurements/TwoWayRangeMeasurementCreator.java b/src/test/java/org/orekit/estimation/measurements/TwoWayRangeMeasurementCreator.java index 4676f59c03..94387ad082 100644 --- a/src/test/java/org/orekit/estimation/measurements/TwoWayRangeMeasurementCreator.java +++ b/src/test/java/org/orekit/estimation/measurements/TwoWayRangeMeasurementCreator.java @@ -87,7 +87,7 @@ public void handleStep(final SpacecraftState currentState) { for (final GroundStation station : provider.getStations()) { final AbsoluteDate date = currentState.getDate(); final Frame inertial = currentState.getFrame(); - final Vector3D position = currentState.toTransform().toStaticTransform().getInverse().transformPosition(satelliteMeanPosition); + final Vector3D position = currentState.toStaticTransform().getInverse().transformPosition(satelliteMeanPosition); if (station.getBaseFrame().getTrackingCoordinates(position, inertial, date).getElevation() > FastMath.toRadians(30.0)) { final UnivariateSolver solver = new BracketingNthOrderBrentSolver(1.0e-12, 5); diff --git a/src/test/java/org/orekit/estimation/measurements/filtering/ElevationFilteringTest.java b/src/test/java/org/orekit/estimation/measurements/filtering/ElevationFilteringTest.java index 036a4c875e..fbd285de23 100644 --- a/src/test/java/org/orekit/estimation/measurements/filtering/ElevationFilteringTest.java +++ b/src/test/java/org/orekit/estimation/measurements/filtering/ElevationFilteringTest.java @@ -46,6 +46,7 @@ import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; import org.orekit.frames.TopocentricFrame; +import org.orekit.models.earth.troposphere.TroposphericModelUtils; import org.orekit.orbits.CartesianOrbit; import org.orekit.orbits.Orbit; import org.orekit.orbits.OrbitType; @@ -133,7 +134,8 @@ public void testElevationFilter() { final OneAxisEllipsoid body = new OneAxisEllipsoid(equatorialRadius, flattening, bodyFrame); final GeodeticPoint position = new GeodeticPoint(29.868112727, -89.673232869, -14.853146); final TopocentricFrame topo = new TopocentricFrame(body, position, "SBCH"); - final GroundStation station = new GroundStation(topo); + final GroundStation station = new GroundStation(topo, + TroposphericModelUtils.STANDARD_ATMOSPHERE_PROVIDER); final double noise = 0.1; Generator generatorThreshold = getGenerator(orbit, station, topo, noise, threshold); final GatheringSubscriber gathererThreshold = new GatheringSubscriber(); diff --git a/src/test/java/org/orekit/estimation/measurements/filtering/PseudoRangeFilteringTest.java b/src/test/java/org/orekit/estimation/measurements/filtering/PseudoRangeFilteringTest.java index 5c32026df4..216e584b80 100644 --- a/src/test/java/org/orekit/estimation/measurements/filtering/PseudoRangeFilteringTest.java +++ b/src/test/java/org/orekit/estimation/measurements/filtering/PseudoRangeFilteringTest.java @@ -1,3 +1,20 @@ +/* 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.estimation.measurements.filtering; import java.io.BufferedReader; @@ -59,19 +76,19 @@ public void testHatchDoppler() throws IOException { // Test reset and null condition on doppler ObservationData obsDataRange = new ObservationData(rangeType, 10, 0, 7); ObservationData obsDataDopplerNull = new ObservationData(dopplerType, Double.NaN, 0, 7); - List listObsData = new ArrayList(); + List listObsData = new ArrayList<>(); listObsData.add(obsDataDopplerNull); listObsData.add(obsDataRange); ObservationDataSet obsDataSetNullDoppler = new ObservationDataSet(new SatInSystem(system, prnNumber), - lastObsDataSet.getDate(), 0, prnNumber, listObsData); + lastObsDataSet.getDate(), 0, 0.0, listObsData); ObservationData obsDataRangeNull = new ObservationData(rangeType, 10, 0, 0); ObservationData obsDataDoppler = new ObservationData(dopplerType, Double.NaN, 0, 0); - List listObsData2 = new ArrayList(); + List listObsData2 = new ArrayList<>(); listObsData2.add(obsDataDoppler); listObsData2.add(obsDataRangeNull); ObservationDataSet obsDataSetNullRange= new ObservationDataSet(new SatInSystem(system, prnNumber), - lastObsDataSet.getDate(), 0, prnNumber, listObsData2); + lastObsDataSet.getDate(), 0, 0.0, listObsData2); List copiedListObsDataSet = new ArrayList<>(listObsDataSet); copiedListObsDataSet.add(obsDataSetNullRange); @@ -133,19 +150,19 @@ public void testHatchCarrierPhaseValues() throws IOException { ObservationData obsDataF1 = new ObservationData(phaseTypeF1, 0, 0, 0); ObservationData obsDataF2 = new ObservationData(phaseTypeF2, 0, 0, 0); - List listObsDataSatSystem = new ArrayList(); + List listObsDataSatSystem = new ArrayList<>(); listObsDataSatSystem.add(obsDataF1); listObsDataSatSystem.add(obsDataF2); listObsDataSatSystem.add(obsDataRange); - List listObsDataSNR = new ArrayList(); + List listObsDataSNR = new ArrayList<>(); listObsDataSNR.add(obsDataF1); listObsDataSNR.add(obsDataF2); listObsDataSNR.add(obsDataRangeSNR); ObservationDataSet obsDataSetRangeGLONASS = new ObservationDataSet(new SatInSystem(SatelliteSystem.GLONASS, prnNumber), - lastObsDataSet.getDate(), 0, prnNumber, listObsDataSatSystem); + lastObsDataSet.getDate(), 0, 0.0, listObsDataSatSystem); ObservationDataSet obsDataSetRangeSNR = new ObservationDataSet(new SatInSystem(system, prnNumber), - lastObsDataSet.getDate(), 0, prnNumber, listObsDataSNR); + lastObsDataSet.getDate(), 0, 0.0, listObsDataSNR); // List copiedListObsDataSet = new ArrayList<>(listObsDataSet); copiedListObsDataSet.add(obsDataSetRangeGLONASS); @@ -168,15 +185,11 @@ public void testHatchCarrierPhaseValues() throws IOException { ArrayList phaseArrayF1 = filter.getFirstFrequencyPhaseHistory(); ArrayList phaseArrayF2 = filter.getSecondFrequencyPhaseHistory(); - ArrayList differencesSF = new ArrayList(); - ArrayList differencesDF = new ArrayList(); DescriptiveStatistics dSF = new DescriptiveStatistics(); DescriptiveStatistics dDF = new DescriptiveStatistics(); for (int i = 0; i < 5000; i++) { double diffSF = gLAB_SF.get(i)-(filteredSF.get(i)-2.4931100000e7 - phaseArrayF1.get(i) - 3.09*(phaseArrayF1.get(i)-phaseArrayF2.get(i))); double diffDF = gLAB_DF.get(i)-(filteredDF.get(i)-2.4931100000e7 - phaseArrayF1.get(i) - 3.09*(phaseArrayF1.get(i)-phaseArrayF2.get(i))); - differencesSF.add(diffSF); - differencesDF.add(diffDF); dSF.addValue(diffSF); dDF.addValue(diffDF); } @@ -192,7 +205,7 @@ public void testHatchCarrierPhaseValues() throws IOException { } @Test - public void testHatchCarrierPhase2() throws IOException { + public void testHatchCarrierPhase2() { ObservationType rangeType = ObservationType.C1; ObservationType phaseTypeF1 = ObservationType.L1; ObservationType phaseTypeF2 = ObservationType.L2; @@ -255,8 +268,7 @@ public void testHatchCarrierPhase2() throws IOException { public ArrayList readFile(final String fileName) throws IOException { - final ArrayList valueArray = new ArrayList(); - final ArrayList timeArray = new ArrayList(); + final ArrayList valueArray = new ArrayList<>(); int cpt = 0; Path pathToFile = Paths.get(fileName); @@ -264,7 +276,6 @@ public ArrayList readFile(final String fileName) throws IOException { String line = br.readLine(); while (line != null) { String[] splitLine = line.split("\\s+"); - timeArray.add(Double.parseDouble(splitLine[0])); valueArray.add(Double.parseDouble(splitLine[1])); cpt = cpt+1; line = br.readLine(); @@ -273,4 +284,4 @@ public ArrayList readFile(final String fileName) throws IOException { return valueArray; } -} \ No newline at end of file +} diff --git a/src/test/java/org/orekit/estimation/measurements/filtering/ResidualsFilteringTest.java b/src/test/java/org/orekit/estimation/measurements/filtering/ResidualsFilteringTest.java index 578556e5a0..fd277a8961 100644 --- a/src/test/java/org/orekit/estimation/measurements/filtering/ResidualsFilteringTest.java +++ b/src/test/java/org/orekit/estimation/measurements/filtering/ResidualsFilteringTest.java @@ -31,6 +31,7 @@ import org.orekit.Utils; import org.orekit.bodies.GeodeticPoint; import org.orekit.bodies.OneAxisEllipsoid; +import org.orekit.estimation.EstimationTestUtils; import org.orekit.estimation.measurements.GroundStation; import org.orekit.estimation.measurements.ObservableSatellite; import org.orekit.estimation.measurements.ObservedMeasurement; @@ -91,20 +92,13 @@ private MeasurementBuilder getBuilder(final RandomGenerator random, return rb; } - private ElevationDetector getElvetaionDetector(final TopocentricFrame topo, final double minElevation) { - ElevationDetector detector = - new ElevationDetector(topo). - withConstantElevation(FastMath.toRadians(5.0)); - return detector; - } - private Generator getGenerator(final Orbit orbit, final GroundStation station, final ObservableSatellite satellite, final TopocentricFrame topo, final double noise) { Generator generator = new Generator(); Propagator propagator = buildPropagator(orbit); generator.addPropagator(propagator); RandomGenerator random = new Well19937a(0x01e226dd859c2c9dl); MeasurementBuilder builder = getBuilder(random, station, satellite, noise); - EventDetector event = getElvetaionDetector(topo, 10.0); + EventDetector event = EstimationTestUtils.getElevationDetector(topo, FastMath.toRadians(5.0)); FixedStepSelector dateSelecor = new FixedStepSelector(30, TimeScalesFactory.getUTC()); EventBasedScheduler scheduler = new EventBasedScheduler(builder, dateSelecor, propagator, event, SignSemantic.FEASIBLE_MEASUREMENT_WHEN_POSITIVE); generator.addScheduler(scheduler); diff --git a/src/test/java/org/orekit/estimation/measurements/generation/AbstractGroundMeasurementBuilderTest.java b/src/test/java/org/orekit/estimation/measurements/generation/AbstractGroundMeasurementBuilderTest.java index 31ced27c40..446f96157e 100644 --- a/src/test/java/org/orekit/estimation/measurements/generation/AbstractGroundMeasurementBuilderTest.java +++ b/src/test/java/org/orekit/estimation/measurements/generation/AbstractGroundMeasurementBuilderTest.java @@ -60,8 +60,8 @@ protected void doTest(long seed, double startPeriod, double endPeriod, int expec generator.addScheduler(new EventBasedScheduler<>(getBuilder(new Well19937a(seed), context.stations.get(0), satellite), new FixedStepSelector(step, TimeScalesFactory.getUTC()), generator.getPropagator(satellite), - new ElevationDetector(context.stations.get(0).getBaseFrame()). - withConstantElevation(FastMath.toRadians(5.0)). + EstimationTestUtils.getElevationDetector(context.stations.get(0).getBaseFrame(), + FastMath.toRadians(5.0)). withHandler(new ContinueOnEvent()), SignSemantic.FEASIBLE_MEASUREMENT_WHEN_POSITIVE)); final GatheringSubscriber gatherer = new GatheringSubscriber(); @@ -93,7 +93,7 @@ protected void doTest(long seed, double startPeriod, double endPeriod, int expec } previous = date; SpacecraftState state = propagator.propagate(date); - double[] e = measurement.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }).getEstimatedValue(); + double[] e = measurement.estimateWithoutDerivatives(new SpacecraftState[] { state }).getEstimatedValue(); for (int i = 0; i < m.length; ++i) { maxError = FastMath.max(maxError, FastMath.abs(e[i] - m[i])); } diff --git a/src/test/java/org/orekit/estimation/measurements/generation/BistaticRangeBuilderTest.java b/src/test/java/org/orekit/estimation/measurements/generation/BistaticRangeBuilderTest.java index 9fd4285dfd..c97bd9a751 100644 --- a/src/test/java/org/orekit/estimation/measurements/generation/BistaticRangeBuilderTest.java +++ b/src/test/java/org/orekit/estimation/measurements/generation/BistaticRangeBuilderTest.java @@ -96,10 +96,10 @@ private void doTest(long seed, double startPeriod, double endPeriod, double tole generator.addScheduler(new EventBasedScheduler<>(getBuilder(new Well19937a(seed), emitter, receiver, satellite), new FixedStepSelector(step, TimeScalesFactory.getUTC()), generator.getPropagator(satellite), - BooleanDetector.andCombine(new ElevationDetector(emitter.getBaseFrame()). - withConstantElevation(FastMath.toRadians(5.0)), - new ElevationDetector(receiver.getBaseFrame()). - withConstantElevation(FastMath.toRadians(5.0))), + BooleanDetector.andCombine(EstimationTestUtils.getElevationDetector(emitter.getBaseFrame(), + FastMath.toRadians(5.0)), + EstimationTestUtils.getElevationDetector(receiver.getBaseFrame(), + FastMath.toRadians(5.0))), SignSemantic.FEASIBLE_MEASUREMENT_WHEN_POSITIVE)); final GatheringSubscriber gatherer = new GatheringSubscriber(); generator.addSubscriber(gatherer); @@ -129,7 +129,7 @@ private void doTest(long seed, double startPeriod, double endPeriod, double tole } previous = date; SpacecraftState state = propagator.propagate(date); - double[] e = measurement.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }).getEstimatedValue(); + double[] e = measurement.estimateWithoutDerivatives(new SpacecraftState[] { state }).getEstimatedValue(); for (int i = 0; i < m.length; ++i) { maxError = FastMath.max(maxError, FastMath.abs(e[i] - m[i])); } diff --git a/src/test/java/org/orekit/estimation/measurements/generation/BistaticRangeRateBuilderTest.java b/src/test/java/org/orekit/estimation/measurements/generation/BistaticRangeRateBuilderTest.java index 3f26de7584..27b2dd1e5e 100644 --- a/src/test/java/org/orekit/estimation/measurements/generation/BistaticRangeRateBuilderTest.java +++ b/src/test/java/org/orekit/estimation/measurements/generation/BistaticRangeRateBuilderTest.java @@ -96,10 +96,10 @@ private void doTest(long seed, double startPeriod, double endPeriod, double tole generator.addScheduler(new EventBasedScheduler<>(getBuilder(new Well19937a(seed), emitter, receiver, satellite), new FixedStepSelector(step, TimeScalesFactory.getUTC()), generator.getPropagator(satellite), - BooleanDetector.andCombine(new ElevationDetector(emitter.getBaseFrame()). - withConstantElevation(FastMath.toRadians(5.0)), - new ElevationDetector(receiver.getBaseFrame()). - withConstantElevation(FastMath.toRadians(5.0))), + BooleanDetector.andCombine(EstimationTestUtils.getElevationDetector(emitter.getBaseFrame(), + FastMath.toRadians(5.0)), + EstimationTestUtils.getElevationDetector(receiver.getBaseFrame(), + FastMath.toRadians(5.0))), SignSemantic.FEASIBLE_MEASUREMENT_WHEN_POSITIVE)); final GatheringSubscriber gatherer = new GatheringSubscriber(); generator.addSubscriber(gatherer); @@ -129,7 +129,7 @@ private void doTest(long seed, double startPeriod, double endPeriod, double tole } previous = date; SpacecraftState state = propagator.propagate(date); - double[] e = measurement.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }).getEstimatedValue(); + double[] e = measurement.estimateWithoutDerivatives(new SpacecraftState[] { state }).getEstimatedValue(); for (int i = 0; i < m.length; ++i) { maxError = FastMath.max(maxError, FastMath.abs(e[i] - m[i])); } diff --git a/src/test/java/org/orekit/estimation/measurements/generation/FDOABuilderTest.java b/src/test/java/org/orekit/estimation/measurements/generation/FDOABuilderTest.java index 958be4cb41..d0cda18441 100644 --- a/src/test/java/org/orekit/estimation/measurements/generation/FDOABuilderTest.java +++ b/src/test/java/org/orekit/estimation/measurements/generation/FDOABuilderTest.java @@ -99,10 +99,10 @@ private void doTest(long seed, double startPeriod, double endPeriod, double tole generator.addScheduler(new EventBasedScheduler<>(getBuilder(new Well19937a(seed), emitter, receiver, satellite), new FixedStepSelector(step, TimeScalesFactory.getUTC()), generator.getPropagator(satellite), - BooleanDetector.andCombine(new ElevationDetector(emitter.getBaseFrame()). - withConstantElevation(FastMath.toRadians(5.0)), - new ElevationDetector(receiver.getBaseFrame()). - withConstantElevation(FastMath.toRadians(5.0))), + BooleanDetector.andCombine(EstimationTestUtils.getElevationDetector(emitter.getBaseFrame(), + FastMath.toRadians(5.0)), + EstimationTestUtils.getElevationDetector(receiver.getBaseFrame(), + FastMath.toRadians(5.0))), SignSemantic.FEASIBLE_MEASUREMENT_WHEN_POSITIVE)); final GatheringSubscriber gatherer = new GatheringSubscriber(); generator.addSubscriber(gatherer); @@ -132,7 +132,7 @@ private void doTest(long seed, double startPeriod, double endPeriod, double tole } previous = date; SpacecraftState state = propagator.propagate(date); - double[] e = measurement.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }).getEstimatedValue(); + double[] e = measurement.estimateWithoutDerivatives(new SpacecraftState[] { state }).getEstimatedValue(); for (int i = 0; i < m.length; ++i) { maxError = FastMath.max(maxError, FastMath.abs(e[i] - m[i])); } diff --git a/src/test/java/org/orekit/estimation/measurements/generation/GeneratorTest.java b/src/test/java/org/orekit/estimation/measurements/generation/GeneratorTest.java index 8d8607f040..46187805dd 100644 --- a/src/test/java/org/orekit/estimation/measurements/generation/GeneratorTest.java +++ b/src/test/java/org/orekit/estimation/measurements/generation/GeneratorTest.java @@ -45,7 +45,8 @@ public class GeneratorTest { @Test public void testIssue557() { - final EventDetector detector = new ElevationDetector(context.stations.get(0).getBaseFrame()); + final EventDetector detector = EstimationTestUtils.getElevationDetector(context.stations.get(0).getBaseFrame(), + FastMath.toRadians(0.0)); double[] azElError = new double[] { FastMath.toRadians(0.015), diff --git a/src/test/java/org/orekit/estimation/measurements/generation/InterSatellitesOneWayRangeRateBuilderTest.java b/src/test/java/org/orekit/estimation/measurements/generation/InterSatellitesOneWayRangeRateBuilderTest.java new file mode 100644 index 0000000000..1c4283d9a5 --- /dev/null +++ b/src/test/java/org/orekit/estimation/measurements/generation/InterSatellitesOneWayRangeRateBuilderTest.java @@ -0,0 +1,167 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.estimation.measurements.generation; + +import org.hipparchus.linear.MatrixUtils; +import org.hipparchus.linear.RealMatrix; +import org.hipparchus.random.CorrelatedRandomVectorGenerator; +import org.hipparchus.random.GaussianRandomGenerator; +import org.hipparchus.random.RandomGenerator; +import org.hipparchus.random.Well19937a; +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.estimation.Context; +import org.orekit.estimation.EstimationTestUtils; +import org.orekit.estimation.Force; +import org.orekit.estimation.measurements.ObservableSatellite; +import org.orekit.estimation.measurements.ObservedMeasurement; +import org.orekit.estimation.measurements.gnss.InterSatellitesOneWayRangeRate; +import org.orekit.estimation.measurements.modifiers.Bias; +import org.orekit.orbits.KeplerianOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; +import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.Propagator; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.analytical.KeplerianPropagator; +import org.orekit.propagation.conversion.NumericalPropagatorBuilder; +import org.orekit.propagation.events.InterSatDirectViewDetector; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FixedStepSelector; +import org.orekit.time.TimeScalesFactory; +import org.orekit.utils.PVCoordinates; + +import java.util.SortedSet; + +public class InterSatellitesOneWayRangeRateBuilderTest { + + private static final double SIGMA = 0.5; + private static final double BIAS = -0.01; + + private MeasurementBuilder getBuilder(final RandomGenerator random, + final ObservableSatellite receiver, + final ObservableSatellite remote) { + final RealMatrix covariance = MatrixUtils.createRealDiagonalMatrix(new double[] { SIGMA * SIGMA }); + MeasurementBuilder isrb = + new InterSatellitesOneWayRangeRateBuilder(random == null ? null : new CorrelatedRandomVectorGenerator(covariance, + 1.0e-10, + new GaussianRandomGenerator(random)), + receiver, remote, SIGMA, 1.0); + isrb.addModifier(new Bias<>(new String[] { "bias" }, + new double[] { BIAS }, + new double[] { 1.0 }, + new double[] { Double.NEGATIVE_INFINITY }, + new double[] { Double.POSITIVE_INFINITY })); + return isrb; + } + + @Test + public void testForward() { + doTest(0x5006ca3f1e03ea93L, 0.0, 1.2, 2.4 * SIGMA); + } + + @Test + public void testBackward() { + doTest(0xd7643ffbaff67906L, 0.0, -1.0, 3.0 * SIGMA); + } + + private Propagator buildPropagator() { + return EstimationTestUtils.createPropagator(context.initialOrbit, propagatorBuilder); + } + + private void doTest(long seed, double startPeriod, double endPeriod, double tolerance) { + Generator generator = new Generator(); + generator.addPropagator(buildPropagator()); // dummy first propagator + generator.addPropagator(buildPropagator()); // dummy second propagator + ObservableSatellite receiver = generator.addPropagator(buildPropagator()); // useful third propagator + generator.addPropagator(buildPropagator()); // dummy fourth propagator + final Orbit o1 = context.initialOrbit; + // for the second satellite, we simply reverse velocity + final Orbit o2 = new KeplerianOrbit(new PVCoordinates(o1.getPosition(), + o1.getPVCoordinates().getVelocity().negate()), + o1.getFrame(), o1.getDate(), o1.getMu()); + ObservableSatellite remote = generator.addPropagator(new KeplerianPropagator(o2)); // useful sixth propagator + final double step = 60.0; + + // beware that in order to avoid deadlocks, the secondary PV coordinates provider + // in InterSatDirectViewDetector must be *different* from the second propagator + // added to generator above! The reason is the event detector will be bound + // to the first propagator, so it cannot also refer to the second one at the same time + // this is the reason why we create a *new* KeplerianPropagator below + generator.addScheduler(new EventBasedScheduler<>(getBuilder(new Well19937a(seed), receiver, remote), + new FixedStepSelector(step, TimeScalesFactory.getUTC()), + generator.getPropagator(receiver), + new InterSatDirectViewDetector(context.earth, new KeplerianPropagator(o2)), + SignSemantic.FEASIBLE_MEASUREMENT_WHEN_POSITIVE)); + + final GatheringSubscriber gatherer = new GatheringSubscriber(); + generator.addSubscriber(gatherer); + final double period = o1.getKeplerianPeriod(); + AbsoluteDate t0 = o1.getDate().shiftedBy(startPeriod * period); + AbsoluteDate t1 = o1.getDate().shiftedBy(endPeriod * period); + generator.generate(t0, t1); + SortedSet> measurements = gatherer.getGeneratedMeasurements(); + + // and yet another set of propagators for reference + Propagator propagator1 = buildPropagator(); + Propagator propagator2 = new KeplerianPropagator(o2); + + double maxError = 0; + AbsoluteDate previous = null; + AbsoluteDate tInf = t0.isBefore(t1) ? t0 : t1; + AbsoluteDate tSup = t0.isBefore(t1) ? t1 : t0; + for (ObservedMeasurement measurement : measurements) { + AbsoluteDate date = measurement.getDate(); + double[] m = measurement.getObservedValue(); + Assertions.assertTrue(date.compareTo(tInf) >= 0); + Assertions.assertTrue(date.compareTo(tSup) <= 0); + if (previous != null) { + if (t0.isBefore(t1)) { + // measurements are expected to be chronological + Assertions.assertTrue(date.durationFrom(previous) >= 0.999999 * step); + } else { + // measurements are expected to be reverse chronological + Assertions.assertTrue(previous.durationFrom(date) >= 0.999999 * step); + } + } + previous = date; + double[] e = measurement.estimateWithoutDerivatives(new SpacecraftState[] { + propagator1.propagate(date), + propagator2.propagate(date) + }).getEstimatedValue(); + for (int i = 0; i < m.length; ++i) { + maxError = FastMath.max(maxError, FastMath.abs(e[i] - m[i])); + } + } + Assertions.assertEquals(0.0, maxError, tolerance); + } + + @BeforeEach + public void setUp() { + context = EstimationTestUtils.eccentricContext("regular-data:potential:tides"); + + propagatorBuilder = context.createBuilder(OrbitType.KEPLERIAN, PositionAngleType.TRUE, true, + 1.0e-6, 300.0, 0.001, Force.POTENTIAL, + Force.THIRD_BODY_SUN, Force.THIRD_BODY_MOON); + } + + Context context; + NumericalPropagatorBuilder propagatorBuilder; + +} diff --git a/src/test/java/org/orekit/estimation/measurements/generation/InterSatellitesPhaseBuilderTest.java b/src/test/java/org/orekit/estimation/measurements/generation/InterSatellitesPhaseBuilderTest.java index e287045dbb..d2e7f2df62 100644 --- a/src/test/java/org/orekit/estimation/measurements/generation/InterSatellitesPhaseBuilderTest.java +++ b/src/test/java/org/orekit/estimation/measurements/generation/InterSatellitesPhaseBuilderTest.java @@ -31,6 +31,7 @@ import org.orekit.estimation.Force; import org.orekit.estimation.measurements.ObservableSatellite; import org.orekit.estimation.measurements.ObservedMeasurement; +import org.orekit.estimation.measurements.gnss.AmbiguityCache; import org.orekit.estimation.measurements.gnss.InterSatellitesPhase; import org.orekit.estimation.measurements.modifiers.Bias; import org.orekit.gnss.Frequency; @@ -64,7 +65,8 @@ private MeasurementBuilder getBuilder(final RandomGenerato new InterSatellitesPhaseBuilder(random == null ? null : new CorrelatedRandomVectorGenerator(covariance, 1.0e-10, new GaussianRandomGenerator(random)), - receiver, remote, WAVELENGTH, SIGMA, 1.0); + receiver, remote, WAVELENGTH, SIGMA, 1.0, + new AmbiguityCache()); isrb.addModifier(new Bias<>(new String[] { "bias" }, new double[] { BIAS }, new double[] { 1.0 }, @@ -75,12 +77,12 @@ private MeasurementBuilder getBuilder(final RandomGenerato @Test public void testForward() { - doTest(0xc82a56322345dc25l, 0.0, 1.2, 2.8 * SIGMA); + doTest(0xc82a56322345dc25L, 0.0, 1.2, 2.8 * SIGMA); } @Test public void testBackward() { - doTest(0x95c10149c4891232l, 0.0, -1.0, 2.6 * SIGMA); + doTest(0x95c10149c4891232L, 0.0, -1.0, 2.6 * SIGMA); } private Propagator buildPropagator() { @@ -143,8 +145,7 @@ private void doTest(long seed, double startPeriod, double endPeriod, double tole } } previous = date; - double[] e = measurement.estimateWithoutDerivatives(0, 0, - new SpacecraftState[] { + double[] e = measurement.estimateWithoutDerivatives(new SpacecraftState[] { propagator1.propagate(date), propagator2.propagate(date) }).getEstimatedValue(); diff --git a/src/test/java/org/orekit/estimation/measurements/generation/InterSatellitesRangeBuilderTest.java b/src/test/java/org/orekit/estimation/measurements/generation/InterSatellitesRangeBuilderTest.java index 167549fb39..a70d9e8657 100644 --- a/src/test/java/org/orekit/estimation/measurements/generation/InterSatellitesRangeBuilderTest.java +++ b/src/test/java/org/orekit/estimation/measurements/generation/InterSatellitesRangeBuilderTest.java @@ -141,8 +141,7 @@ private void doTest(long seed, double startPeriod, double endPeriod, double tole } } previous = date; - double[] e = measurement.estimateWithoutDerivatives(0, 0, - new SpacecraftState[] { + double[] e = measurement.estimateWithoutDerivatives(new SpacecraftState[] { propagator1.propagate(date), propagator2.propagate(date) }).getEstimatedValue(); diff --git a/src/test/java/org/orekit/estimation/measurements/generation/OneWayGNSSPhaseBuilderTest.java b/src/test/java/org/orekit/estimation/measurements/generation/OneWayGNSSPhaseBuilderTest.java index 34f3e7fa92..882c365159 100644 --- a/src/test/java/org/orekit/estimation/measurements/generation/OneWayGNSSPhaseBuilderTest.java +++ b/src/test/java/org/orekit/estimation/measurements/generation/OneWayGNSSPhaseBuilderTest.java @@ -16,7 +16,9 @@ */ package org.orekit.estimation.measurements.generation; +import java.lang.reflect.Field; import java.util.SortedSet; +import java.util.function.Function; import org.hipparchus.linear.MatrixUtils; import org.hipparchus.linear.RealMatrix; @@ -33,6 +35,8 @@ import org.orekit.estimation.Force; import org.orekit.estimation.measurements.ObservableSatellite; import org.orekit.estimation.measurements.ObservedMeasurement; +import org.orekit.estimation.measurements.QuadraticClockModel; +import org.orekit.estimation.measurements.gnss.AmbiguityCache; import org.orekit.estimation.measurements.gnss.OneWayGNSSPhase; import org.orekit.estimation.measurements.modifiers.Bias; import org.orekit.gnss.Frequency; @@ -65,8 +69,10 @@ private MeasurementBuilder getBuilder(final RandomGenerator ran 1.0e-10, new GaussianRandomGenerator(random)), receiver, remote, - date -> 1.0e-16, - WAVELENGTH, SIGMA, 1.0); + new QuadraticClockModel(AbsoluteDate.ARBITRARY_EPOCH, + 1.0e-16, 0, 0), + WAVELENGTH, SIGMA, 1.0, + new AmbiguityCache()); b.addModifier(new Bias<>(new String[] { "bias" }, new double[] { BIAS }, new double[] { 1.0 }, @@ -75,14 +81,43 @@ private MeasurementBuilder getBuilder(final RandomGenerator ran return b; } + @Deprecated + @Test + public void testDeprecated() { + final QuadraticClockModel refQuadratic = new QuadraticClockModel(AbsoluteDate.GALILEO_EPOCH, + 1.0e-16, 2.0e-17, -7.0e-18); + MeasurementBuilder b = + new OneWayGNSSPhaseBuilder(null, + new ObservableSatellite(0), + new ObservableSatellite(1), + date -> refQuadratic.getOffset(date).getOffset(), + WAVELENGTH, SIGMA, 1.0); + try { + Field clockBuilderField = OneWayGNSSPhaseBuilder.class.getDeclaredField("clockBuilder"); + clockBuilderField.setAccessible(true); + Function clockBuilder = + (Function) clockBuilderField.get(b); + QuadraticClockModel rebuilt = clockBuilder.apply(AbsoluteDate.GALILEO_EPOCH); + for (double dt = -3; dt < 3; dt += 0.01) { + final AbsoluteDate date = AbsoluteDate.GALILEO_EPOCH.shiftedBy(dt); + Assertions.assertEquals(refQuadratic.getOffset(date).getOffset(), + rebuilt.getOffset(date).getOffset(), + 1.0e-30); + } + + } catch (NoSuchFieldException | IllegalAccessException e) { + Assertions.fail(e.getLocalizedMessage()); + } + } + @Test public void testForward() { - doTest(0x812bfe784826bab3l, 0.0, 1.2, 3.4 * SIGMA); + doTest(0x812bfe784826bab3L, 0.0, 1.2, 3.4 * SIGMA); } @Test public void testBackward() { - doTest(0xb54896c361d88441l, 0.0, -1.0, 2.8 * SIGMA); + doTest(0xb54896c361d88441L, 0.0, -1.0, 2.8 * SIGMA); } private Propagator buildPropagator() { @@ -145,8 +180,7 @@ private void doTest(long seed, double startPeriod, double endPeriod, double tole } } previous = date; - double[] e = measurement.estimateWithoutDerivatives(0, 0, - new SpacecraftState[] { + double[] e = measurement.estimateWithoutDerivatives(new SpacecraftState[] { propagator1.propagate(date), propagator2.propagate(date) }).getEstimatedValue(); diff --git a/src/test/java/org/orekit/estimation/measurements/generation/OneWayGNSSRangeBuilderTest.java b/src/test/java/org/orekit/estimation/measurements/generation/OneWayGNSSRangeBuilderTest.java index 93c5899576..27b3035a64 100644 --- a/src/test/java/org/orekit/estimation/measurements/generation/OneWayGNSSRangeBuilderTest.java +++ b/src/test/java/org/orekit/estimation/measurements/generation/OneWayGNSSRangeBuilderTest.java @@ -143,8 +143,7 @@ private void doTest(long seed, double startPeriod, double endPeriod, double tole } } previous = date; - double[] e = measurement.estimateWithoutDerivatives(0, 0, - new SpacecraftState[] { + double[] e = measurement.estimateWithoutDerivatives(new SpacecraftState[] { propagator1.propagate(date), propagator2.propagate(date) }).getEstimatedValue(); diff --git a/src/test/java/org/orekit/estimation/measurements/generation/OneWayGNSSRangeRateBuilderTest.java b/src/test/java/org/orekit/estimation/measurements/generation/OneWayGNSSRangeRateBuilderTest.java new file mode 100644 index 0000000000..e32cc59834 --- /dev/null +++ b/src/test/java/org/orekit/estimation/measurements/generation/OneWayGNSSRangeRateBuilderTest.java @@ -0,0 +1,168 @@ +/* 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.estimation.measurements.generation; + +import org.hipparchus.linear.MatrixUtils; +import org.hipparchus.linear.RealMatrix; +import org.hipparchus.random.CorrelatedRandomVectorGenerator; +import org.hipparchus.random.GaussianRandomGenerator; +import org.hipparchus.random.RandomGenerator; +import org.hipparchus.random.Well19937a; +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.estimation.Context; +import org.orekit.estimation.EstimationTestUtils; +import org.orekit.estimation.Force; +import org.orekit.estimation.measurements.ObservableSatellite; +import org.orekit.estimation.measurements.ObservedMeasurement; +import org.orekit.estimation.measurements.gnss.OneWayGNSSRangeRate; +import org.orekit.estimation.measurements.modifiers.Bias; +import org.orekit.orbits.KeplerianOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; +import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.Propagator; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.analytical.KeplerianPropagator; +import org.orekit.propagation.conversion.NumericalPropagatorBuilder; +import org.orekit.propagation.events.InterSatDirectViewDetector; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FixedStepSelector; +import org.orekit.time.TimeScalesFactory; +import org.orekit.utils.PVCoordinates; + +import java.util.SortedSet; + +public class OneWayGNSSRangeRateBuilderTest { + + private static final double SIGMA = 0.5; + private static final double BIAS = -0.01; + + private MeasurementBuilder getBuilder(final RandomGenerator random, + final ObservableSatellite receiver, + final ObservableSatellite remote) { + final RealMatrix covariance = MatrixUtils.createRealDiagonalMatrix(new double[] { SIGMA * SIGMA }); + MeasurementBuilder b = + new OneWayGNSSRangeRateBuilder(random == null ? null : new CorrelatedRandomVectorGenerator(covariance, + 1.0e-10, + new GaussianRandomGenerator(random)), + receiver, remote, + SIGMA, 1.0); + b.addModifier(new Bias<>(new String[] { "bias" }, + new double[] { BIAS }, + new double[] { 1.0 }, + new double[] { Double.NEGATIVE_INFINITY }, + new double[] { Double.POSITIVE_INFINITY })); + return b; + } + + @Test + public void testForward() { + doTest(0x066acbc9bf1074a3L, 0.0, 1.2, 2.8 * SIGMA); + } + + @Test + public void testBackward() { + doTest(0x58ffc7ad03c2310bL, 0.0, -1.0, 2.5 * SIGMA); + } + + private Propagator buildPropagator() { + return EstimationTestUtils.createPropagator(context.initialOrbit, propagatorBuilder); + } + + private void doTest(long seed, double startPeriod, double endPeriod, double tolerance) { + Generator generator = new Generator(); + generator.addPropagator(buildPropagator()); // dummy first propagator + generator.addPropagator(buildPropagator()); // dummy second propagator + ObservableSatellite receiver = generator.addPropagator(buildPropagator()); // useful third propagator + generator.addPropagator(buildPropagator()); // dummy fourth propagator + final Orbit o1 = context.initialOrbit; + // for the second satellite, we simply reverse velocity + final Orbit o2 = new KeplerianOrbit(new PVCoordinates(o1.getPosition(), + o1.getPVCoordinates().getVelocity().negate()), + o1.getFrame(), o1.getDate(), o1.getMu()); + ObservableSatellite remote = generator.addPropagator(new KeplerianPropagator(o2)); // useful sixth propagator + final double step = 60.0; + + // beware that in order to avoid deadlocks, the secondary PV coordinates provider + // in InterSatDirectViewDetector must be *different* from the second propagator + // added to generator above! The reason is the event detector will be bound + // to the first propagator, so it cannot also refer to the second one at the same time + // this is the reason why we create a *new* KeplerianPropagator below + generator.addScheduler(new EventBasedScheduler<>(getBuilder(new Well19937a(seed), receiver, remote), + new FixedStepSelector(step, TimeScalesFactory.getUTC()), + generator.getPropagator(receiver), + new InterSatDirectViewDetector(context.earth, new KeplerianPropagator(o2)), + SignSemantic.FEASIBLE_MEASUREMENT_WHEN_POSITIVE)); + + final GatheringSubscriber gatherer = new GatheringSubscriber(); + generator.addSubscriber(gatherer); + final double period = o1.getKeplerianPeriod(); + AbsoluteDate t0 = o1.getDate().shiftedBy(startPeriod * period); + AbsoluteDate t1 = o1.getDate().shiftedBy(endPeriod * period); + generator.generate(t0, t1); + SortedSet> measurements = gatherer.getGeneratedMeasurements(); + + // and yet another set of propagators for reference + Propagator propagator1 = buildPropagator(); + Propagator propagator2 = new KeplerianPropagator(o2); + + double maxError = 0; + AbsoluteDate previous = null; + AbsoluteDate tInf = t0.isBefore(t1) ? t0 : t1; + AbsoluteDate tSup = t0.isBefore(t1) ? t1 : t0; + for (ObservedMeasurement measurement : measurements) { + AbsoluteDate date = measurement.getDate(); + double[] m = measurement.getObservedValue(); + Assertions.assertTrue(date.compareTo(tInf) >= 0); + Assertions.assertTrue(date.compareTo(tSup) <= 0); + if (previous != null) { + if (t0.isBefore(t1)) { + // measurements are expected to be chronological + Assertions.assertTrue(date.durationFrom(previous) >= 0.999999 * step); + } else { + // measurements are expected to be reverse chronological + Assertions.assertTrue(previous.durationFrom(date) >= 0.999999 * step); + } + } + previous = date; + double[] e = measurement.estimateWithoutDerivatives(new SpacecraftState[] { + propagator1.propagate(date), + propagator2.propagate(date) + }).getEstimatedValue(); + for (int i = 0; i < m.length; ++i) { + maxError = FastMath.max(maxError, FastMath.abs(e[i] - m[i])); + } + } + Assertions.assertEquals(0.0, maxError, tolerance); + } + + @BeforeEach + public void setUp() { + context = EstimationTestUtils.eccentricContext("regular-data:potential:tides"); + + propagatorBuilder = context.createBuilder(OrbitType.KEPLERIAN, PositionAngleType.TRUE, true, + 1.0e-6, 300.0, 0.001, Force.POTENTIAL, + Force.THIRD_BODY_SUN, Force.THIRD_BODY_MOON); + } + + Context context; + NumericalPropagatorBuilder propagatorBuilder; + +} diff --git a/src/test/java/org/orekit/estimation/measurements/generation/PVBuilderTest.java b/src/test/java/org/orekit/estimation/measurements/generation/PVBuilderTest.java index 0dbdd42b80..064c130cc5 100644 --- a/src/test/java/org/orekit/estimation/measurements/generation/PVBuilderTest.java +++ b/src/test/java/org/orekit/estimation/measurements/generation/PVBuilderTest.java @@ -135,7 +135,7 @@ private void doTest(long seed, double startPeriod, double endPeriod, double tole previous = date; ++count; SpacecraftState state = propagator.propagate(date); - double[] e = measurement.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }).getEstimatedValue(); + double[] e = measurement.estimateWithoutDerivatives(new SpacecraftState[] { state }).getEstimatedValue(); for (int i = 0; i < 3; ++i) { maxErrorP = FastMath.max(maxErrorP, FastMath.abs(e[i] - m[i])); } diff --git a/src/test/java/org/orekit/estimation/measurements/generation/PositionBuilderTest.java b/src/test/java/org/orekit/estimation/measurements/generation/PositionBuilderTest.java index 50753754d3..dc9c45c71e 100644 --- a/src/test/java/org/orekit/estimation/measurements/generation/PositionBuilderTest.java +++ b/src/test/java/org/orekit/estimation/measurements/generation/PositionBuilderTest.java @@ -129,7 +129,7 @@ private void doTest(long seed, double startPeriod, double endPeriod, double tole previous = date; ++count; SpacecraftState state = propagator.propagate(date); - double[] e = measurement.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }).getEstimatedValue(); + double[] e = measurement.estimateWithoutDerivatives(new SpacecraftState[] { state }).getEstimatedValue(); for (int i = 0; i < m.length; ++i) { maxError = FastMath.max(maxError, FastMath.abs(e[i] - m[i])); } diff --git a/src/test/java/org/orekit/estimation/measurements/generation/TDOABuilderTest.java b/src/test/java/org/orekit/estimation/measurements/generation/TDOABuilderTest.java index 7443e3acd6..b557e3f142 100644 --- a/src/test/java/org/orekit/estimation/measurements/generation/TDOABuilderTest.java +++ b/src/test/java/org/orekit/estimation/measurements/generation/TDOABuilderTest.java @@ -96,10 +96,10 @@ private void doTest(long seed, double startPeriod, double endPeriod, double tole generator.addScheduler(new EventBasedScheduler<>(getBuilder(new Well19937a(seed), emitter, receiver, satellite), new FixedStepSelector(step, TimeScalesFactory.getUTC()), generator.getPropagator(satellite), - BooleanDetector.andCombine(new ElevationDetector(emitter.getBaseFrame()). - withConstantElevation(FastMath.toRadians(5.0)), - new ElevationDetector(receiver.getBaseFrame()). - withConstantElevation(FastMath.toRadians(5.0))), + BooleanDetector.andCombine(EstimationTestUtils.getElevationDetector(emitter.getBaseFrame(), + FastMath.toRadians(5.0)), + EstimationTestUtils.getElevationDetector(receiver.getBaseFrame(), + FastMath.toRadians(5.0))), SignSemantic.FEASIBLE_MEASUREMENT_WHEN_POSITIVE)); final GatheringSubscriber gatherer = new GatheringSubscriber(); generator.addSubscriber(gatherer); @@ -129,7 +129,7 @@ private void doTest(long seed, double startPeriod, double endPeriod, double tole } previous = date; SpacecraftState state = propagator.propagate(date); - double[] e = measurement.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }).getEstimatedValue(); + double[] e = measurement.estimateWithoutDerivatives(new SpacecraftState[] { state }).getEstimatedValue(); for (int i = 0; i < m.length; ++i) { maxError = FastMath.max(maxError, FastMath.abs(e[i] - m[i])); } diff --git a/src/test/java/org/orekit/estimation/measurements/generation/TurnAroundRangeBuilderTest.java b/src/test/java/org/orekit/estimation/measurements/generation/TurnAroundRangeBuilderTest.java index 1f66a02116..d4a3ed1308 100644 --- a/src/test/java/org/orekit/estimation/measurements/generation/TurnAroundRangeBuilderTest.java +++ b/src/test/java/org/orekit/estimation/measurements/generation/TurnAroundRangeBuilderTest.java @@ -98,10 +98,10 @@ private void doTest(long seed, double startPeriod, double endPeriod, double tole generator.addScheduler(new EventBasedScheduler<>(getBuilder(new Well19937a(seed), primary, secondary, satellite), new FixedStepSelector(step, TimeScalesFactory.getUTC()), generator.getPropagator(satellite), - BooleanDetector.andCombine(new ElevationDetector(primary.getBaseFrame()). - withConstantElevation(FastMath.toRadians(5.0)), - new ElevationDetector(secondary.getBaseFrame()). - withConstantElevation(FastMath.toRadians(5.0))), + BooleanDetector.andCombine(EstimationTestUtils.getElevationDetector(primary.getBaseFrame(), + FastMath.toRadians(5.0)), + EstimationTestUtils.getElevationDetector(secondary.getBaseFrame(), + FastMath.toRadians(5.0))), SignSemantic.FEASIBLE_MEASUREMENT_WHEN_POSITIVE)); final GatheringSubscriber gatherer = new GatheringSubscriber(); generator.addSubscriber(gatherer); @@ -131,7 +131,7 @@ private void doTest(long seed, double startPeriod, double endPeriod, double tole } previous = date; SpacecraftState state = propagator.propagate(date); - double[] e = measurement.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }).getEstimatedValue(); + double[] e = measurement.estimateWithoutDerivatives(new SpacecraftState[] { state }).getEstimatedValue(); for (int i = 0; i < m.length; ++i) { maxError = FastMath.max(maxError, FastMath.abs(e[i] - m[i])); } diff --git a/src/test/java/org/orekit/estimation/measurements/gnss/AmbiguityCacheTest.java b/src/test/java/org/orekit/estimation/measurements/gnss/AmbiguityCacheTest.java new file mode 100644 index 0000000000..99f9d989ed --- /dev/null +++ b/src/test/java/org/orekit/estimation/measurements/gnss/AmbiguityCacheTest.java @@ -0,0 +1,47 @@ +/* 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.estimation.measurements.gnss; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.orekit.gnss.Frequency; + +public class AmbiguityCacheTest { + + @Test + public void testCache() { + final AmbiguityCache cache = new AmbiguityCache(); + final AmbiguityDriver + driver01 = cache.getAmbiguity("E18", "TUKT", Frequency.E01.getWavelength()); + Assertions.assertEquals("E18", driver01.getEmitter()); + Assertions.assertEquals("TUKT", driver01.getReceiver()); + Assertions.assertEquals(Frequency.E01.getWavelength(), driver01.getWavelength(), 1.0e-10); + Assertions.assertEquals("ambiguity-E18-TUKT-154.00", driver01.getName()); + final AmbiguityDriver driver05 = cache.getAmbiguity("E18", "TUKT", Frequency.E05.getWavelength()); + Assertions.assertEquals("E18", driver05.getEmitter()); + Assertions.assertEquals("TUKT", driver05.getReceiver()); + Assertions.assertEquals(Frequency.E05.getWavelength(), driver05.getWavelength(), 1.0e-10); + Assertions.assertEquals("ambiguity-E18-TUKT-115.00", driver05.getName()); + final AmbiguityDriver driverB = cache.getAmbiguity("E19", "AGGO", Frequency.E01.getWavelength()); + Assertions.assertEquals("E19", driverB.getEmitter()); + Assertions.assertEquals("AGGO", driverB.getReceiver()); + Assertions.assertEquals(Frequency.E01.getWavelength(), driverB.getWavelength(), 1.0e-10); + Assertions.assertEquals("ambiguity-E19-AGGO-154.00", driverB.getName()); + Assertions.assertSame(driver01, cache.getAmbiguity("E18", "TUKT", Frequency.E01.getWavelength())); + } + +} diff --git a/src/test/java/org/orekit/estimation/measurements/gnss/AmbiguitySolverTest.java b/src/test/java/org/orekit/estimation/measurements/gnss/AmbiguitySolverTest.java index fa32d9acf0..f54a5b9ab6 100644 --- a/src/test/java/org/orekit/estimation/measurements/gnss/AmbiguitySolverTest.java +++ b/src/test/java/org/orekit/estimation/measurements/gnss/AmbiguitySolverTest.java @@ -20,6 +20,7 @@ import org.hipparchus.linear.RealMatrix; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.orekit.gnss.Frequency; import org.orekit.utils.ParameterDriver; import java.util.ArrayList; @@ -54,11 +55,11 @@ public void testJoostenTiberiusFAQ() { } private List createAmbiguities(double...floatValues) { + final AmbiguityCache cache = new AmbiguityCache(); final List ambiguitiesDrivers = new ArrayList<>(floatValues.length); for (int i = 0; i < floatValues.length; ++i) { - final ParameterDriver driver = new ParameterDriver(Phase.AMBIGUITY_NAME + i, 0.0, 1.0, - Double.NEGATIVE_INFINITY, - Double.POSITIVE_INFINITY); + final ParameterDriver driver = cache.getAmbiguity("emitter-" + i, "receiver", + Frequency.E01.getWavelength()); driver.setValue(floatValues[i]); driver.setSelected(true); ambiguitiesDrivers.add(driver); diff --git a/src/test/java/org/orekit/estimation/measurements/gnss/InterSatellitesOneWayRangeRateMeasurementCreator.java b/src/test/java/org/orekit/estimation/measurements/gnss/InterSatellitesOneWayRangeRateMeasurementCreator.java new file mode 100644 index 0000000000..fdfb039e3e --- /dev/null +++ b/src/test/java/org/orekit/estimation/measurements/gnss/InterSatellitesOneWayRangeRateMeasurementCreator.java @@ -0,0 +1,141 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.estimation.measurements.gnss; + +import org.hipparchus.analysis.solvers.BracketingNthOrderBrentSolver; +import org.hipparchus.analysis.solvers.UnivariateSolver; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.orekit.errors.OrekitException; +import org.orekit.estimation.measurements.MeasurementCreator; +import org.orekit.estimation.measurements.ObservableSatellite; +import org.orekit.propagation.BoundedPropagator; +import org.orekit.propagation.SpacecraftState; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.ClockOffset; +import org.orekit.utils.Constants; +import org.orekit.utils.PVCoordinates; +import org.orekit.utils.ParameterDriver; + +import java.util.Arrays; + +public class InterSatellitesOneWayRangeRateMeasurementCreator + extends MeasurementCreator { + + private final BoundedPropagator ephemeris; + private final Vector3D antennaPhaseCenter1; + private final Vector3D antennaPhaseCenter2; + private final ObservableSatellite local; + private final ObservableSatellite remote; + + public InterSatellitesOneWayRangeRateMeasurementCreator(final BoundedPropagator ephemeris, + final double localClockOffset, + final double localClockRate, + final double localClockAcceleration, + final double remoteClockOffset, + final double remoteClockRate, + final double remoteClockAcceleration) { + this(ephemeris, + localClockOffset, localClockRate, localClockAcceleration, + remoteClockOffset, remoteClockRate, remoteClockAcceleration, + Vector3D.ZERO, Vector3D.ZERO); + } + + public InterSatellitesOneWayRangeRateMeasurementCreator(final BoundedPropagator ephemeris, + final double localClockOffset, + final double localClockRate, + final double localClockAcceleration, + final double remoteClockOffset, + final double remoteClockRate, + final double remoteClockAcceleration, + final Vector3D antennaPhaseCenter1, + final Vector3D antennaPhaseCenter2) { + this.ephemeris = ephemeris; + this.antennaPhaseCenter1 = antennaPhaseCenter1; + this.antennaPhaseCenter2 = antennaPhaseCenter2; + this.local = new ObservableSatellite(0); + this.local.getClockOffsetDriver().setValue(localClockOffset); + this.local.getClockDriftDriver().setValue(localClockRate); + this.local.getClockAccelerationDriver().setValue(localClockAcceleration); + this.remote = new ObservableSatellite(1); + this.remote.getClockOffsetDriver().setValue(remoteClockOffset); + this.remote.getClockDriftDriver().setValue(remoteClockRate); + this.remote.getClockAccelerationDriver().setValue(remoteClockAcceleration); + } + + public ObservableSatellite getLocalSatellite() { + return local; + } + + public ObservableSatellite getRemoteSatellite() { + return remote; + } + + public void init(final SpacecraftState s0, final AbsoluteDate t, final double step) { + for (final ParameterDriver driver : Arrays.asList(local.getClockOffsetDriver(), + local.getClockDriftDriver(), + local.getClockAccelerationDriver(), + remote.getClockOffsetDriver(), + remote.getClockDriftDriver(), + remote.getClockAccelerationDriver())) { + if (driver.getReferenceDate() == null) { + driver.setReferenceDate(s0.getDate()); + } + } + } + + public void handleStep(final SpacecraftState currentState) { + try { + final AbsoluteDate date = currentState.getDate(); + final PVCoordinates pv = currentState.toTransform().getInverse(). + transformPVCoordinates(new PVCoordinates(antennaPhaseCenter1)); + final ClockOffset localClk = local.getQuadraticClockModel().getOffset(date); + + final UnivariateSolver solver = new BracketingNthOrderBrentSolver(1.0e-12, 5); + + final double downLinkDelay = solver.solve(1000, x -> { + final Vector3D other = ephemeris. + propagate(date.shiftedBy(-x)). + toTransform(). + getInverse(). + transformPosition(antennaPhaseCenter2); + final double d = Vector3D.distance(pv.getPosition(), other); + return d - x * Constants.SPEED_OF_LIGHT; + }, -1.0, 1.0); + final AbsoluteDate transitDate = currentState.getDate().shiftedBy(-downLinkDelay); + final PVCoordinates otherAtTransit = + ephemeris.propagate(transitDate). + toTransform(). + getInverse(). + transformPVCoordinates(new PVCoordinates(antennaPhaseCenter2)); + final PVCoordinates delta = new PVCoordinates(otherAtTransit, pv); + final double rangeRate = Vector3D.dotProduct(delta.getPosition().normalize(), delta.getVelocity()) + + Constants.SPEED_OF_LIGHT * (local.getQuadraticClockModel().getOffset(date).getRate() - + remote.getQuadraticClockModel().getOffset(transitDate).getRate()); + + // generate measurement + final InterSatellitesOneWayRangeRate phase = new InterSatellitesOneWayRangeRate(local, remote, + date.shiftedBy(localClk.getOffset()), + rangeRate, + 1.0, 10); + addMeasurement(phase); + + } catch (OrekitException oe) { + throw new OrekitException(oe); + } + } + +} diff --git a/src/test/java/org/orekit/estimation/measurements/gnss/InterSatellitesOneWayRangeRateTest.java b/src/test/java/org/orekit/estimation/measurements/gnss/InterSatellitesOneWayRangeRateTest.java new file mode 100644 index 0000000000..6d75e7b711 --- /dev/null +++ b/src/test/java/org/orekit/estimation/measurements/gnss/InterSatellitesOneWayRangeRateTest.java @@ -0,0 +1,613 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.estimation.measurements.gnss; + +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.stat.descriptive.moment.Mean; +import org.hipparchus.stat.descriptive.rank.Max; +import org.hipparchus.stat.descriptive.rank.Median; +import org.hipparchus.stat.descriptive.rank.Min; +import org.hipparchus.util.FastMath; +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.estimation.measurements.EstimatedMeasurementBase; +import org.orekit.estimation.measurements.ObservedMeasurement; +import org.orekit.orbits.CartesianOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; +import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.BoundedPropagator; +import org.orekit.propagation.EphemerisGenerator; +import org.orekit.propagation.Propagator; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.conversion.NumericalPropagatorBuilder; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.Constants; +import org.orekit.utils.Differentiation; +import org.orekit.utils.PVCoordinates; +import org.orekit.utils.ParameterDriver; +import org.orekit.utils.ParameterFunction; +import org.orekit.utils.TimeSpanMap.Span; +import org.orekit.utils.TimeStampedPVCoordinates; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Locale; + +public class InterSatellitesOneWayRangeRateTest { + + /** + * Test the values of the range rate comparing the observed values and the estimated values + * Both are calculated with a different algorithm + */ + @Test + public void testValues() { + boolean printResults = false; + if (printResults) { + System.out.println("\nTest inter-satellites Range Rate Values\n"); + } + // Run test + this.genericTestValues(printResults); + } + + /** + * Test the values of the state derivatives using a numerical + * finite differences calculation as a reference + */ + @Test + public void testStateDerivativesEmitter() { + + boolean printResults = false; + if (printResults) { + System.out.println("\nTest inter-satellites Range Rate State Derivatives - Finite Differences Comparison\n"); + } + // Run test + double refErrorsPMedian = 7.1e-10; + double refErrorsPMean = 7.1e-09; + double refErrorsPMax = 1.8e-06; + double refErrorsVMedian = 2.4e-10; + double refErrorsVMean = 5.2e-10; + double refErrorsVMax = 2.5e-08; + this.genericTestStateDerivatives(printResults, 0, + refErrorsPMedian, refErrorsPMean, refErrorsPMax, + refErrorsVMedian, refErrorsVMean, refErrorsVMax); + } + + /** + * Test the values of the state derivatives using a numerical + * finite differences calculation as a reference + */ + @Test + public void testStateDerivativesTransit() { + + boolean printResults = false; + if (printResults) { + System.out.println("\nTest inter-satellites Range Rate State Derivatives - Finite Differences Comparison\n"); + } + // Run test + double refErrorsPMedian = 7.1e-10; + double refErrorsPMean = 7.1e-09; + double refErrorsPMax = 1.8e-06; + double refErrorsVMedian = 2.6e-010; + double refErrorsVMean = 4.9e-10; + double refErrorsVMax = 1.1e-08; + this.genericTestStateDerivatives(printResults, 1, + refErrorsPMedian, refErrorsPMean, refErrorsPMax, + refErrorsVMedian, refErrorsVMean, refErrorsVMax); + } + + /** + * Test the values of the parameters' derivatives using a numerical + * finite differences calculation as a reference + */ + @Test + public void testParameterDerivatives() { + + // Print the results ? + boolean printResults = false; + + if (printResults) { + System.out.println("\nTest Range Rate Parameter Derivatives - Finite Differences Comparison\n"); + } + // Run test + double refErrorsMedian = 2.2e-16; + double refErrorsMean = 1.2e-7; + double refErrorsMax = 2.9e-6; + this.genericTestParameterDerivatives(printResults, + refErrorsMedian, refErrorsMean, refErrorsMax); + + } + + /** + * Generic test function for values of the inter-satellites range rate + * @param printResults Print the results ? + */ + void genericTestValues(final boolean printResults) { + + Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides"); + + final NumericalPropagatorBuilder propagatorBuilder = + context.createBuilder(OrbitType.KEPLERIAN, PositionAngleType.TRUE, true, + 1.0e-6, 60.0, 0.001); + + // Create perfect inter-satellites range rate measurements + final TimeStampedPVCoordinates original = context.initialOrbit.getPVCoordinates(); + final Orbit closeOrbit = new CartesianOrbit(new TimeStampedPVCoordinates(context.initialOrbit.getDate(), + original.getPosition().add(new Vector3D(1000, 2000, 3000)), + original.getVelocity().add(new Vector3D(-0.03, 0.01, 0.02))), + context.initialOrbit.getFrame(), + context.initialOrbit.getMu()); + final Propagator closePropagator = EstimationTestUtils.createPropagator(closeOrbit, + propagatorBuilder); + final EphemerisGenerator generator = closePropagator.getEphemerisGenerator(); + closePropagator.propagate(context.initialOrbit.getDate().shiftedBy(3.5 * closeOrbit.getKeplerianPeriod())); + final BoundedPropagator ephemeris = generator.getGeneratedEphemeris(); + final Propagator propagator = EstimationTestUtils.createPropagator(context.initialOrbit, + propagatorBuilder); + + final double localClockOffset = 0.14e-06; + final double localClockRate = -0.12e-10; + final double localClockAcceleration = 1.4e-13; + final double remoteClockOffset = 469.0e-06; + final double remoteClockRate = 33.0e-10; + final double remoteClockAcceleration = 0.5e-13; + final List> measurements = + EstimationTestUtils.createMeasurements(propagator, + new InterSatellitesOneWayRangeRateMeasurementCreator(ephemeris, + localClockOffset, localClockRate, localClockAcceleration, + remoteClockOffset, remoteClockRate, remoteClockAcceleration), + 1.0, 3.0, 300.0); + + // Lists for results' storage - Used only for derivatives with respect to state + // "final" value to be seen by "handleStep" function of the propagator + final List absoluteErrors = new ArrayList<>(); + final List relativeErrors = new ArrayList<>(); + + // Use a lambda function to implement "handleStep" function + propagator.setStepHandler(interpolator -> { + + for (final ObservedMeasurement measurement : measurements) { + + // Play test if the measurement date is between interpolator previous and current date + if (measurement.getDate().isAfter(interpolator.getPreviousState()) && + measurement.getDate().isBeforeOrEqualTo(interpolator.getCurrentState())) { + // We intentionally propagate to a date which is close to the + // real spacecraft state but is *not* the accurate date, by + // compensating only part of the downlink delay. This is done + // in order to validate the partial derivatives with respect + // to velocity. + final double meanDelay = measurement.getObservedValue()[0] / Constants.SPEED_OF_LIGHT; + final AbsoluteDate date = measurement.getDate().shiftedBy(-0.75 * meanDelay); + final SpacecraftState state = interpolator.getInterpolatedState(date); + + // Values of the range rate & errors + final double rangeRateObserved = measurement.getObservedValue()[0]; + final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(new SpacecraftState[] { + state, + ephemeris.propagate(state.getDate()) + }); + + final TimeStampedPVCoordinates[] participants = estimated.getParticipants(); + Assertions.assertEquals(2, participants.length); + final PVCoordinates delta = new PVCoordinates(participants[0], participants[1]); + final double radialVelocity = Vector3D.dotProduct(delta.getVelocity(), delta.getPosition().normalize()); + final AbsoluteDate t0 = measurement.getSatellites().get(0).getClockOffsetDriver().getReferenceDate(); + final double dtLocal = measurement.getDate().durationFrom(t0); + final double localRate = 2 * localClockAcceleration * dtLocal + localClockRate; + final double dtRemote = participants[0].getDate().durationFrom(t0); + final double remoteRate = 2 * remoteClockAcceleration * dtRemote + remoteClockRate; + Assertions.assertEquals(radialVelocity + Constants.SPEED_OF_LIGHT * (localRate - remoteRate), + estimated.getEstimatedValue()[0], + 1.0e-7); + + final double rangeRateEstimated = estimated.getEstimatedValue()[0]; + final double absoluteError = rangeRateEstimated-rangeRateObserved; + absoluteErrors.add(absoluteError); + relativeErrors.add(FastMath.abs(absoluteError)/FastMath.abs(rangeRateObserved)); + + // Print results on console ? + if (printResults) { + final AbsoluteDate measurementDate = measurement.getDate(); + + System.out.format(Locale.US, "%-23s %-23s %19.6f %19.6f %13.6e %13.6e%n", + measurementDate.toStringWithoutUtcOffset(context.utc, 3), + date.toStringWithoutUtcOffset(context.utc, 3), + rangeRateObserved, rangeRateEstimated, + FastMath.abs(rangeRateEstimated-rangeRateObserved), + FastMath.abs((rangeRateEstimated-rangeRateObserved)/rangeRateObserved)); + } + + } // End if measurement date between previous and current interpolator step + } // End for loop on the measurements + }); // End lambda function handlestep + + // Print results on console ? Header + if (printResults) { + System.out.format(Locale.US, "%-23s %-23s %19s %19s %19s %19s%n", + "Measurement Date", "State Date", + "range rate observed [m/s]", "range rate estimated [m/s]", + "Δrange rate [m/s]", "rel Δrange rate"); + } + + // Rewind the propagator to initial date + propagator.propagate(context.initialOrbit.getDate()); + + // Sort measurements chronologically + measurements.sort(Comparator.naturalOrder()); + + // Propagate to final measurement's date + propagator.propagate(measurements.get(measurements.size()-1).getDate()); + + // Convert lists to double array + final double[] absErrors = absoluteErrors.stream().mapToDouble(Double::doubleValue).toArray(); + final double[] relErrors = relativeErrors.stream().mapToDouble(Double::doubleValue).toArray(); + + // Statistics' assertion + final double absErrorsMedian = new Median().evaluate(absErrors); + final double absErrorsMin = new Min().evaluate(absErrors); + final double absErrorsMax = new Max().evaluate(absErrors); + final double relErrorsMedian = new Median().evaluate(relErrors); + final double relErrorsMax = new Max().evaluate(relErrors); + + // Print the results on console ? Final results + if (printResults) { + System.out.println(); + System.out.println("Absolute errors median: " + absErrorsMedian); + System.out.println("Absolute errors min : " + absErrorsMin); + System.out.println("Absolute errors max : " + absErrorsMax); + System.out.println("Relative errors median: " + relErrorsMedian); + System.out.println("Relative errors max : " + relErrorsMax); + } + + Assertions.assertEquals(0.0, absErrorsMedian, 3.7e-09); + Assertions.assertEquals(0.0, absErrorsMin, 2.6e-11); + Assertions.assertEquals(0.0, absErrorsMax, 1.5e-08); + Assertions.assertEquals(0.0, relErrorsMedian, 9.9e-10); + Assertions.assertEquals(0.0, relErrorsMax, 5.7e-8); + + // Test measurement type + Assertions.assertEquals(InterSatellitesOneWayRangeRate.MEASUREMENT_TYPE, measurements.get(0).getMeasurementType()); + } + + void genericTestStateDerivatives(final boolean printResults, final int index, + final double refErrorsPMedian, final double refErrorsPMean, final double refErrorsPMax, + final double refErrorsVMedian, final double refErrorsVMean, final double refErrorsVMax) { + + Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides"); + + final NumericalPropagatorBuilder propagatorBuilder = + context.createBuilder(OrbitType.KEPLERIAN, PositionAngleType.TRUE, true, + 1.0e-6, 60.0, 0.001); + + // Create perfect inter-satellites range rate measurements + final TimeStampedPVCoordinates original = context.initialOrbit.getPVCoordinates(); + final Orbit closeOrbit = new CartesianOrbit(new TimeStampedPVCoordinates(context.initialOrbit.getDate(), + original.getPosition().add(new Vector3D(1000, 2000, 3000)), + original.getVelocity().add(new Vector3D(-0.03, 0.01, 0.02))), + context.initialOrbit.getFrame(), + context.initialOrbit.getMu()); + final Propagator closePropagator = EstimationTestUtils.createPropagator(closeOrbit, + propagatorBuilder); + final EphemerisGenerator generator = closePropagator.getEphemerisGenerator(); + closePropagator.propagate(context.initialOrbit.getDate().shiftedBy(3.5 * closeOrbit.getKeplerianPeriod())); + final BoundedPropagator ephemeris = generator.getGeneratedEphemeris(); + final Propagator propagator = EstimationTestUtils.createPropagator(context.initialOrbit, + propagatorBuilder); + + final double localClockOffset = 0.14e-06; + final double localClockRate = -0.12e-10; + final double localClockAcceleration = 1.4e-13; + final double remoteClockOffset = 469.0e-06; + final double remoteClockRate = 33.0e-10; + final double remoteClockAcceleration = 0.5e-13; + final List> measurements = + EstimationTestUtils.createMeasurements(propagator, + new InterSatellitesOneWayRangeRateMeasurementCreator(ephemeris, + localClockOffset, localClockRate, localClockAcceleration, + remoteClockOffset, remoteClockRate, remoteClockAcceleration), + 1.0, 3.0, 300.0); + + // Lists for results' storage - Used only for derivatives with respect to state + // "final" value to be seen by "handleStep" function of the propagator + final List errorsP = new ArrayList<>(); + final List errorsV = new ArrayList<>(); + + // Use a lambda function to implement "handleStep" function + propagator.setStepHandler(interpolator -> { + + for (final ObservedMeasurement measurement : measurements) { + + // Play test if the measurement date is between interpolator previous and current date + if (measurement.getDate().isAfter(interpolator.getPreviousState()) && + measurement.getDate().isBeforeOrEqualTo(interpolator.getCurrentState())) { + + // We intentionally propagate to a date which is close to the + // real spacecraft state but is *not* the accurate date, by + // compensating only part of the downlink delay. This is done + // in order to validate the partial derivatives with respect + // to velocity. + final double meanDelay = measurement.getObservedValue()[0] / Constants.SPEED_OF_LIGHT; + final AbsoluteDate date = measurement.getDate().shiftedBy(-0.75 * meanDelay); + final SpacecraftState[] states = { + interpolator.getInterpolatedState(date), + ephemeris.propagate(date) + }; + final double[][] jacobian = measurement.estimate(0, 0, states).getStateDerivatives(index); + + // Jacobian reference value + final double[][] jacobianRef; + + // Compute a reference value using finite differences + jacobianRef = Differentiation.differentiate(state -> { + final SpacecraftState[] s = states.clone(); + s[index] = state; + return measurement.estimateWithoutDerivatives(s).getEstimatedValue(); + }, measurement.getDimension(), propagator.getAttitudeProvider(), + OrbitType.CARTESIAN, PositionAngleType.TRUE, 2.0, 3).value(states[index]); + + Assertions.assertEquals(jacobianRef.length, jacobian.length); + Assertions.assertEquals(jacobianRef[0].length, jacobian[0].length); + + // Errors & relative errors on the Jacobian + double [][] dJacobian = new double[jacobian.length][jacobian[0].length]; + double [][] dJacobianRelative = new double[jacobian.length][jacobian[0].length]; + for (int i = 0; i < jacobian.length; ++i) { + for (int j = 0; j < jacobian[i].length; ++j) { + dJacobian[i][j] = jacobian[i][j] - jacobianRef[i][j]; + dJacobianRelative[i][j] = FastMath.abs(dJacobian[i][j]/jacobianRef[i][j]); + + if (j < 3) { + errorsP.add(dJacobianRelative[i][j]); + } else { + errorsV.add(dJacobianRelative[i][j]); + } + } + } + // Print values in console ? + if (printResults) { + System.out.format(Locale.US, "%-23s %-23s " + + "%10.3e %10.3e %10.3e " + + "%10.3e %10.3e %10.3e " + + "%10.3e %10.3e %10.3e " + + "%10.3e %10.3e %10.3e%n", + measurement.getDate().toStringWithoutUtcOffset(context.utc, 3), + date.toStringWithoutUtcOffset(context.utc, 3), + dJacobian[0][0], dJacobian[0][1], dJacobian[0][2], + dJacobian[0][3], dJacobian[0][4], dJacobian[0][5], + dJacobianRelative[0][0], dJacobianRelative[0][1], dJacobianRelative[0][2], + dJacobianRelative[0][3], dJacobianRelative[0][4], dJacobianRelative[0][5]); + } + } // End if measurement date between previous and current interpolator step + } // End for loop on the measurements + }); + + // Print results on console ? + if (printResults) { + System.out.format(Locale.US, "%-23s %-23s " + + "%10s %10s %10s " + + "%10s %10s %10s " + + "%10s %10s %10s " + + "%10s %10s %10s%n", + "Measurement Date", "State Date", + "ΔdPx", "ΔdPy", "ΔdPz", "ΔdVx", "ΔdVy", "ΔdVz", + "rel ΔdPx", "rel ΔdPy", "rel ΔdPz", + "rel ΔdVx", "rel ΔdVy", "rel ΔdVz"); + } + + // Rewind the propagator to initial date + propagator.propagate(context.initialOrbit.getDate()); + + // Sort measurements, primarily chronologically + measurements.sort(Comparator.naturalOrder()); + + // Propagate to final measurement's date + propagator.propagate(measurements.get(measurements.size()-1).getDate()); + + // Convert lists to double[] and evaluate some statistics + final double[] relErrorsP = errorsP.stream().mapToDouble(Double::doubleValue).toArray(); + final double[] relErrorsV = errorsV.stream().mapToDouble(Double::doubleValue).toArray(); + + final double errorsPMedian = new Median().evaluate(relErrorsP); + final double errorsPMean = new Mean().evaluate(relErrorsP); + final double errorsPMax = new Max().evaluate(relErrorsP); + final double errorsVMedian = new Median().evaluate(relErrorsV); + final double errorsVMean = new Mean().evaluate(relErrorsV); + final double errorsVMax = new Max().evaluate(relErrorsV); + + // Print the results on console ? + if (printResults) { + System.out.println(); + System.out.format(Locale.US, "Relative errors dR/dP -> Median: %6.3e / Mean: %6.3e / Max: %6.3e%n", + errorsPMedian, errorsPMean, errorsPMax); + System.out.format(Locale.US, "Relative errors dR/dV -> Median: %6.3e / Mean: %6.3e / Max: %6.3e%n", + errorsVMedian, errorsVMean, errorsVMax); + } + + Assertions.assertEquals(0.0, errorsPMedian, refErrorsPMedian); + Assertions.assertEquals(0.0, errorsPMean, refErrorsPMean); + Assertions.assertEquals(0.0, errorsPMax, refErrorsPMax); + Assertions.assertEquals(0.0, errorsVMedian, refErrorsVMedian); + Assertions.assertEquals(0.0, errorsVMean, refErrorsVMean); + Assertions.assertEquals(0.0, errorsVMax, refErrorsVMax); + } + + void genericTestParameterDerivatives(final boolean printResults, + final double refErrorsMedian, final double refErrorsMean, final double refErrorsMax) { + + Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides"); + + final NumericalPropagatorBuilder propagatorBuilder = + context.createBuilder(OrbitType.KEPLERIAN, PositionAngleType.TRUE, true, + 1.0e-6, 60.0, 0.001); + + // Create perfect inter-satellites range rate measurements + final TimeStampedPVCoordinates original = context.initialOrbit.getPVCoordinates(); + final Orbit closeOrbit = new CartesianOrbit(new TimeStampedPVCoordinates(context.initialOrbit.getDate(), + original.getPosition().add(new Vector3D(1000, 2000, 3000)), + original.getVelocity().add(new Vector3D(-0.03, 0.01, 0.02))), + context.initialOrbit.getFrame(), + context.initialOrbit.getMu()); + final Propagator closePropagator = EstimationTestUtils.createPropagator(closeOrbit, propagatorBuilder); + final EphemerisGenerator generator = closePropagator.getEphemerisGenerator(); + closePropagator.propagate(context.initialOrbit.getDate().shiftedBy(3.5 * closeOrbit.getKeplerianPeriod())); + final BoundedPropagator ephemeris = generator.getGeneratedEphemeris(); + + // Create perfect range rate measurements + final double localClockOffset = 0.14e-06; + final double localClockRate = -0.12e-10; + final double localClockAcceleration = 1.4e-13; + final double remoteClockOffset = 469.0e-06; + final double remoteClockRate = 33.0e-10; + final double remoteClockAcceleration = 0.5e-13; + final InterSatellitesOneWayRangeRateMeasurementCreator creator = + new InterSatellitesOneWayRangeRateMeasurementCreator(ephemeris, + localClockOffset, localClockRate, localClockAcceleration, + remoteClockOffset, remoteClockRate, remoteClockAcceleration); + creator.getLocalSatellite().getClockOffsetDriver().setSelected(true); + creator.getLocalSatellite().getClockDriftDriver().setSelected(true); + creator.getLocalSatellite().getClockAccelerationDriver().setSelected(true); + creator.getRemoteSatellite().getClockOffsetDriver().setSelected(true); + creator.getRemoteSatellite().getClockDriftDriver().setSelected(true); + creator.getRemoteSatellite().getClockAccelerationDriver().setSelected(true); + + final Propagator propagator = EstimationTestUtils.createPropagator(context.initialOrbit, + propagatorBuilder); + final List> measurements = + EstimationTestUtils.createMeasurements(propagator, creator, 1.0, 3.0, 300.0); + + // List to store the results + final List relErrorList = new ArrayList<>(); + + // Use a lambda function to implement "handleStep" function + propagator.setStepHandler(interpolator -> { + + for (final ObservedMeasurement measurement : measurements) { + + // Play test if the measurement date is between interpolator previous and current date + if (measurement.getDate().isAfter(interpolator.getPreviousState()) && + measurement.getDate().isBeforeOrEqualTo(interpolator.getCurrentState())) { + + // We intentionally propagate to a date which is close to the + // real spacecraft state but is *not* the accurate date, by + // compensating only part of the downlink delay. This is done + // in order to validate the partial derivatives with respect + // to velocity. If we had chosen the proper state date, the + // range rate would have depended only on the current position but + // not on the current velocity. + final double meanDelay = measurement.getObservedValue()[0] / Constants.SPEED_OF_LIGHT; + final AbsoluteDate date = measurement.getDate().shiftedBy(-0.75 * meanDelay); + final SpacecraftState[] states = { + interpolator.getInterpolatedState(date), + ephemeris.propagate(date) + }; + final ParameterDriver[] drivers = new ParameterDriver[] { + measurement.getSatellites().get(0).getClockOffsetDriver(), + measurement.getSatellites().get(0).getClockDriftDriver(), + measurement.getSatellites().get(0).getClockAccelerationDriver(), + measurement.getSatellites().get(1).getClockOffsetDriver(), + measurement.getSatellites().get(1).getClockDriftDriver(), + measurement.getSatellites().get(1).getClockAccelerationDriver() + }; + + for (final ParameterDriver driver : drivers) { + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + final double[] gradient = measurement.estimate(0, 0, states).getParameterDerivatives(driver, span.getStart()); + Assertions.assertEquals(1, measurement.getDimension()); + Assertions.assertEquals(1, gradient.length); + + // Compute a reference value using finite differences + final ParameterFunction dMkdP = + Differentiation.differentiate(new ParameterFunction() { + /** {@inheritDoc} */ + @Override + public double value(final ParameterDriver parameterDriver, final AbsoluteDate date) { + return measurement. + estimateWithoutDerivatives(states). + getEstimatedValue()[0]; + } + }, 3, 20.0 * driver.getScale()); + final double ref = dMkdP.value(driver, span.getStart()); + + final double relError; + if (ref == 0.0) { + // this protection is because range rate is completely independent for remote clock offset + // (it depends only on remote clock rate and acceleration), so ref is exactly 0.0 + // so here we compute an absolute error and not a relative error (anyway, it is 0) + relError = ref - gradient[0]; + } else { + relError = FastMath.abs((ref - gradient[0]) / ref); + } + relErrorList.add(relError); + + if (printResults) { + System.out.format(Locale.US, "%10.3e %10.3e ", gradient[0]-ref, relError); + } + + } + } + if (printResults) { + System.out.format(Locale.US, "%n"); + } + + } // End if measurement date between previous and current interpolator step + } // End for loop on the measurements + }); + + // Rewind the propagator to initial date + propagator.propagate(context.initialOrbit.getDate()); + + // Sort measurements chronologically + measurements.sort(Comparator.naturalOrder()); + + // Print results ? Header + if (printResults) { + System.out.format(Locale.US, "%-22s %-22s %-22s %-22s %-22s %-22s%n" + + "%10s %10s %10s %10s %10s %10s %10s %10s %10s %10s %10s %10s%n", + "local offset", "local rate", "local acceleration", + "remote offset", "remote rate", "remote acceleration", + "Δoₗ", "rel Δoₗ", "Δrₗ", "rel Δrₗ", "Δaₗ", "rel Δaₗ", + "Δoᵣ", "rel Δoᵣ", "Δrᵣ", "rel Δrᵣ", "Δaᵣ", "rel Δaᵣ"); + } + + // Propagate to final measurement's date + propagator.propagate(measurements.get(measurements.size()-1).getDate()); + + // Convert error list to double[] + final double[] relErrors = relErrorList.stream().mapToDouble(Double::doubleValue).toArray(); + + // Compute statistics + final double relErrorsMedian = new Median().evaluate(relErrors); + final double relErrorsMean = new Mean().evaluate(relErrors); + final double relErrorsMax = new Max().evaluate(relErrors); + + // Print the results on console ? + if (printResults) { + System.out.println(); + System.out.format(Locale.US, "Relative errors dR/dQ -> Median: %6.3e / Mean: %6.3e / Max: %6.3e%n", + relErrorsMedian, relErrorsMean, relErrorsMax); + } + + Assertions.assertEquals(0.0, relErrorsMedian, refErrorsMedian); + Assertions.assertEquals(0.0, relErrorsMean, refErrorsMean); + Assertions.assertEquals(0.0, relErrorsMax, refErrorsMax); + + } + +} diff --git a/src/test/java/org/orekit/estimation/measurements/gnss/InterSatellitesPhaseMeasurementCreator.java b/src/test/java/org/orekit/estimation/measurements/gnss/InterSatellitesPhaseMeasurementCreator.java index 387666a525..cc5f83e5e0 100644 --- a/src/test/java/org/orekit/estimation/measurements/gnss/InterSatellitesPhaseMeasurementCreator.java +++ b/src/test/java/org/orekit/estimation/measurements/gnss/InterSatellitesPhaseMeasurementCreator.java @@ -16,14 +16,12 @@ */ package org.orekit.estimation.measurements.gnss; -import org.hipparchus.analysis.UnivariateFunction; import org.hipparchus.analysis.solvers.BracketingNthOrderBrentSolver; import org.hipparchus.analysis.solvers.UnivariateSolver; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.orekit.errors.OrekitException; import org.orekit.estimation.measurements.MeasurementCreator; import org.orekit.estimation.measurements.ObservableSatellite; -import org.orekit.estimation.measurements.modifiers.InterSatellitesPhaseAmbiguityModifier; import org.orekit.gnss.Frequency; import org.orekit.propagation.BoundedPropagator; import org.orekit.propagation.SpacecraftState; @@ -34,11 +32,12 @@ public class InterSatellitesPhaseMeasurementCreator extends MeasurementCreator { private final BoundedPropagator ephemeris; private final double wavelength; - private final InterSatellitesPhaseAmbiguityModifier ambiguity; + private final int ambiguity; private final Vector3D antennaPhaseCenter1; private final Vector3D antennaPhaseCenter2; private final ObservableSatellite local; private final ObservableSatellite remote; + private final AmbiguityCache cache; public InterSatellitesPhaseMeasurementCreator(final BoundedPropagator ephemeris, final Frequency frequency, @@ -57,13 +56,14 @@ public InterSatellitesPhaseMeasurementCreator(final BoundedPropagator ephemeris, final Vector3D antennaPhaseCenter2) { this.ephemeris = ephemeris; this.wavelength = frequency.getWavelength(); - this.ambiguity = new InterSatellitesPhaseAmbiguityModifier(0, ambiguity); + this.ambiguity = ambiguity; this.antennaPhaseCenter1 = antennaPhaseCenter1; this.antennaPhaseCenter2 = antennaPhaseCenter2; this.local = new ObservableSatellite(0); this.local.getClockOffsetDriver().setValue(localClockOffset); this.remote = new ObservableSatellite(1); this.remote.getClockOffsetDriver().setValue(remoteClockOffset); + this.cache = new AmbiguityCache(); } public ObservableSatellite getLocalSatellite() { @@ -85,25 +85,22 @@ public void init(final SpacecraftState s0, final AbsoluteDate t, final double st public void handleStep(final SpacecraftState currentState) { try { - final double n = ambiguity.getParametersDrivers().get(0).getValue(); final AbsoluteDate date = currentState.getDate(); - final Vector3D position = currentState.toTransform().toStaticTransform().getInverse().transformPosition(antennaPhaseCenter1); + final Vector3D position = currentState.toStaticTransform().getInverse().transformPosition(antennaPhaseCenter1); final double remoteClk = remote.getClockOffsetDriver().getValue(date); final double localClk = local.getClockOffsetDriver().getValue(date); final double deltaD = Constants.SPEED_OF_LIGHT * (localClk - remoteClk); final UnivariateSolver solver = new BracketingNthOrderBrentSolver(1.0e-12, 5); - final double downLinkDelay = solver.solve(1000, new UnivariateFunction() { - public double value(final double x) { - final Vector3D other = ephemeris. - propagate(date.shiftedBy(-x)). - toTransform(). - getInverse(). - transformPosition(antennaPhaseCenter2); - final double d = Vector3D.distance(position, other); - return d - x * Constants.SPEED_OF_LIGHT; - } + final double downLinkDelay = solver.solve(1000, x -> { + final Vector3D other = ephemeris. + propagate(date.shiftedBy(-x)). + toTransform(). + getInverse(). + transformPosition(antennaPhaseCenter2); + final double d = Vector3D.distance(position, other); + return d - x * Constants.SPEED_OF_LIGHT; }, -1.0, 1.0); final AbsoluteDate transitDate = currentState.getDate().shiftedBy(-downLinkDelay); final Vector3D otherAtTransit = @@ -114,8 +111,11 @@ public double value(final double x) { final double downLinkDistance = Vector3D.distance(position, otherAtTransit); // generate measurement - final InterSatellitesPhase phase = new InterSatellitesPhase(local, remote, date.shiftedBy(localClk), (downLinkDistance + deltaD) / wavelength + n, wavelength, 1.0, 10); - phase.addModifier(ambiguity); + final InterSatellitesPhase phase = + new InterSatellitesPhase(local, remote, date.shiftedBy(localClk), + (downLinkDistance + deltaD) / wavelength + ambiguity, wavelength, + 1.0, 10, cache); + phase.getAmbiguityDriver().setValue(ambiguity); addMeasurement(phase); } catch (OrekitException oe) { diff --git a/src/test/java/org/orekit/estimation/measurements/gnss/InterSatellitesPhaseTest.java b/src/test/java/org/orekit/estimation/measurements/gnss/InterSatellitesPhaseTest.java index a259f283b5..0bf626ef9a 100644 --- a/src/test/java/org/orekit/estimation/measurements/gnss/InterSatellitesPhaseTest.java +++ b/src/test/java/org/orekit/estimation/measurements/gnss/InterSatellitesPhaseTest.java @@ -50,7 +50,6 @@ import org.orekit.utils.Differentiation; import org.orekit.utils.ParameterDriver; import org.orekit.utils.ParameterFunction; -import org.orekit.utils.StateFunction; import org.orekit.utils.TimeSpanMap.Span; import org.orekit.utils.TimeStampedPVCoordinates; @@ -177,8 +176,8 @@ void genericTestValues(final boolean printResults) { // Lists for results' storage - Used only for derivatives with respect to state // "final" value to be seen by "handleStep" function of the propagator - final List absoluteErrors = new ArrayList(); - final List relativeErrors = new ArrayList(); + final List absoluteErrors = new ArrayList<>(); + final List relativeErrors = new ArrayList<>(); // Use a lambda function to implement "handleStep" function propagator.setStepHandler(interpolator -> { @@ -200,8 +199,7 @@ void genericTestValues(final boolean printResults) { // Values of the phase & errors final double phaseObserved = measurement.getObservedValue()[0]; - final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(0, 0, - new SpacecraftState[] { + final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(new SpacecraftState[] { state, ephemeris.propagate(state.getDate()) }); @@ -210,7 +208,7 @@ void genericTestValues(final boolean printResults) { Assertions.assertEquals(2, participants.length); Assertions.assertEquals(FREQUENCY.getWavelength(), ((InterSatellitesPhase) measurement).getWavelength(), 1.0e-15); final double dt = participants[1].getDate().durationFrom(participants[0].getDate()); - Assertions.assertEquals(1.0e6 * FREQUENCY.getMHzFrequency() * (dt + localClockOffset - remoteClockOffset) + ambiguity, + Assertions.assertEquals(FREQUENCY.getFrequency() * (dt + localClockOffset - remoteClockOffset) + ambiguity, estimated.getEstimatedValue()[0], 1.0e-7); @@ -317,8 +315,8 @@ void genericTestStateDerivatives(final boolean printResults, final int index, // Lists for results' storage - Used only for derivatives with respect to state // "final" value to be seen by "handleStep" function of the propagator - final List errorsP = new ArrayList(); - final List errorsV = new ArrayList(); + final List errorsP = new ArrayList<>(); + final List errorsV = new ArrayList<>(); // Use a lambda function to implement "handleStep" function propagator.setStepHandler(interpolator -> { @@ -326,9 +324,8 @@ void genericTestStateDerivatives(final boolean printResults, final int index, for (final ObservedMeasurement measurement : measurements) { // Play test if the measurement date is between interpolator previous and current date - if ((measurement.getDate().durationFrom(interpolator.getPreviousState().getDate()) > 0.) && - (measurement.getDate().durationFrom(interpolator.getCurrentState().getDate()) <= 0.) - ) { + if (measurement.getDate().isAfter(interpolator.getPreviousState()) && + measurement.getDate().isBeforeOrEqualTo(interpolator.getCurrentState())) { // We intentionally propagate to a date which is close to the // real spacecraft state but is *not* the accurate date, by @@ -347,14 +344,12 @@ void genericTestStateDerivatives(final boolean printResults, final int index, final double[][] jacobianRef; // Compute a reference value using finite differences - jacobianRef = Differentiation.differentiate(new StateFunction() { - public double[] value(final SpacecraftState state) { - final SpacecraftState[] s = states.clone(); - s[index] = state; - return measurement.estimateWithoutDerivatives(0, 0, s).getEstimatedValue(); - } + jacobianRef = Differentiation.differentiate(state -> { + final SpacecraftState[] s = states.clone(); + s[index] = state; + return measurement.estimateWithoutDerivatives(s).getEstimatedValue(); }, measurement.getDimension(), propagator.getAttitudeProvider(), - OrbitType.CARTESIAN, PositionAngleType.TRUE, 2.0, 3).value(states[index]); + OrbitType.CARTESIAN, PositionAngleType.TRUE, 2.0, 3).value(states[index]); Assertions.assertEquals(jacobianRef.length, jacobian.length); Assertions.assertEquals(jacobianRef[0].length, jacobian[0].length); @@ -367,8 +362,11 @@ public double[] value(final SpacecraftState state) { dJacobian[i][j] = jacobian[i][j] - jacobianRef[i][j]; dJacobianRelative[i][j] = FastMath.abs(dJacobian[i][j]/jacobianRef[i][j]); - if (j < 3) { errorsP.add(dJacobianRelative[i][j]); - } else { errorsV.add(dJacobianRelative[i][j]); } + if (j < 3) { + errorsP.add(dJacobianRelative[i][j]); + } else { + errorsV.add(dJacobianRelative[i][j]); + } } } // Print values in console ? @@ -411,8 +409,8 @@ public double[] value(final SpacecraftState state) { propagator.propagate(measurements.get(measurements.size()-1).getDate()); // Convert lists to double[] and evaluate some statistics - final double relErrorsP[] = errorsP.stream().mapToDouble(Double::doubleValue).toArray(); - final double relErrorsV[] = errorsV.stream().mapToDouble(Double::doubleValue).toArray(); + final double[] relErrorsP = errorsP.stream().mapToDouble(Double::doubleValue).toArray(); + final double[] relErrorsV = errorsV.stream().mapToDouble(Double::doubleValue).toArray(); final double errorsPMedian = new Median().evaluate(relErrorsP); final double errorsPMean = new Mean().evaluate(relErrorsP); @@ -474,7 +472,7 @@ void genericTestParameterDerivatives(final boolean printResults, EstimationTestUtils.createMeasurements(propagator, creator, 1.0, 3.0, 300.0); // List to store the results - final List relErrorList = new ArrayList(); + final List relErrorList = new ArrayList<>(); // Use a lambda function to implement "handleStep" function propagator.setStepHandler(interpolator -> { @@ -503,12 +501,12 @@ void genericTestParameterDerivatives(final boolean printResults, measurement.getSatellites().get(1).getClockOffsetDriver() }; - for (int i = 0; i < drivers.length; ++i) { - for (Span span = drivers[i].getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { - final double[] gradient = measurement.estimate(0, 0, states).getParameterDerivatives(drivers[i], span.getStart()); + for (final ParameterDriver driver : drivers) { + for (Span span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + final double[] gradient = measurement.estimate(0, 0, states).getParameterDerivatives(driver, span.getStart()); Assertions.assertEquals(1, measurement.getDimension()); Assertions.assertEquals(1, gradient.length); - + // Compute a reference value using finite differences final ParameterFunction dMkdP = Differentiation.differentiate(new ParameterFunction() { @@ -516,16 +514,16 @@ void genericTestParameterDerivatives(final boolean printResults, @Override public double value(final ParameterDriver parameterDriver, final AbsoluteDate date) { return measurement. - estimateWithoutDerivatives(0, 0, states). + estimateWithoutDerivatives(states). getEstimatedValue()[0]; } - }, 3, 20.0 * drivers[i].getScale()); - final double ref = dMkdP.value(drivers[i], span.getStart()); - + }, 3, 20.0 * driver.getScale()); + final double ref = dMkdP.value(driver, span.getStart()); + if (printResults) { System.out.format(Locale.US, "%10.3e %10.3e ", gradient[0]-ref, FastMath.abs((gradient[0]-ref)/ref)); } - + final double relError = FastMath.abs((ref-gradient[0])/ref); relErrorList.add(relError); // Assert.assertEquals(ref, gradient[0], 6.1e-5 * FastMath.abs(ref)); @@ -561,7 +559,7 @@ public double value(final ParameterDriver parameterDriver, final AbsoluteDate da propagator.propagate(measurements.get(measurements.size()-1).getDate()); // Convert error list to double[] - final double relErrors[] = relErrorList.stream().mapToDouble(Double::doubleValue).toArray(); + final double[] relErrors = relErrorList.stream().mapToDouble(Double::doubleValue).toArray(); // Compute statistics final double relErrorsMedian = new Median().evaluate(relErrors); @@ -589,7 +587,8 @@ public void testIssue734() { // Create a phase measurement. Remote is set to null since it not used by the test final InterSatellitesPhase phase = new InterSatellitesPhase(new ObservableSatellite(0), new ObservableSatellite(1), AbsoluteDate.J2000_EPOCH, 467614.701, Frequency.G01.getWavelength(), - 0.02, 1.0); + 0.02, 1.0, + new AmbiguityCache()); // First check Assertions.assertEquals(0.0, phase.getAmbiguityDriver().getValue(), Double.MIN_VALUE); @@ -604,7 +603,7 @@ public void testIssue734() { Assertions.assertTrue(phase.getAmbiguityDriver().isSelected()); for (ParameterDriver driver : phase.getParametersDrivers()) { // Verify if the current driver corresponds to the phase ambiguity - if (driver.getName() == Phase.AMBIGUITY_NAME) { + if (driver instanceof AmbiguityDriver) { Assertions.assertEquals(1234.0, phase.getAmbiguityDriver().getValue(), Double.MIN_VALUE); Assertions.assertTrue(phase.getAmbiguityDriver().isSelected()); } diff --git a/src/test/java/org/orekit/estimation/measurements/gnss/InterSatellitesWindUpTest.java b/src/test/java/org/orekit/estimation/measurements/gnss/InterSatellitesWindUpTest.java index a922ac663f..29947b0a1c 100644 --- a/src/test/java/org/orekit/estimation/measurements/gnss/InterSatellitesWindUpTest.java +++ b/src/test/java/org/orekit/estimation/measurements/gnss/InterSatellitesWindUpTest.java @@ -45,6 +45,7 @@ import org.orekit.gnss.SatelliteSystem; import org.orekit.gnss.attitude.GPSBlockIIA; import org.orekit.gnss.attitude.GPSBlockIIR; +import org.orekit.models.earth.troposphere.TroposphericModelUtils; import org.orekit.orbits.CartesianOrbit; import org.orekit.orbits.Orbit; import org.orekit.propagation.Propagator; @@ -80,7 +81,8 @@ public void testYawSteering() { new GeodeticPoint(FastMath.toRadians(55.0 + ( 1.0 + 10.0 / 60.0) / 60.0), FastMath.toRadians(82.0 + (55.0 + 22.0 / 60.0) / 60.0), 160.0), - "Новосибирск")), + "Новосибирск"), + TroposphericModelUtils.STANDARD_ATMOSPHERE_PROVIDER), -0.082134, 0.060814); } @@ -106,7 +108,8 @@ public void testMidnightTurn() { new GeodeticPoint(FastMath.toRadians( -(25.0 + 4.0 / 60.0)), FastMath.toRadians(-(130.0 + 6.0 / 60.0)), 0.0), - "Adamstown")), + "Adamstown"), + TroposphericModelUtils.STANDARD_ATMOSPHERE_PROVIDER), -0.961925, 0.360695); } @@ -128,7 +131,8 @@ public void testNoonTurn() { new GeodeticPoint(FastMath.toRadians( 19.0 + (49.0 + 20.0 / 60.0) / 60.0), FastMath.toRadians(-(155.0 + (28.0 + 30.0 / 60.0) / 60.0)), 4205.0), - "Mauna Kea")), + "Mauna Kea"), + TroposphericModelUtils.STANDARD_ATMOSPHERE_PROVIDER), 0.349123, 0.972542); } @@ -161,7 +165,8 @@ private void doTest(final Orbit emitterOrbit, final AttitudeProvider emitterAtti receiverSat, emitterSat, Frequency.G01.getWavelength(), 0.01 * Frequency.G01.getWavelength(), - 1.0); + 1.0, + new AmbiguityCache()); generator.addScheduler(new EventBasedScheduler<>(builder, new FixedStepSelector(60.0, TimeScalesFactory.getUTC()), generator.getPropagator(emitterSat), @@ -186,8 +191,7 @@ private void doTest(final Orbit emitterOrbit, final AttitudeProvider emitterAtti InterSatellitesPhase phase = (InterSatellitesPhase) m; @SuppressWarnings("unchecked") EstimatedMeasurementBase estimated = - (EstimatedMeasurementBase) m.estimateWithoutDerivatives(0, 0, - new SpacecraftState[] { + (EstimatedMeasurementBase) m.estimateWithoutDerivatives(new SpacecraftState[] { receiverPropagator.propagate(phase.getDate()), emitterPropagator.propagate(phase.getDate()) }); diff --git a/src/test/java/org/orekit/estimation/measurements/gnss/MeasurementCombinationFactoryTest.java b/src/test/java/org/orekit/estimation/measurements/gnss/MeasurementCombinationFactoryTest.java index 504ab7d74b..ec6dd82b8c 100644 --- a/src/test/java/org/orekit/estimation/measurements/gnss/MeasurementCombinationFactoryTest.java +++ b/src/test/java/org/orekit/estimation/measurements/gnss/MeasurementCombinationFactoryTest.java @@ -31,7 +31,7 @@ import org.orekit.files.rinex.observation.ObservationData; import org.orekit.files.rinex.observation.ObservationDataSet; import org.orekit.files.rinex.observation.RinexObservationParser; -import org.orekit.gnss.Frequency; +import org.orekit.gnss.GnssSignal; import org.orekit.gnss.MeasurementType; import org.orekit.gnss.ObservationType; import org.orekit.gnss.SatInSystem; @@ -41,7 +41,7 @@ public class MeasurementCombinationFactoryTest { /** Threshold for test acceptance. */ - private static double eps = 1.0e-4; + private static final double eps = 1.0e-4; /** First observation data. */ private ObservationData obs1; @@ -124,8 +124,9 @@ private void doTestEmptyDataSet(final MeasurementCombination combination) { final ObservationDataSet emptyDataSet = new ObservationDataSet(new SatInSystem(dataSetRinex2.getSatellite().getSystem(), dataSetRinex2.getSatellite() .getPRN()), - dataSetRinex2.getDate(), 0, dataSetRinex2.getRcvrClkOffset(), - new ArrayList()); + dataSetRinex2.getDate(), 0, + dataSetRinex2.getRcvrClkOffset(), + new ArrayList<>()); // Test first method signature final CombinedObservationDataSet combinedData = combination.combine(emptyDataSet); Assertions.assertEquals(0, combinedData.getObservationData().size()); @@ -213,25 +214,25 @@ public void testRinex2GeometryFree() { @Test public void testRinex2IonoFree() { doTestRinexDualFrequency(MeasurementCombinationFactory.getIonosphereFreeCombination(system), - CombinationType.IONO_FREE, 23732467.5026, 3772223175.669, 0.0, 4658 * Frequency.F0, 2, 2); + CombinationType.IONO_FREE, 23732467.5026, 3772223175.669, 0.0, 4658 * GnssSignal.F0, 2, 2); } @Test public void testRinex2WideLane() { doTestRinexDualFrequency(MeasurementCombinationFactory.getWideLaneCombination(system), - CombinationType.WIDE_LANE, 23732453.7100, 27534453.519, 0.0, 34 * Frequency.F0, 2, 2); + CombinationType.WIDE_LANE, 23732453.7100, 27534453.519, 0.0, 34 * GnssSignal.F0, 2, 2); } @Test public void testRinex2NarrowLane() { doTestRinexDualFrequency(MeasurementCombinationFactory.getNarrowLaneCombination(system), - CombinationType.NARROW_LANE, 23732481.2951, 221895659.955, 0.0, 274 * Frequency.F0, 2, 2); + CombinationType.NARROW_LANE, 23732481.2951, 221895659.955, 0.0, 274 * GnssSignal.F0, 2, 2); } @Test public void testRinex2MelbourneWubbena() { doTestRinexDualFrequency(MeasurementCombinationFactory.getMelbourneWubbenaCombination(system), - CombinationType.MELBOURNE_WUBBENA, 0.0, 0.0, 3801972.2239, 34 * Frequency.F0, 1, 2); + CombinationType.MELBOURNE_WUBBENA, 0.0, 0.0, 3801972.2239, 34 * GnssSignal.F0, 1, 2); } @Test @@ -256,7 +257,13 @@ private void doTestRinex2SingleFrequency(final MeasurementCombination combinatio final List data = combinedDataSet.getObservationData(); // L2/P2 Assertions.assertEquals(expectedL2P2, data.get(0).getValue(), eps); - Assertions.assertEquals(120 * Frequency.F0, data.get(0).getCombinedMHzFrequency(), eps); + Assertions.assertEquals(120 * GnssSignal.F0, data.get(0).getCombinedFrequency(), eps); + + // test deprecated method + Assertions.assertEquals(120 * GnssSignal.F0 / AbstractDualFrequencyCombination.MHZ_TO_HZ, + data.get(0).getCombinedMHzFrequency(), + eps); + } @Test @@ -268,25 +275,25 @@ public void testRinex3GeometryFree() { @Test public void testRinex3IonoFree() { doTestRinexDualFrequency(MeasurementCombinationFactory.getIonosphereFreeCombination(system), - CombinationType.IONO_FREE, 22399214.1934, 179620369.206, 0.0, 235 * Frequency.F0, 2, 3); + CombinationType.IONO_FREE, 22399214.1934, 179620369.206, 0.0, 235 * GnssSignal.F0, 2, 3); } @Test public void testRinex3WideLane() { doTestRinexDualFrequency(MeasurementCombinationFactory.getWideLaneCombination(system), - CombinationType.WIDE_LANE, 22399239.8790, 3821708.096, 0.0, 5 * Frequency.F0, 2, 3); + CombinationType.WIDE_LANE, 22399239.8790, 3821708.096, 0.0, 5 * GnssSignal.F0, 2, 3); } @Test public void testRinex3NarrowLane() { doTestRinexDualFrequency(MeasurementCombinationFactory.getNarrowLaneCombination(system), - CombinationType.NARROW_LANE, 22399188.5078, 179620457.900, 0.0, 235 * Frequency.F0, 2, 3); + CombinationType.NARROW_LANE, 22399188.5078, 179620457.900, 0.0, 235 * GnssSignal.F0, 2, 3); } @Test public void testRinex3MelbourneWubbena() { doTestRinexDualFrequency(MeasurementCombinationFactory.getMelbourneWubbenaCombination(system), - CombinationType.MELBOURNE_WUBBENA, 0.0, 0.0, -18577480.4117, 5 * Frequency.F0, 1, 3); + CombinationType.MELBOURNE_WUBBENA, 0.0, 0.0, -18577480.4117, 5 * GnssSignal.F0, 1, 3); } @Test @@ -312,17 +319,17 @@ private void doTestRinex3SingleFrequency(final MeasurementCombination combinatio // Verify the combined observation data final List data = combinedDataSet.getObservationData(); // L1C/C1C - Assertions.assertEquals(expected1C, data.get(0).getValue(), eps); - Assertions.assertEquals(154 * Frequency.F0, data.get(0).getCombinedMHzFrequency(), eps); + Assertions.assertEquals(expected1C, data.get(0).getValue(), eps); + Assertions.assertEquals(154 * GnssSignal.F0, data.get(0).getCombinedFrequency(), eps); // L2W/C2W - Assertions.assertEquals(expected2W, data.get(1).getValue(), eps); - Assertions.assertEquals(120 * Frequency.F0, data.get(1).getCombinedMHzFrequency(), eps); + Assertions.assertEquals(expected2W, data.get(1).getValue(), eps); + Assertions.assertEquals(120 * GnssSignal.F0, data.get(1).getCombinedFrequency(), eps); // L2X/C2X - Assertions.assertEquals(expected2X, data.get(2).getValue(), eps); - Assertions.assertEquals(120 * Frequency.F0, data.get(1).getCombinedMHzFrequency(), eps); + Assertions.assertEquals(expected2X, data.get(2).getValue(), eps); + Assertions.assertEquals(120 * GnssSignal.F0, data.get(1).getCombinedFrequency(), eps); // L5X/C5X - Assertions.assertEquals(expected5X, data.get(3).getValue(), eps); - Assertions.assertEquals(115 * Frequency.F0, data.get(3).getCombinedMHzFrequency(), eps); + Assertions.assertEquals(expected5X, data.get(3).getValue(), eps); + Assertions.assertEquals(115 * GnssSignal.F0, data.get(3).getCombinedFrequency(), eps); } /** @@ -350,19 +357,19 @@ private void doTestRinexDualFrequency(final MeasurementCombination combination, if (cod.getMeasurementType() == MeasurementType.CARRIER_PHASE) { Assertions.assertEquals(expectedPhaseValue, cod.getValue(), eps); - Assertions.assertEquals(expectedFrequency, cod.getCombinedMHzFrequency(), eps); + Assertions.assertEquals(expectedFrequency, cod.getCombinedFrequency(), eps); Assertions.assertEquals(expectedType, cod.getCombinationType()); } else if (cod.getMeasurementType() == MeasurementType.PSEUDO_RANGE) { Assertions.assertEquals(expectedRangeValue, cod.getValue(), eps); - Assertions.assertEquals(expectedFrequency, cod.getCombinedMHzFrequency(), eps); + Assertions.assertEquals(expectedFrequency, cod.getCombinedFrequency(), eps); Assertions.assertEquals(expectedType, cod.getCombinationType()); } else if (cod.getMeasurementType() == MeasurementType.COMBINED_RANGE_PHASE) { Assertions.assertEquals(expectedRangePhase, cod.getValue(), eps); - Assertions.assertEquals(expectedFrequency, cod.getCombinedMHzFrequency(), eps); + Assertions.assertEquals(expectedFrequency, cod.getCombinedFrequency(), eps); Assertions.assertEquals(expectedType, cod.getCombinationType()); } @@ -398,7 +405,7 @@ public void testIssue746() { final CombinedObservationData combined = ionoFree.combine(obs1, obs2); // Combine data - final double wavelength = Constants.SPEED_OF_LIGHT / (combined.getCombinedMHzFrequency() * 1.0e6); + final double wavelength = Constants.SPEED_OF_LIGHT / combined.getCombinedFrequency(); final double combineValueMeters = combined.getValue() * wavelength; // Verify diff --git a/src/test/java/org/orekit/estimation/measurements/gnss/OneWayGNSSPhaseCreator.java b/src/test/java/org/orekit/estimation/measurements/gnss/OneWayGNSSPhaseCreator.java index 6053898eb9..bb2811e3f0 100644 --- a/src/test/java/org/orekit/estimation/measurements/gnss/OneWayGNSSPhaseCreator.java +++ b/src/test/java/org/orekit/estimation/measurements/gnss/OneWayGNSSPhaseCreator.java @@ -16,14 +16,13 @@ */ package org.orekit.estimation.measurements.gnss; -import org.hipparchus.analysis.UnivariateFunction; import org.hipparchus.analysis.solvers.BracketingNthOrderBrentSolver; import org.hipparchus.analysis.solvers.UnivariateSolver; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.orekit.errors.OrekitException; import org.orekit.estimation.measurements.MeasurementCreator; import org.orekit.estimation.measurements.ObservableSatellite; -import org.orekit.estimation.measurements.modifiers.OneWayGNSSPhaseAmbiguityModifier; +import org.orekit.estimation.measurements.QuadraticClockModel; import org.orekit.gnss.Frequency; import org.orekit.propagation.BoundedPropagator; import org.orekit.propagation.SpacecraftState; @@ -33,22 +32,27 @@ public class OneWayGNSSPhaseCreator extends MeasurementCreator { private final BoundedPropagator ephemeris; - private final double remoteClk; + private final String remoteName; + private final QuadraticClockModel remoteClk; private final double wavelength; - private final OneWayGNSSPhaseAmbiguityModifier ambiguity; private final Vector3D antennaPhaseCenter1; private final Vector3D antennaPhaseCenter2; private final ObservableSatellite local; + private final AmbiguityCache cache; + private final AmbiguityDriver ambiguityDriver; public OneWayGNSSPhaseCreator(final BoundedPropagator ephemeris, + final String remoteName, final Frequency frequency, final int ambiguity, final double localClockOffset, final double remoteClockOffset) { - this(ephemeris, frequency, ambiguity, localClockOffset, remoteClockOffset, Vector3D.ZERO, Vector3D.ZERO); + this(ephemeris, remoteName, frequency, ambiguity, + localClockOffset, remoteClockOffset, Vector3D.ZERO, Vector3D.ZERO); } public OneWayGNSSPhaseCreator(final BoundedPropagator ephemeris, + final String remoteName, final Frequency frequency, final int ambiguity, final double localClockOffset, @@ -56,13 +60,20 @@ public OneWayGNSSPhaseCreator(final BoundedPropagator ephemeris, final Vector3D antennaPhaseCenter1, final Vector3D antennaPhaseCenter2) { this.ephemeris = ephemeris; - this.remoteClk = remoteClockOffset; + this.remoteName = remoteName; + this.remoteClk = new QuadraticClockModel(ephemeris.getMinDate(), + remoteClockOffset, 0.0, 0.0); this.antennaPhaseCenter1 = antennaPhaseCenter1; this.antennaPhaseCenter2 = antennaPhaseCenter2; this.wavelength = frequency.getWavelength(); - this.ambiguity = new OneWayGNSSPhaseAmbiguityModifier(0, ambiguity); this.local = new ObservableSatellite(0); this.local.getClockOffsetDriver().setValue(localClockOffset); + this.cache = new AmbiguityCache(); + this.ambiguityDriver = cache. + getAmbiguity(remoteName, + "sat-" + local.getPropagatorIndex(), + wavelength); + ambiguityDriver.setValue(ambiguity); } public ObservableSatellite getLocalSatellite() { @@ -77,24 +88,23 @@ public void init(final SpacecraftState s0, final AbsoluteDate t, final double st public void handleStep(final SpacecraftState currentState) { try { - final double n = ambiguity.getParametersDrivers().get(0).getValue(currentState.getDate()); + final double n = ambiguityDriver.getValue(currentState.getDate()); final double localClk = local.getClockOffsetDriver().getValue(currentState.getDate()); - final double deltaD = Constants.SPEED_OF_LIGHT * (localClk - remoteClk); + final double deltaD = Constants.SPEED_OF_LIGHT * (localClk - + remoteClk.getOffset(currentState.getDate()).getOffset()); final AbsoluteDate date = currentState.getDate(); - final Vector3D position = currentState.toTransform().toStaticTransform().getInverse().transformPosition(antennaPhaseCenter1); + final Vector3D position = currentState.toStaticTransform().getInverse().transformPosition(antennaPhaseCenter1); final UnivariateSolver solver = new BracketingNthOrderBrentSolver(1.0e-12, 5); - final double downLinkDelay = solver.solve(1000, new UnivariateFunction() { - public double value(final double x) { - final Vector3D other = ephemeris. - propagate(date.shiftedBy(-x)). - toTransform(). - getInverse(). - transformPosition(antennaPhaseCenter2); - final double d = Vector3D.distance(position, other); - return d - x * Constants.SPEED_OF_LIGHT; - } + final double downLinkDelay = solver.solve(1000, x -> { + final Vector3D other = ephemeris. + propagate(date.shiftedBy(-x)). + toTransform(). + getInverse(). + transformPosition(antennaPhaseCenter2); + final double d = Vector3D.distance(position, other); + return d - x * Constants.SPEED_OF_LIGHT; }, -1.0, 1.0); final AbsoluteDate transitDate = currentState.getDate().shiftedBy(-downLinkDelay); final Vector3D otherAtTransit = @@ -105,8 +115,11 @@ public double value(final double x) { final double downLinkDistance = Vector3D.distance(position, otherAtTransit); // Generate measurement - final OneWayGNSSPhase gnssPhase = new OneWayGNSSPhase(ephemeris, remoteClk, date.shiftedBy(localClk), (downLinkDistance + deltaD) / wavelength + n, wavelength, 1.0, 10.0, local); - gnssPhase.addModifier(ambiguity); + final OneWayGNSSPhase gnssPhase = new OneWayGNSSPhase(ephemeris, remoteName, + remoteClk, date.shiftedBy(localClk), + (downLinkDistance + deltaD) / wavelength + n, wavelength, + 1.0, 10.0, local, + cache); addMeasurement(gnssPhase); } catch (OrekitException oe) { diff --git a/src/test/java/org/orekit/estimation/measurements/gnss/OneWayGNSSPhaseTest.java b/src/test/java/org/orekit/estimation/measurements/gnss/OneWayGNSSPhaseTest.java index ca77900cbe..c801552299 100644 --- a/src/test/java/org/orekit/estimation/measurements/gnss/OneWayGNSSPhaseTest.java +++ b/src/test/java/org/orekit/estimation/measurements/gnss/OneWayGNSSPhaseTest.java @@ -50,7 +50,6 @@ import org.orekit.utils.Differentiation; import org.orekit.utils.ParameterDriver; import org.orekit.utils.ParameterFunction; -import org.orekit.utils.StateFunction; import org.orekit.utils.TimeSpanMap.Span; import org.orekit.utils.TimeStampedPVCoordinates; @@ -84,12 +83,19 @@ public void testStateDerivatives() { System.out.println("\nTest One-way GNSS phase State Derivatives - Finite Differences Comparison\n"); } // Run test - double refErrorsPMedian = 1.9e-10; - double refErrorsPMean = 5.3e-10; - double refErrorsPMax = 3.7e-08; - double refErrorsVMedian = 4.7e-04; - double refErrorsVMean = 1.6e-03; - double refErrorsVMax = 1.1e-01; + // the following relative tolerances for derivatives with respect to velocity + // may seem high, but they have been validated. The partial derivative of + // signal flight time with respect to velocity ∂τ/∂{vx, vy, vz} is about 10⁻¹³ + // when the signal flight time τ is about 10⁻⁴, so finite differences lose + // about 9 significant figures, so it is expected that partial derivatives + // computed with finite differences will only have a few digits corrects and + // that there will be outliers + double refErrorsPMedian = 5.6e-09; + double refErrorsPMean = 2.1e-08; + double refErrorsPMax = 1.1e-06; + double refErrorsVMedian = 1.7e-04; + double refErrorsVMean = 5.1e-04; + double refErrorsVMax = 4.2e-02; this.genericTestStateDerivatives(printResults, 0, refErrorsPMedian, refErrorsPMean, refErrorsPMax, refErrorsVMedian, refErrorsVMean, refErrorsVMax); @@ -109,9 +115,9 @@ public void testParameterDerivatives() { System.out.println("\nTest One-way GNSS phase Derivatives - Finite Differences Comparison\n"); } // Run test - double refErrorsMedian = 1.0e-15; - double refErrorsMean = 1.0e-15; - double refErrorsMax = 1.0e-15; + double refErrorsMedian = 5.8e-15; + double refErrorsMean = 8.5e-15; + double refErrorsMax = 3.2e-14; this.genericTestParameterDerivatives(printResults, refErrorsMedian, refErrorsMean, refErrorsMax); @@ -149,13 +155,15 @@ void genericTestValues(final boolean printResults) { final double remoteClockOffset = 469.0e-6; final List> measurements = EstimationTestUtils.createMeasurements(propagator, - new OneWayGNSSPhaseCreator(ephemeris, FREQUENCY, ambiguity, localClockOffset, remoteClockOffset), + new OneWayGNSSPhaseCreator(ephemeris, "remote", + FREQUENCY, ambiguity, + localClockOffset, remoteClockOffset), 1.0, 3.0, 300.0); // Lists for results' storage - Used only for derivatives with respect to state // "final" value to be seen by "handleStep" function of the propagator - final List absoluteErrors = new ArrayList(); - final List relativeErrors = new ArrayList(); + final List absoluteErrors = new ArrayList<>(); + final List relativeErrors = new ArrayList<>(); // Use a lambda function to implement "handleStep" function propagator.setStepHandler(interpolator -> { @@ -177,8 +185,7 @@ void genericTestValues(final boolean printResults) { // Values of the phase & errors final double phaseObserved = measurement.getObservedValue()[0]; - final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(0, 0, - new SpacecraftState[] { + final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(new SpacecraftState[] { state, ephemeris.propagate(state.getDate()) }); @@ -241,11 +248,11 @@ void genericTestValues(final boolean printResults) { System.out.println("Relative errors max : " + relErrorsMax); } - Assertions.assertEquals(0.0, absErrorsMedian, 6.5e-7); - Assertions.assertEquals(0.0, absErrorsMin, 3.1e-6); - Assertions.assertEquals(0.0, absErrorsMax, 9.0e-7); - Assertions.assertEquals(0.0, relErrorsMedian, 5.9e-12); - Assertions.assertEquals(0.0, relErrorsMax, 1.4e-10); + Assertions.assertEquals(0.0, absErrorsMedian, 6.7e-7); + Assertions.assertEquals(0.0, absErrorsMin, 3.2e-6); + Assertions.assertEquals(0.0, absErrorsMax, 5.7e-7); + Assertions.assertEquals(0.0, relErrorsMedian, 5.4e-12); + Assertions.assertEquals(0.0, relErrorsMax, 1.6e-10); // Test measurement type Assertions.assertEquals(OneWayGNSSPhase.MEASUREMENT_TYPE, measurements.get(0).getMeasurementType()); @@ -281,13 +288,15 @@ void genericTestStateDerivatives(final boolean printResults, final int index, final double remoteClockOffset = 469.0e-6; final List> measurements = EstimationTestUtils.createMeasurements(propagator, - new OneWayGNSSPhaseCreator(ephemeris, FREQUENCY, ambiguity, localClockOffset, remoteClockOffset), + new OneWayGNSSPhaseCreator(ephemeris, "remote", + FREQUENCY, ambiguity, + localClockOffset, remoteClockOffset), 1.0, 3.0, 300.0); // Lists for results' storage - Used only for derivatives with respect to state // "final" value to be seen by "handleStep" function of the propagator - final List errorsP = new ArrayList(); - final List errorsV = new ArrayList(); + final List errorsP = new ArrayList<>(); + final List errorsV = new ArrayList<>(); // Use a lambda function to implement "handleStep" function propagator.setStepHandler(interpolator -> { @@ -316,14 +325,12 @@ void genericTestStateDerivatives(final boolean printResults, final int index, final double[][] jacobianRef; // Compute a reference value using finite differences - jacobianRef = Differentiation.differentiate(new StateFunction() { - public double[] value(final SpacecraftState state) { - final SpacecraftState[] s = states.clone(); - s[index] = state; - return measurement.estimateWithoutDerivatives(0, 0, s).getEstimatedValue(); - } + jacobianRef = Differentiation.differentiate(state -> { + final SpacecraftState[] s = states.clone(); + s[index] = state; + return measurement.estimateWithoutDerivatives(s).getEstimatedValue(); }, measurement.getDimension(), propagator.getAttitudeProvider(), - OrbitType.CARTESIAN, PositionAngleType.TRUE, 2.0, 3).value(states[index]); + OrbitType.CARTESIAN, PositionAngleType.TRUE, 8.0, 5).value(states[index]); Assertions.assertEquals(jacobianRef.length, jacobian.length); Assertions.assertEquals(jacobianRef[0].length, jacobian[0].length); @@ -380,8 +387,8 @@ public double[] value(final SpacecraftState state) { propagator.propagate(measurements.get(measurements.size()-1).getDate()); // Convert lists to double[] and evaluate some statistics - final double relErrorsP[] = errorsP.stream().mapToDouble(Double::doubleValue).toArray(); - final double relErrorsV[] = errorsV.stream().mapToDouble(Double::doubleValue).toArray(); + final double[] relErrorsP = errorsP.stream().mapToDouble(Double::doubleValue).toArray(); + final double[] relErrorsV = errorsV.stream().mapToDouble(Double::doubleValue).toArray(); final double errorsPMedian = new Median().evaluate(relErrorsP); final double errorsPMean = new Mean().evaluate(relErrorsP); @@ -432,7 +439,8 @@ void genericTestParameterDerivatives(final boolean printResults, final int ambiguity = 1234; final double localClockOffset = 0.137e-6; final double remoteClockOffset = 469.0e-6; - final OneWayGNSSPhaseCreator creator = new OneWayGNSSPhaseCreator(ephemeris, FREQUENCY, ambiguity, localClockOffset, remoteClockOffset); + final OneWayGNSSPhaseCreator creator = new OneWayGNSSPhaseCreator(ephemeris, "remote", + FREQUENCY, ambiguity, localClockOffset, remoteClockOffset); creator.getLocalSatellite().getClockOffsetDriver().setSelected(true); final Propagator propagator = EstimationTestUtils.createPropagator(context.initialOrbit, @@ -441,7 +449,7 @@ void genericTestParameterDerivatives(final boolean printResults, EstimationTestUtils.createMeasurements(propagator, creator, 1.0, 3.0, 300.0); // List to store the results - final List relErrorList = new ArrayList(); + final List relErrorList = new ArrayList<>(); // Use a lambda function to implement "handleStep" function propagator.setStepHandler(interpolator -> { @@ -482,10 +490,10 @@ void genericTestParameterDerivatives(final boolean printResults, @Override public double value(final ParameterDriver parameterDriver, final AbsoluteDate date) { return measurement. - estimateWithoutDerivatives(0, 0, states). + estimateWithoutDerivatives(states). getEstimatedValue()[0]; } - }, 3, 20.0 * drivers[i].getScale()); + }, 5, 10.0 * drivers[i].getScale()); final double ref = dMkdP.value(drivers[i], date); if (printResults) { @@ -526,7 +534,7 @@ public double value(final ParameterDriver parameterDriver, final AbsoluteDate da propagator.propagate(measurements.get(measurements.size()-1).getDate()); // Convert error list to double[] - final double relErrors[] = relErrorList.stream().mapToDouble(Double::doubleValue).toArray(); + final double[] relErrors = relErrorList.stream().mapToDouble(Double::doubleValue).toArray(); // Compute statistics final double relErrorsMedian = new Median().evaluate(relErrors); @@ -568,7 +576,7 @@ public void testIssue734() { Assertions.assertTrue(phase.getAmbiguityDriver().isSelected()); for (ParameterDriver driver : phase.getParametersDrivers()) { // Verify if the current driver corresponds to the phase ambiguity - if (driver.getName() == Phase.AMBIGUITY_NAME) { + if (driver instanceof AmbiguityDriver) { Assertions.assertEquals(1234.0, phase.getAmbiguityDriver().getValue(), Double.MIN_VALUE); Assertions.assertTrue(phase.getAmbiguityDriver().isSelected()); } diff --git a/src/test/java/org/orekit/estimation/measurements/gnss/OneWayGNSSRangeCreator.java b/src/test/java/org/orekit/estimation/measurements/gnss/OneWayGNSSRangeCreator.java index 31bec8c587..8c41a8d53d 100644 --- a/src/test/java/org/orekit/estimation/measurements/gnss/OneWayGNSSRangeCreator.java +++ b/src/test/java/org/orekit/estimation/measurements/gnss/OneWayGNSSRangeCreator.java @@ -70,7 +70,7 @@ public void handleStep(final SpacecraftState currentState) { final double localClk = local.getClockOffsetDriver().getValue(currentState.getDate()); final double deltaD = Constants.SPEED_OF_LIGHT * (localClk - remoteClk); final AbsoluteDate date = currentState.getDate(); - final Vector3D position = currentState.toTransform().toStaticTransform().getInverse().transformPosition(antennaPhaseCenter1); + final Vector3D position = currentState.toStaticTransform().getInverse().transformPosition(antennaPhaseCenter1); final UnivariateSolver solver = new BracketingNthOrderBrentSolver(1.0e-12, 5); diff --git a/src/test/java/org/orekit/estimation/measurements/gnss/OneWayGNSSRangeRateCreator.java b/src/test/java/org/orekit/estimation/measurements/gnss/OneWayGNSSRangeRateCreator.java new file mode 100644 index 0000000000..e0afcc2b78 --- /dev/null +++ b/src/test/java/org/orekit/estimation/measurements/gnss/OneWayGNSSRangeRateCreator.java @@ -0,0 +1,130 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.estimation.measurements.gnss; + +import org.hipparchus.analysis.solvers.BracketingNthOrderBrentSolver; +import org.hipparchus.analysis.solvers.UnivariateSolver; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.orekit.errors.OrekitException; +import org.orekit.estimation.measurements.MeasurementCreator; +import org.orekit.estimation.measurements.ObservableSatellite; +import org.orekit.estimation.measurements.QuadraticClockModel; +import org.orekit.propagation.BoundedPropagator; +import org.orekit.propagation.SpacecraftState; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.ClockOffset; +import org.orekit.utils.Constants; +import org.orekit.utils.PVCoordinates; +import org.orekit.utils.ParameterDriver; + +import java.util.Arrays; + +public class OneWayGNSSRangeRateCreator + extends MeasurementCreator { + + private final BoundedPropagator ephemeris; + private final QuadraticClockModel remoteClk; + private final Vector3D antennaPhaseCenter1; + private final Vector3D antennaPhaseCenter2; + private final ObservableSatellite local; + + public OneWayGNSSRangeRateCreator(final BoundedPropagator ephemeris, + final double localClockOffset, + final double localClockRate, + final double localClockAcceleration, + final double remoteClockOffset, + final double remoteClockRate, + final double remoteClockAcceleration) { + this(ephemeris, + localClockOffset, localClockRate, localClockAcceleration, + remoteClockOffset, remoteClockRate, remoteClockAcceleration, + Vector3D.ZERO, Vector3D.ZERO); + } + + public OneWayGNSSRangeRateCreator(final BoundedPropagator ephemeris, + final double localClockOffset, + final double localClockRate, + final double localClockAcceleration, + final double remoteClockOffset, + final double remoteClockRate, + final double remoteClockAcceleration, + final Vector3D antennaPhaseCenter1, + final Vector3D antennaPhaseCenter2) { + this.ephemeris = ephemeris; + this.antennaPhaseCenter1 = antennaPhaseCenter1; + this.antennaPhaseCenter2 = antennaPhaseCenter2; + this.local = new ObservableSatellite(0); + this.local.getClockOffsetDriver().setValue(localClockOffset); + this.local.getClockDriftDriver().setValue(localClockRate); + this.local.getClockAccelerationDriver().setValue(localClockAcceleration); + this.remoteClk = new QuadraticClockModel(ephemeris.getMinDate(), + remoteClockOffset, + remoteClockRate, + remoteClockAcceleration); + } + + public ObservableSatellite getLocalSatellite() { + return local; + } + + public void init(final SpacecraftState s0, final AbsoluteDate t, final double step) { + for (final ParameterDriver driver : Arrays.asList(local.getClockOffsetDriver(), + local.getClockDriftDriver(), + local.getClockAccelerationDriver())) { + if (driver.getReferenceDate() == null) { + driver.setReferenceDate(s0.getDate()); + } + } + } + + public void handleStep(final SpacecraftState currentState) { + try { + final AbsoluteDate date = currentState.getDate(); + final PVCoordinates pv = currentState.toTransform().getInverse(). + transformPVCoordinates(new PVCoordinates(antennaPhaseCenter1)); + final ClockOffset localClk = local.getQuadraticClockModel().getOffset(date); + + final UnivariateSolver solver = new BracketingNthOrderBrentSolver(1.0e-12, 5); + + final double downLinkDelay = solver.solve(1000, x -> { + final Vector3D other = ephemeris. + propagate(date.shiftedBy(-x)). + toTransform(). + getInverse(). + transformPosition(antennaPhaseCenter2); + final double d = Vector3D.distance(pv.getPosition(), other); + return d - x * Constants.SPEED_OF_LIGHT; + }, -1.0, 1.0); + final AbsoluteDate transitDate = currentState.getDate().shiftedBy(-downLinkDelay); + final PVCoordinates otherAtTransit = ephemeris.propagate(transitDate). + toTransform(). + getInverse(). + transformPVCoordinates(new PVCoordinates(antennaPhaseCenter2)); + final PVCoordinates delta = new PVCoordinates(otherAtTransit, pv); + final double rangeRate = Vector3D.dotProduct(delta.getPosition().normalize(), delta.getVelocity()) + + Constants.SPEED_OF_LIGHT * (local.getQuadraticClockModel().getOffset(date).getRate() - + remoteClk.getOffset(transitDate).getRate()); + + // Generate measurement + addMeasurement(new OneWayGNSSRangeRate(ephemeris, remoteClk, date.shiftedBy(localClk.getOffset()), rangeRate, 1.0, 10, local)); + + } catch (OrekitException oe) { + throw new OrekitException(oe); + } + } + +} diff --git a/src/test/java/org/orekit/estimation/measurements/gnss/OneWayGNSSRangeRateTest.java b/src/test/java/org/orekit/estimation/measurements/gnss/OneWayGNSSRangeRateTest.java new file mode 100644 index 0000000000..0f5ea494dc --- /dev/null +++ b/src/test/java/org/orekit/estimation/measurements/gnss/OneWayGNSSRangeRateTest.java @@ -0,0 +1,563 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.estimation.measurements.gnss; + +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.stat.descriptive.moment.Mean; +import org.hipparchus.stat.descriptive.rank.Max; +import org.hipparchus.stat.descriptive.rank.Median; +import org.hipparchus.stat.descriptive.rank.Min; +import org.hipparchus.util.FastMath; +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.estimation.measurements.EstimatedMeasurementBase; +import org.orekit.estimation.measurements.ObservedMeasurement; +import org.orekit.orbits.CartesianOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; +import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.BoundedPropagator; +import org.orekit.propagation.EphemerisGenerator; +import org.orekit.propagation.Propagator; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.conversion.NumericalPropagatorBuilder; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.Constants; +import org.orekit.utils.Differentiation; +import org.orekit.utils.ParameterDriver; +import org.orekit.utils.ParameterFunction; +import org.orekit.utils.TimeSpanMap.Span; +import org.orekit.utils.TimeStampedPVCoordinates; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Locale; + +public class OneWayGNSSRangeRateTest { + + /** + * Test the values of the range rate comparing the observed values and the estimated values + * Both are calculated with a different algorithm + */ + @Test + public void testValues() { + boolean printResults = false; + if (printResults) { + System.out.println("\nTest One-way GNSS range rate Values\n"); + } + // Run test + this.genericTestValues(printResults); + } + + /** + * Test the values of the state derivatives using a numerical + * finite differences calculation as a reference + */ + @Test + public void testStateDerivatives() { + + boolean printResults = false; + if (printResults) { + System.out.println("\nTest One-way GNSS range rate State Derivatives - Finite Differences Comparison\n"); + } + // Run test + // the following relative tolerances for derivatives with respect to velocity + // may seem high, but they have been validated. The partial derivative of + // signal flight time with respect to velocity ∂τ/∂{vx, vy, vz} is about 10⁻¹³ + // when the signal flight time τ is about 10⁻⁴, so finite differences lose + // about 9 significant figures, so it is expected that partial derivatives + // computed with finite differences will only have a few digits corrects and + // that there will be outliers + double refErrorsPMedian = 5.6e-10; + double refErrorsPMean = 3.4e-09; + double refErrorsPMax = 6.8e-07; + double refErrorsVMedian = 6.6e-11; + double refErrorsVMean = 1.8e-10; + double refErrorsVMax = 7.1e-09; + this.genericTestStateDerivatives(printResults, 0, + refErrorsPMedian, refErrorsPMean, refErrorsPMax, + refErrorsVMedian, refErrorsVMean, refErrorsVMax); + } + + /** + * Test the values of the parameters' derivatives using a numerical + * finite differences calculation as a reference + */ + @Test + public void testParameterDerivatives() { + + // Print the results ? + boolean printResults = false; + + if (printResults) { + System.out.println("\nTest One-way GNSS range rate Derivatives - Finite Differences Comparison\n"); + } + // Run test + double refErrorsMedian = 6.7e-8; + double refErrorsMean = 2.1e-7; + double refErrorsMax = 2.8e-6; + this.genericTestParameterDerivatives(printResults, + refErrorsMedian, refErrorsMean, refErrorsMax); + + } + + /** + * Generic test function for values of the one-way GNSS range + * @param printResults Print the results ? + */ + void genericTestValues(final boolean printResults) { + + Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides"); + + final NumericalPropagatorBuilder propagatorBuilder = + context.createBuilder(OrbitType.KEPLERIAN, PositionAngleType.TRUE, true, + 1.0e-6, 60.0, 0.001); + + // Create perfect inter-satellites range rate measurements + final TimeStampedPVCoordinates original = context.initialOrbit.getPVCoordinates(); + final Orbit closeOrbit = new CartesianOrbit(new TimeStampedPVCoordinates(context.initialOrbit.getDate(), + original.getPosition().add(new Vector3D(1000, 2000, 3000)), + original.getVelocity().add(new Vector3D(-0.03, 0.01, 0.02))), + context.initialOrbit.getFrame(), + context.initialOrbit.getMu()); + final Propagator closePropagator = EstimationTestUtils.createPropagator(closeOrbit, + propagatorBuilder); + final EphemerisGenerator generator = closePropagator.getEphemerisGenerator(); + closePropagator.propagate(context.initialOrbit.getDate().shiftedBy(3.5 * closeOrbit.getKeplerianPeriod())); + final BoundedPropagator ephemeris = generator.getGeneratedEphemeris(); + final Propagator propagator = EstimationTestUtils.createPropagator(context.initialOrbit, + propagatorBuilder); + + final double localClockOffset = 0.14e-06; + final double localClockRate = -0.12e-10; + final double localClockAcceleration = 1.4e-13; + final double remoteClockOffset = 469.0e-06; + final double remoteClockRate = 33.0e-10; + final double remoteClockAcceleration = 0.5e-13; + final List> measurements = + EstimationTestUtils.createMeasurements(propagator, + new OneWayGNSSRangeRateCreator(ephemeris, + localClockOffset, localClockRate, localClockAcceleration, + remoteClockOffset, remoteClockRate, remoteClockAcceleration), + 1.0, 3.0, 300.0); + + // Lists for results' storage - Used only for derivatives with respect to state + // "final" value to be seen by "handleStep" function of the propagator + final List absoluteErrors = new ArrayList<>(); + final List relativeErrors = new ArrayList<>(); + + // Use a lambda function to implement "handleStep" function + propagator.setStepHandler(interpolator -> { + + for (final ObservedMeasurement measurement : measurements) { + + // Play test if the measurement date is between interpolator previous and current date + if ((measurement.getDate().durationFrom(interpolator.getPreviousState().getDate()) > 0.) && + (measurement.getDate().durationFrom(interpolator.getCurrentState().getDate()) <= 0.) + ) { + // We intentionally propagate to a date which is close to the + // real spacecraft state but is *not* the accurate date, by + // compensating only part of the downlink delay. This is done + // in order to validate the partial derivatives with respect + // to velocity. + final double meanDelay = measurement.getObservedValue()[0] / Constants.SPEED_OF_LIGHT; + final AbsoluteDate date = measurement.getDate().shiftedBy(-0.75 * meanDelay); + final SpacecraftState state = interpolator.getInterpolatedState(date); + + // Values of the range rate & errors + final double rangeObserved = measurement.getObservedValue()[0]; + final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(new SpacecraftState[] { + state, + ephemeris.propagate(state.getDate()) + }); + + final double rangeEstimated = estimated.getEstimatedValue()[0]; + final double absoluteError = rangeEstimated-rangeObserved; + absoluteErrors.add(absoluteError); + relativeErrors.add(FastMath.abs(absoluteError)/FastMath.abs(rangeObserved)); + + // Print results on console ? + if (printResults) { + final AbsoluteDate measurementDate = measurement.getDate(); + + System.out.format(Locale.US, "%-23s %-23s %19.6f %19.6f %13.6e %13.6e%n", + measurementDate, date, + rangeObserved, rangeEstimated, + FastMath.abs(rangeEstimated-rangeObserved), + FastMath.abs((rangeEstimated-rangeObserved)/rangeObserved)); + } + + } // End if measurement date between previous and current interpolator step + } // End for loop on the measurements + }); // End lambda function handlestep + + // Print results on console ? Header + if (printResults) { + System.out.format(Locale.US, "%-23s %-23s %19s %19s %19s %19s%n", + "Measurement Date", "State Date", + "range rate observed [m/s]", "range rate estimated [m/s]", + "Δrange rate[m/s]", "rel ΔRange rate"); + } + + // Rewind the propagator to initial date + propagator.propagate(context.initialOrbit.getDate()); + + // Sort measurements chronologically + measurements.sort(Comparator.naturalOrder()); + + // Propagate to final measurement's date + propagator.propagate(measurements.get(measurements.size()-1).getDate()); + + // Convert lists to double array + final double[] absErrors = absoluteErrors.stream().mapToDouble(Double::doubleValue).toArray(); + final double[] relErrors = relativeErrors.stream().mapToDouble(Double::doubleValue).toArray(); + + // Statistics' assertion + final double absErrorsMedian = new Median().evaluate(absErrors); + final double absErrorsMin = new Min().evaluate(absErrors); + final double absErrorsMax = new Max().evaluate(absErrors); + final double relErrorsMedian = new Median().evaluate(relErrors); + final double relErrorsMax = new Max().evaluate(relErrors); + + // Print the results on console ? Final results + if (printResults) { + System.out.println(); + System.out.println("Absolute errors median: " + absErrorsMedian); + System.out.println("Absolute errors min : " + absErrorsMin); + System.out.println("Absolute errors max : " + absErrorsMax); + System.out.println("Relative errors median: " + relErrorsMedian); + System.out.println("Relative errors max : " + relErrorsMax); + } + + Assertions.assertEquals(0.0, absErrorsMedian, 3.9e-9); + Assertions.assertEquals(0.0, absErrorsMin, 1.2e-11); + Assertions.assertEquals(0.0, absErrorsMax, 1.6e-8); + Assertions.assertEquals(0.0, relErrorsMedian, 1.1e-9); + Assertions.assertEquals(0.0, relErrorsMax, 1.1e-7); + + // Test measurement type + Assertions.assertEquals(OneWayGNSSRangeRate.MEASUREMENT_TYPE, measurements.get(0).getMeasurementType()); + } + + void genericTestStateDerivatives(final boolean printResults, final int index, + final double refErrorsPMedian, final double refErrorsPMean, final double refErrorsPMax, + final double refErrorsVMedian, final double refErrorsVMean, final double refErrorsVMax) { + + Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides"); + + final NumericalPropagatorBuilder propagatorBuilder = + context.createBuilder(OrbitType.KEPLERIAN, PositionAngleType.TRUE, true, + 1.0e-6, 60.0, 0.001); + + // Create perfect one-way GNSS range rate measurements + final TimeStampedPVCoordinates original = context.initialOrbit.getPVCoordinates(); + final Orbit closeOrbit = new CartesianOrbit(new TimeStampedPVCoordinates(context.initialOrbit.getDate(), + original.getPosition().add(new Vector3D(1000, 2000, 3000)), + original.getVelocity().add(new Vector3D(-0.03, 0.01, 0.02))), + context.initialOrbit.getFrame(), + context.initialOrbit.getMu()); + final Propagator closePropagator = EstimationTestUtils.createPropagator(closeOrbit, + propagatorBuilder); + final EphemerisGenerator generator = closePropagator.getEphemerisGenerator(); + closePropagator.propagate(context.initialOrbit.getDate().shiftedBy(3.5 * closeOrbit.getKeplerianPeriod())); + final BoundedPropagator ephemeris = generator.getGeneratedEphemeris(); + final Propagator propagator = EstimationTestUtils.createPropagator(context.initialOrbit, + propagatorBuilder); + + final double localClockOffset = 0.14e-06; + final double localClockRate = -0.12e-10; + final double localClockAcceleration = 1.4e-13; + final double remoteClockOffset = 469.0e-06; + final double remoteClockRate = 33.0e-10; + final double remoteClockAcceleration = 0.5e-13; + final List> measurements = + EstimationTestUtils.createMeasurements(propagator, + new OneWayGNSSRangeRateCreator(ephemeris, + localClockOffset, localClockRate, localClockAcceleration, + remoteClockOffset, remoteClockRate, remoteClockAcceleration), + 1.0, 3.0, 300.0); + + // Lists for results' storage - Used only for derivatives with respect to state + // "final" value to be seen by "handleStep" function of the propagator + final List errorsP = new ArrayList<>(); + final List errorsV = new ArrayList<>(); + + // Use a lambda function to implement "handleStep" function + propagator.setStepHandler(interpolator -> { + + for (final ObservedMeasurement measurement : measurements) { + + // Play test if the measurement date is between interpolator previous and current date + if ((measurement.getDate().durationFrom(interpolator.getPreviousState().getDate()) > 0.) && + (measurement.getDate().durationFrom(interpolator.getCurrentState().getDate()) <= 0.) + ) { + + // We intentionally propagate to a date which is close to the + // real spacecraft state but is *not* the accurate date, by + // compensating only part of the downlink delay. This is done + // in order to validate the partial derivatives with respect + // to velocity. + final double meanDelay = measurement.getObservedValue()[0] / Constants.SPEED_OF_LIGHT; + final AbsoluteDate date = measurement.getDate().shiftedBy(-0.9 * meanDelay); + final SpacecraftState[] states = { + interpolator.getInterpolatedState(date), + ephemeris.propagate(date) + }; + final double[][] jacobian = measurement.estimate(0, 0, states).getStateDerivatives(index); + + // Jacobian reference value + final double[][] jacobianRef; + + // Compute a reference value using finite differences + jacobianRef = Differentiation.differentiate(state -> { + final SpacecraftState[] s = states.clone(); + s[index] = state; + return measurement.estimateWithoutDerivatives(s).getEstimatedValue(); + }, measurement.getDimension(), propagator.getAttitudeProvider(), + OrbitType.CARTESIAN, PositionAngleType.TRUE, 8.0, 5).value(states[index]); + + Assertions.assertEquals(jacobianRef.length, jacobian.length); + Assertions.assertEquals(jacobianRef[0].length, jacobian[0].length); + + // Errors & relative errors on the Jacobian + double [][] dJacobian = new double[jacobian.length][jacobian[0].length]; + double [][] dJacobianRelative = new double[jacobian.length][jacobian[0].length]; + for (int i = 0; i < jacobian.length; ++i) { + for (int j = 0; j < jacobian[i].length; ++j) { + dJacobian[i][j] = jacobian[i][j] - jacobianRef[i][j]; + dJacobianRelative[i][j] = FastMath.abs(dJacobian[i][j]/jacobianRef[i][j]); + + if (j < 3) { errorsP.add(dJacobianRelative[i][j]); + } else { errorsV.add(dJacobianRelative[i][j]); } + } + } + // Print values in console ? + if (printResults) { + System.out.format(Locale.US, "%-23s %-23s " + + "%10.3e %10.3e %10.3e " + + "%10.3e %10.3e %10.3e " + + "%10.3e %10.3e %10.3e " + + "%10.3e %10.3e %10.3e%n", + measurement.getDate(), date, + dJacobian[0][0], dJacobian[0][1], dJacobian[0][2], + dJacobian[0][3], dJacobian[0][4], dJacobian[0][5], + dJacobianRelative[0][0], dJacobianRelative[0][1], dJacobianRelative[0][2], + dJacobianRelative[0][3], dJacobianRelative[0][4], dJacobianRelative[0][5]); + } + } // End if measurement date between previous and current interpolator step + } // End for loop on the measurements + }); + + // Print results on console ? + if (printResults) { + System.out.format(Locale.US, "%-23s %-23s " + + "%10s %10s %10s " + + "%10s %10s %10s " + + "%10s %10s %10s " + + "%10s %10s %10s%n", + "Measurement Date", "State Date", + "ΔdPx", "ΔdPy", "ΔdPz", "ΔdVx", "ΔdVy", "ΔdVz", + "rel ΔdPx", "rel ΔdPy", "rel ΔdPz", + "rel ΔdVx", "rel ΔdVy", "rel ΔdVz"); + } + + // Rewind the propagator to initial date + propagator.propagate(context.initialOrbit.getDate()); + + // Sort measurements, primarily chronologically + measurements.sort(Comparator.naturalOrder()); + + // Propagate to final measurement's date + propagator.propagate(measurements.get(measurements.size()-1).getDate()); + + // Convert lists to double[] and evaluate some statistics + final double[] relErrorsP = errorsP.stream().mapToDouble(Double::doubleValue).toArray(); + final double[] relErrorsV = errorsV.stream().mapToDouble(Double::doubleValue).toArray(); + + final double errorsPMedian = new Median().evaluate(relErrorsP); + final double errorsPMean = new Mean().evaluate(relErrorsP); + final double errorsPMax = new Max().evaluate(relErrorsP); + final double errorsVMedian = new Median().evaluate(relErrorsV); + final double errorsVMean = new Mean().evaluate(relErrorsV); + final double errorsVMax = new Max().evaluate(relErrorsV); + + // Print the results on console ? + if (printResults) { + System.out.println(); + System.out.format(Locale.US, "Relative errors dR/dP -> Median: %6.3e / Mean: %6.3e / Max: %6.3e%n", + errorsPMedian, errorsPMean, errorsPMax); + System.out.format(Locale.US, "Relative errors dR/dV -> Median: %6.3e / Mean: %6.3e / Max: %6.3e%n", + errorsVMedian, errorsVMean, errorsVMax); + } + + Assertions.assertEquals(0.0, errorsPMedian, refErrorsPMedian); + Assertions.assertEquals(0.0, errorsPMean, refErrorsPMean); + Assertions.assertEquals(0.0, errorsPMax, refErrorsPMax); + Assertions.assertEquals(0.0, errorsVMedian, refErrorsVMedian); + Assertions.assertEquals(0.0, errorsVMean, refErrorsVMean); + Assertions.assertEquals(0.0, errorsVMax, refErrorsVMax); + } + + void genericTestParameterDerivatives(final boolean printResults, + final double refErrorsMedian, final double refErrorsMean, final double refErrorsMax) { + + Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides"); + + final NumericalPropagatorBuilder propagatorBuilder = + context.createBuilder(OrbitType.KEPLERIAN, PositionAngleType.TRUE, true, + 1.0e-6, 60.0, 0.001); + + // Create perfect one-way GNSS range ratemeasurements + final TimeStampedPVCoordinates original = context.initialOrbit.getPVCoordinates(); + final Orbit closeOrbit = new CartesianOrbit(new TimeStampedPVCoordinates(context.initialOrbit.getDate(), + original.getPosition().add(new Vector3D(1000, 2000, 3000)), + original.getVelocity().add(new Vector3D(-0.03, 0.01, 0.02))), + context.initialOrbit.getFrame(), + context.initialOrbit.getMu()); + final Propagator closePropagator = EstimationTestUtils.createPropagator(closeOrbit, propagatorBuilder); + final EphemerisGenerator generator = closePropagator.getEphemerisGenerator(); + closePropagator.propagate(context.initialOrbit.getDate().shiftedBy(3.5 * closeOrbit.getKeplerianPeriod())); + final BoundedPropagator ephemeris = generator.getGeneratedEphemeris(); + + // Create perfect range ratemeasurements + final double localClockOffset = 0.14e-06; + final double localClockRate = -0.12e-10; + final double localClockAcceleration = 1.4e-13; + final double remoteClockOffset = 469.0e-06; + final double remoteClockRate = 33.0e-10; + final double remoteClockAcceleration = 0.5e-13; + final OneWayGNSSRangeRateCreator creator = new OneWayGNSSRangeRateCreator(ephemeris, + localClockOffset, localClockRate, localClockAcceleration, + remoteClockOffset, remoteClockRate, remoteClockAcceleration); + creator.getLocalSatellite().getClockOffsetDriver().setSelected(true); + + final Propagator propagator = EstimationTestUtils.createPropagator(context.initialOrbit, + propagatorBuilder); + final List> measurements = + EstimationTestUtils.createMeasurements(propagator, creator, 1.0, 3.0, 300.0); + + // List to store the results + final List relErrorList = new ArrayList<>(); + + // Use a lambda function to implement "handleStep" function + propagator.setStepHandler(interpolator -> { + + for (final ObservedMeasurement measurement : measurements) { + + // Play test if the measurement date is between interpolator previous and current date + if ((measurement.getDate().durationFrom(interpolator.getPreviousState().getDate()) > 0.) && + (measurement.getDate().durationFrom(interpolator.getCurrentState().getDate()) <= 0.)) { + + // We intentionally propagate to a date which is close to the + // real spacecraft state but is *not* the accurate date, by + // compensating only part of the downlink delay. This is done + // in order to validate the partial derivatives with respect + // to velocity. If we had chosen the proper state date, the + // range rate would have depended only on the current position but + // not on the current velocity. + final double meanDelay = measurement.getObservedValue()[0] / Constants.SPEED_OF_LIGHT; + final AbsoluteDate date = measurement.getDate().shiftedBy(-0.75 * meanDelay); + final SpacecraftState[] states = { + interpolator.getInterpolatedState(date), + ephemeris.propagate(date) + }; + final ParameterDriver[] drivers = new ParameterDriver[] { + measurement.getSatellites().get(0).getClockOffsetDriver(), + }; + + for (int i = 0; i < drivers.length; ++i) { + for (Span span = drivers[i].getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) { + + final double[] gradient = measurement.estimate(0, 0, states).getParameterDerivatives(drivers[i], span.getStart()); + Assertions.assertEquals(1, measurement.getDimension()); + Assertions.assertEquals(1, gradient.length); + + // Compute a reference value using finite differences + final ParameterFunction dMkdP = + Differentiation.differentiate(new ParameterFunction() { + /** {@inheritDoc} */ + @Override + public double value(final ParameterDriver parameterDriver, final AbsoluteDate date) { + return measurement. + estimateWithoutDerivatives(states). + getEstimatedValue()[0]; + } + }, 5, 10.0 * drivers[i].getScale()); + final double ref = dMkdP.value(drivers[i], date); + + if (printResults) { + System.out.format(Locale.US, "%10.3e %10.3e ", gradient[0]-ref, FastMath.abs((gradient[0]-ref)/ref)); + } + + final double relError = FastMath.abs((ref-gradient[0])/ref); + relErrorList.add(relError); + } + } + if (printResults) { + System.out.format(Locale.US, "%n"); + } + + } // End if measurement date between previous and current interpolator step + } // End for loop on the measurements + }); + + // Rewind the propagator to initial date + propagator.propagate(context.initialOrbit.getDate()); + + // Sort measurements chronologically + measurements.sort(Comparator.naturalOrder()); + + // Print results ? Header + if (printResults) { + System.out.format(Locale.US, "%-15s %-23s %-23s " + + "%10s %10s %10s %10s %10s %10s %10s %10s %10s %10s%n", + "Station", "Measurement Date", "State Date", + "Δt", "rel Δt", + "ΔdQx", "rel ΔdQx", + "ΔdQy", "rel ΔdQy", + "ΔdQz", "rel ΔdQz", + "Δtsat", "rel Δtsat"); + } + + // Propagate to final measurement's date + propagator.propagate(measurements.get(measurements.size()-1).getDate()); + + // Convert error list to double[] + final double[] relErrors = relErrorList.stream().mapToDouble(Double::doubleValue).toArray(); + + // Compute statistics + final double relErrorsMedian = new Median().evaluate(relErrors); + final double relErrorsMean = new Mean().evaluate(relErrors); + final double relErrorsMax = new Max().evaluate(relErrors); + + // Print the results on console ? + if (printResults) { + System.out.println(); + System.out.format(Locale.US, "Relative errors dR/dQ -> Median: %6.3e / Mean: %6.3e / Max: %6.3e%n", + relErrorsMedian, relErrorsMean, relErrorsMax); + } + + Assertions.assertEquals(0.0, relErrorsMedian, refErrorsMedian); + Assertions.assertEquals(0.0, relErrorsMean, refErrorsMean); + Assertions.assertEquals(0.0, relErrorsMax, refErrorsMax); + + } + +} diff --git a/src/test/java/org/orekit/estimation/measurements/gnss/OneWayGNSSRangeTest.java b/src/test/java/org/orekit/estimation/measurements/gnss/OneWayGNSSRangeTest.java index d1e4bb4a82..286ee7d82b 100644 --- a/src/test/java/org/orekit/estimation/measurements/gnss/OneWayGNSSRangeTest.java +++ b/src/test/java/org/orekit/estimation/measurements/gnss/OneWayGNSSRangeTest.java @@ -47,7 +47,6 @@ import org.orekit.utils.Differentiation; import org.orekit.utils.ParameterDriver; import org.orekit.utils.ParameterFunction; -import org.orekit.utils.StateFunction; import org.orekit.utils.TimeSpanMap.Span; import org.orekit.utils.TimeStampedPVCoordinates; @@ -79,12 +78,19 @@ public void testStateDerivatives() { System.out.println("\nTest One-way GNSS range State Derivatives - Finite Differences Comparison\n"); } // Run test - double refErrorsPMedian = 1.8e-10; - double refErrorsPMean = 5.1e-10; - double refErrorsPMax = 3.3e-08; - double refErrorsVMedian = 2.3e-03; - double refErrorsVMean = 6.7e-03; - double refErrorsVMax = 2.3e-01; + // the following relative tolerances for derivatives with respect to velocity + // may seem high, but they have been validated. The partial derivative of + // signal flight time with respect to velocity ∂τ/∂{vx, vy, vz} is about 10⁻¹³ + // when the signal flight time τ is about 10⁻⁴, so finite differences lose + // about 9 significant figures, so it is expected that partial derivatives + // computed with finite differences will only have a few digits corrects and + // that there will be outliers + double refErrorsPMedian = 5.6e-09; + double refErrorsPMean = 2.1e-08; + double refErrorsPMax = 1.1e-06; + double refErrorsVMedian = 6.4e-04; + double refErrorsVMean = 2.1e-03; + double refErrorsVMax = 6.8e-02; this.genericTestStateDerivatives(printResults, 0, refErrorsPMedian, refErrorsPMean, refErrorsPMax, refErrorsVMedian, refErrorsVMean, refErrorsVMax); @@ -104,9 +110,9 @@ public void testParameterDerivatives() { System.out.println("\nTest One-way GNSS range Derivatives - Finite Differences Comparison\n"); } // Run test - double refErrorsMedian = 1.0e-15; - double refErrorsMean = 1.0e-15; - double refErrorsMax = 1.0e-15; + double refErrorsMedian = 5.8e-15; + double refErrorsMean = 8.4e-15; + double refErrorsMax = 3.3e-14; this.genericTestParameterDerivatives(printResults, refErrorsMedian, refErrorsMean, refErrorsMax); @@ -148,8 +154,8 @@ void genericTestValues(final boolean printResults) { // Lists for results' storage - Used only for derivatives with respect to state // "final" value to be seen by "handleStep" function of the propagator - final List absoluteErrors = new ArrayList(); - final List relativeErrors = new ArrayList(); + final List absoluteErrors = new ArrayList<>(); + final List relativeErrors = new ArrayList<>(); // Use a lambda function to implement "handleStep" function propagator.setStepHandler(interpolator -> { @@ -171,8 +177,7 @@ void genericTestValues(final boolean printResults) { // Values of the range & errors final double rangeObserved = measurement.getObservedValue()[0]; - final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(0, 0, - new SpacecraftState[] { + final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(new SpacecraftState[] { state, ephemeris.propagate(state.getDate()) }); @@ -235,11 +240,11 @@ void genericTestValues(final boolean printResults) { System.out.println("Relative errors max : " + relErrorsMax); } - Assertions.assertEquals(0.0, absErrorsMedian, 1.3e-7); - Assertions.assertEquals(0.0, absErrorsMin, 6.5e-6); - Assertions.assertEquals(0.0, absErrorsMax, 1.7e-7); - Assertions.assertEquals(0.0, relErrorsMedian, 5.3e-12); - Assertions.assertEquals(0.0, relErrorsMax, 7.6e-11); + Assertions.assertEquals(0.0, absErrorsMedian, 1.4e-7); + Assertions.assertEquals(0.0, absErrorsMin, 6.6e-7); + Assertions.assertEquals(0.0, absErrorsMax, 1.5e-7); + Assertions.assertEquals(0.0, relErrorsMedian, 5.7e-12); + Assertions.assertEquals(0.0, relErrorsMax, 7.2e-11); // Test measurement type Assertions.assertEquals(OneWayGNSSRange.MEASUREMENT_TYPE, measurements.get(0).getMeasurementType()); @@ -279,8 +284,8 @@ void genericTestStateDerivatives(final boolean printResults, final int index, // Lists for results' storage - Used only for derivatives with respect to state // "final" value to be seen by "handleStep" function of the propagator - final List errorsP = new ArrayList(); - final List errorsV = new ArrayList(); + final List errorsP = new ArrayList<>(); + final List errorsV = new ArrayList<>(); // Use a lambda function to implement "handleStep" function propagator.setStepHandler(interpolator -> { @@ -298,7 +303,7 @@ void genericTestStateDerivatives(final boolean printResults, final int index, // in order to validate the partial derivatives with respect // to velocity. final double meanDelay = measurement.getObservedValue()[0] / Constants.SPEED_OF_LIGHT; - final AbsoluteDate date = measurement.getDate().shiftedBy(-0.75 * meanDelay); + final AbsoluteDate date = measurement.getDate().shiftedBy(-0.9 * meanDelay); final SpacecraftState[] states = { interpolator.getInterpolatedState(date), ephemeris.propagate(date) @@ -309,14 +314,12 @@ void genericTestStateDerivatives(final boolean printResults, final int index, final double[][] jacobianRef; // Compute a reference value using finite differences - jacobianRef = Differentiation.differentiate(new StateFunction() { - public double[] value(final SpacecraftState state) { - final SpacecraftState[] s = states.clone(); - s[index] = state; - return measurement.estimateWithoutDerivatives(0, 0, s).getEstimatedValue(); - } + jacobianRef = Differentiation.differentiate(state -> { + final SpacecraftState[] s = states.clone(); + s[index] = state; + return measurement.estimateWithoutDerivatives(s).getEstimatedValue(); }, measurement.getDimension(), propagator.getAttitudeProvider(), - OrbitType.CARTESIAN, PositionAngleType.TRUE, 2.0, 3).value(states[index]); + OrbitType.CARTESIAN, PositionAngleType.TRUE, 8.0, 5).value(states[index]); Assertions.assertEquals(jacobianRef.length, jacobian.length); Assertions.assertEquals(jacobianRef[0].length, jacobian[0].length); @@ -373,8 +376,8 @@ public double[] value(final SpacecraftState state) { propagator.propagate(measurements.get(measurements.size()-1).getDate()); // Convert lists to double[] and evaluate some statistics - final double relErrorsP[] = errorsP.stream().mapToDouble(Double::doubleValue).toArray(); - final double relErrorsV[] = errorsV.stream().mapToDouble(Double::doubleValue).toArray(); + final double[] relErrorsP = errorsP.stream().mapToDouble(Double::doubleValue).toArray(); + final double[] relErrorsV = errorsV.stream().mapToDouble(Double::doubleValue).toArray(); final double errorsPMedian = new Median().evaluate(relErrorsP); final double errorsPMean = new Mean().evaluate(relErrorsP); @@ -433,7 +436,7 @@ void genericTestParameterDerivatives(final boolean printResults, EstimationTestUtils.createMeasurements(propagator, creator, 1.0, 3.0, 300.0); // List to store the results - final List relErrorList = new ArrayList(); + final List relErrorList = new ArrayList<>(); // Use a lambda function to implement "handleStep" function propagator.setStepHandler(interpolator -> { @@ -475,10 +478,10 @@ void genericTestParameterDerivatives(final boolean printResults, @Override public double value(final ParameterDriver parameterDriver, final AbsoluteDate date) { return measurement. - estimateWithoutDerivatives(0, 0, states). + estimateWithoutDerivatives(states). getEstimatedValue()[0]; } - }, 3, 20.0 * drivers[i].getScale()); + }, 5, 10.0 * drivers[i].getScale()); final double ref = dMkdP.value(drivers[i], date); if (printResults) { @@ -519,7 +522,7 @@ public double value(final ParameterDriver parameterDriver, final AbsoluteDate da propagator.propagate(measurements.get(measurements.size()-1).getDate()); // Convert error list to double[] - final double relErrors[] = relErrorList.stream().mapToDouble(Double::doubleValue).toArray(); + final double[] relErrors = relErrorList.stream().mapToDouble(Double::doubleValue).toArray(); // Compute statistics final double relErrorsMedian = new Median().evaluate(relErrors); diff --git a/src/test/java/org/orekit/estimation/measurements/gnss/PhaseMeasurementCreator.java b/src/test/java/org/orekit/estimation/measurements/gnss/PhaseMeasurementCreator.java index 96fd2b620f..a50c51eb40 100644 --- a/src/test/java/org/orekit/estimation/measurements/gnss/PhaseMeasurementCreator.java +++ b/src/test/java/org/orekit/estimation/measurements/gnss/PhaseMeasurementCreator.java @@ -16,7 +16,6 @@ */ package org.orekit.estimation.measurements.gnss; -import org.hipparchus.analysis.UnivariateFunction; import org.hipparchus.analysis.solvers.BracketingNthOrderBrentSolver; import org.hipparchus.analysis.solvers.UnivariateSolver; import org.hipparchus.geometry.euclidean.threed.Vector3D; @@ -26,7 +25,6 @@ import org.orekit.estimation.measurements.GroundStation; import org.orekit.estimation.measurements.MeasurementCreator; import org.orekit.estimation.measurements.ObservableSatellite; -import org.orekit.estimation.measurements.modifiers.PhaseAmbiguityModifier; import org.orekit.frames.Frame; import org.orekit.frames.Transform; import org.orekit.gnss.Frequency; @@ -42,12 +40,13 @@ public class PhaseMeasurementCreator extends MeasurementCreator { private final Context context; private final double wavelength; - private final PhaseAmbiguityModifier ambiguity; + private final int ambiguity; private final Vector3D stationMeanPosition; private final PhaseCenterVariationFunction stationPhaseCenterVariation; private final Vector3D satelliteMeanPosition; private final PhaseCenterVariationFunction satellitePhaseCenterVariation; private final ObservableSatellite satellite; + private final AmbiguityCache cache; public PhaseMeasurementCreator(final Context context, final Frequency frequency, final int ambiguity, final double satClockOffset) { @@ -61,13 +60,14 @@ public PhaseMeasurementCreator(final Context context, final Frequency frequency, final Vector3D satelliteMeanPosition, final PhaseCenterVariationFunction satellitePhaseCenterVariation) { this.context = context; this.wavelength = frequency.getWavelength(); - this.ambiguity = new PhaseAmbiguityModifier(0, ambiguity); + this.ambiguity = ambiguity; this.stationMeanPosition = stationMeanPosition; this.stationPhaseCenterVariation = stationPhaseCenterVariation; this.satelliteMeanPosition = satelliteMeanPosition; this.satellitePhaseCenterVariation = satellitePhaseCenterVariation; this.satellite = new ObservableSatellite(0); this.satellite.getClockOffsetDriver().setValue(satClockOffset); + this.cache = new AmbiguityCache(); } public ObservableSatellite getSatellite() { @@ -100,21 +100,18 @@ public void init(SpacecraftState s0, AbsoluteDate t, double step) { public void handleStep(final SpacecraftState currentState) { try { - final double n = ambiguity.getParametersDrivers().get(0).getValue(); for (final GroundStation station : context.stations) { final AbsoluteDate date = currentState.getDate(); final Frame inertial = currentState.getFrame(); - final Vector3D position = currentState.toTransform().toStaticTransform().getInverse().transformPosition(satelliteMeanPosition); + final Vector3D position = currentState.toStaticTransform().getInverse().transformPosition(satelliteMeanPosition); if (station.getBaseFrame().getTrackingCoordinates(position, inertial, date).getElevation() > FastMath.toRadians(30.0)) { final UnivariateSolver solver = new BracketingNthOrderBrentSolver(1.0e-12, 5); - final double downLinkDelay = solver.solve(1000, new UnivariateFunction() { - public double value(final double x) { - final Transform t = station.getOffsetToInertial(inertial, date.shiftedBy(x), true); - final double d = Vector3D.distance(position, t.transformPosition(Vector3D.ZERO)); - return d - x * Constants.SPEED_OF_LIGHT; - } + final double downLinkDelay = solver.solve(1000, x -> { + final Transform t = station.getOffsetToInertial(inertial, date.shiftedBy(x), true); + final double d = Vector3D.distance(position, t.transformPosition(Vector3D.ZERO)); + return d - x * Constants.SPEED_OF_LIGHT; }, -1.0, 1.0); final AbsoluteDate receptionDate = currentState.getDate().shiftedBy(downLinkDelay); final Transform stationToInertReception = station.getOffsetToInertial(inertial, receptionDate, true); @@ -139,9 +136,10 @@ public double value(final double x) { final double correctedDownLinkDistance = downLinkDistance + satPCVDown + staPCVDown + (groundClk - satClk) * Constants.SPEED_OF_LIGHT; final Phase phase = new Phase(station, receptionDate.shiftedBy(groundClk), - correctedDownLinkDistance / wavelength + n, - wavelength, 1.0, 10, satellite); - phase.addModifier(ambiguity); + correctedDownLinkDistance / wavelength + ambiguity, + wavelength, 1.0, 10, satellite, + cache); + phase.getAmbiguityDriver().setValue(ambiguity); addMeasurement(phase); } diff --git a/src/test/java/org/orekit/estimation/measurements/gnss/PhaseTest.java b/src/test/java/org/orekit/estimation/measurements/gnss/PhaseTest.java index c611e7b3f3..f3753cbffd 100644 --- a/src/test/java/org/orekit/estimation/measurements/gnss/PhaseTest.java +++ b/src/test/java/org/orekit/estimation/measurements/gnss/PhaseTest.java @@ -44,8 +44,9 @@ import org.orekit.gnss.Frequency; import org.orekit.models.earth.ionosphere.IonosphericModel; import org.orekit.models.earth.ionosphere.KlobucharIonoModel; -import org.orekit.models.earth.troposphere.EstimatedTroposphericModel; +import org.orekit.models.earth.troposphere.EstimatedModel; import org.orekit.models.earth.troposphere.NiellMappingFunctionModel; +import org.orekit.models.earth.troposphere.TroposphericModelUtils; import org.orekit.orbits.OrbitType; import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.Propagator; @@ -57,7 +58,6 @@ import org.orekit.utils.IERSConventions; import org.orekit.utils.ParameterDriver; import org.orekit.utils.ParameterFunction; -import org.orekit.utils.StateFunction; import org.orekit.utils.TimeStampedPVCoordinates; public class PhaseTest { @@ -199,13 +199,12 @@ void genericTestValues(final boolean printResults) { // Values of the Phase & errors final double phaseObserved = measurement.getObservedValue()[0]; - final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(0, 0, - new SpacecraftState[] { state }); + final EstimatedMeasurementBase estimated = measurement.estimateWithoutDerivatives(new SpacecraftState[] { state }); final TimeStampedPVCoordinates[] participants = estimated.getParticipants(); Assertions.assertEquals(2, participants.length); final double dt = participants[1].getDate().durationFrom(participants[0].getDate()); - Assertions.assertEquals(1.0e6 * Frequency.E01.getMHzFrequency() * (dt + groundClockOffset - satClockOffset) + ambiguity, + Assertions.assertEquals(Frequency.E01.getFrequency() * (dt + groundClockOffset - satClockOffset) + ambiguity, estimated.getEstimatedValue()[0], 1.0e-7); @@ -307,8 +306,8 @@ void genericTestStateDerivatives(final boolean printResults, // Lists for results' storage - Used only for derivatives with respect to state // "final" value to be seen by "handleStep" function of the propagator - final List errorsP = new ArrayList(); - final List errorsV = new ArrayList(); + final List errorsP = new ArrayList<>(); + final List errorsV = new ArrayList<>(); // Use a lambda function to implement "handleStep" function propagator.setStepHandler(interpolator -> { @@ -335,14 +334,13 @@ void genericTestStateDerivatives(final boolean printResults, final double[][] jacobianRef; // Compute a reference value using finite differences - jacobianRef = Differentiation.differentiate(new StateFunction() { - public double[] value(final SpacecraftState state) { - return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). - getEstimatedValue(); - } - }, measurement.getDimension(), propagator.getAttitudeProvider(), - OrbitType.CARTESIAN, PositionAngleType.TRUE, 2.0, 3).value(state); + jacobianRef = Differentiation.differentiate( + state1 -> measurement. + estimateWithoutDerivatives(new SpacecraftState[] { + state1 + }). + getEstimatedValue(), measurement.getDimension(), propagator.getAttitudeProvider(), + OrbitType.CARTESIAN, PositionAngleType.TRUE, 2.0, 3).value(state); Assertions.assertEquals(jacobianRef.length, jacobian.length); Assertions.assertEquals(jacobianRef[0].length, jacobian[0].length); @@ -400,8 +398,8 @@ public double[] value(final SpacecraftState state) { propagator.propagate(measurements.get(measurements.size()-1).getDate()); // Convert lists to double[] and evaluate some statistics - final double relErrorsP[] = errorsP.stream().mapToDouble(Double::doubleValue).toArray(); - final double relErrorsV[] = errorsV.stream().mapToDouble(Double::doubleValue).toArray(); + final double[] relErrorsP = errorsP.stream().mapToDouble(Double::doubleValue).toArray(); + final double[] relErrorsV = errorsV.stream().mapToDouble(Double::doubleValue).toArray(); final double errorsPMedian = new Median().evaluate(relErrorsP); final double errorsPMean = new Mean().evaluate(relErrorsP); @@ -443,7 +441,8 @@ void genericTestParameterDerivatives(final boolean printResults, } final int ambiguity = 1234; final double satClockOffset = 345.0e-6; - final PhaseMeasurementCreator creator = new PhaseMeasurementCreator(context, Frequency.E01, + final PhaseMeasurementCreator creator = new PhaseMeasurementCreator(context, + Frequency.E01, ambiguity, satClockOffset); creator.getSatellite().getClockOffsetDriver().setSelected(true); @@ -459,7 +458,7 @@ void genericTestParameterDerivatives(final boolean printResults, EstimationTestUtils.createMeasurements(propagator, creator, 1.0, 3.0, 300.0); // List to store the results - final List relErrorList = new ArrayList(); + final List relErrorList = new ArrayList<>(); // Use a lambda function to implement "handleStep" function propagator.setStepHandler(interpolator -> { @@ -509,7 +508,7 @@ void genericTestParameterDerivatives(final boolean printResults, @Override public double value(final ParameterDriver parameterDriver, final AbsoluteDate date) { return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). + estimateWithoutDerivatives(new SpacecraftState[] { state }). getEstimatedValue()[0]; } }, 3, 20.0 * drivers[i].getScale()); @@ -553,7 +552,7 @@ public double value(final ParameterDriver parameterDriver, final AbsoluteDate da propagator.propagate(measurements.get(measurements.size()-1).getDate()); // Convert error list to double[] - final double relErrors[] = relErrorList.stream().mapToDouble(Double::doubleValue).toArray(); + final double[] relErrors = relErrorList.stream().mapToDouble(Double::doubleValue).toArray(); // Compute statistics final double relErrorsMedian = new Median().evaluate(relErrors); @@ -577,11 +576,11 @@ public double value(final ParameterDriver parameterDriver, final AbsoluteDate da public void testStateDerivativesWithTroposphericModifier() { final boolean printResults = false; - final double refErrorsPMedian = 5.9e-10; - final double refErrorsPMean = 4.3e-9; - final double refErrorsPMax = 3.8e-7; - final double refErrorsVMedian = 2.0e-5; - final double refErrorsVMean = 8.3e-5; + final double refErrorsPMedian = 6.0e-10; + final double refErrorsPMean = 2.9e-9; + final double refErrorsPMax = 1.1e-7; + final double refErrorsVMedian = 1.5e-5; + final double refErrorsVMean = 7.8e-5; final double refErrorsVMax = 4.6e-3; Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides"); @@ -609,8 +608,8 @@ public void testStateDerivativesWithTroposphericModifier() { // Lists for results' storage - Used only for derivatives with respect to state // "final" value to be seen by "handleStep" function of the propagator - final List errorsP = new ArrayList(); - final List errorsV = new ArrayList(); + final List errorsP = new ArrayList<>(); + final List errorsV = new ArrayList<>(); // Use a lambda function to implement "handleStep" function propagator.setStepHandler(interpolator -> { @@ -624,11 +623,11 @@ public void testStateDerivativesWithTroposphericModifier() { String stationName = ((Phase) measurement).getStation().getBaseFrame().getName(); // Add modifier - final NiellMappingFunctionModel mappingFunction = new NiellMappingFunctionModel(); - final EstimatedTroposphericModel tropoModel = new EstimatedTroposphericModel(mappingFunction, 5.0); - final PhaseTroposphericDelayModifier modifier = new PhaseTroposphericDelayModifier(tropoModel); - final List parameters = modifier.getParametersDrivers(); - parameters.get(0).setName(stationName + "/" + EstimatedTroposphericModel.TOTAL_ZENITH_DELAY); + final NiellMappingFunctionModel mappingFunction = new NiellMappingFunctionModel(); + final EstimatedModel tropoModel = new EstimatedModel(mappingFunction, 5.0); + final PhaseTroposphericDelayModifier modifier = new PhaseTroposphericDelayModifier(tropoModel); + final List parameters = modifier.getParametersDrivers(); + parameters.get(0).setName(stationName + "/" + EstimatedModel.TOTAL_ZENITH_DELAY); parameters.get(0).setSelected(true); ((Phase) measurement).addModifier(modifier); @@ -648,14 +647,13 @@ public void testStateDerivativesWithTroposphericModifier() { final double[][] jacobianRef; // Compute a reference value using finite differences - jacobianRef = Differentiation.differentiate(new StateFunction() { - public double[] value(final SpacecraftState state) { - return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). - getEstimatedValue(); - } - }, measurement.getDimension(), propagator.getAttitudeProvider(), - OrbitType.CARTESIAN, PositionAngleType.TRUE, 2.0, 3).value(state); + jacobianRef = Differentiation.differentiate( + state1 -> measurement. + estimateWithoutDerivatives(new SpacecraftState[] { + state1 + }). + getEstimatedValue(), measurement.getDimension(), propagator.getAttitudeProvider(), + OrbitType.CARTESIAN, PositionAngleType.TRUE, 2.0, 3).value(state); Assertions.assertEquals(jacobianRef.length, jacobian.length); Assertions.assertEquals(jacobianRef[0].length, jacobian[0].length); @@ -712,8 +710,8 @@ public double[] value(final SpacecraftState state) { propagator.propagate(measurements.get(measurements.size()-1).getDate()); // Convert lists to double[] and evaluate some statistics - final double relErrorsP[] = errorsP.stream().mapToDouble(Double::doubleValue).toArray(); - final double relErrorsV[] = errorsV.stream().mapToDouble(Double::doubleValue).toArray(); + final double[] relErrorsP = errorsP.stream().mapToDouble(Double::doubleValue).toArray(); + final double[] relErrorsV = errorsV.stream().mapToDouble(Double::doubleValue).toArray(); final double errorsPMedian = new Median().evaluate(relErrorsP); final double errorsPMean = new Mean().evaluate(relErrorsP); @@ -775,12 +773,12 @@ public void testStateDerivativesWithIonosphericModifier() { // Lists for results' storage - Used only for derivatives with respect to state // "final" value to be seen by "handleStep" function of the propagator - final List errorsP = new ArrayList(); - final List errorsV = new ArrayList(); + final List errorsP = new ArrayList<>(); + final List errorsV = new ArrayList<>(); final IonosphericModel model = new KlobucharIonoModel(new double[]{.3820e-07, .1490e-07, -.1790e-06, 0}, new double[]{.1430e+06, 0, -.3280e+06, .1130e+06}); - final double frequency = Frequency.G01.getMHzFrequency() * 1.0e6; + final double frequency = Frequency.G01.getFrequency(); final PhaseIonosphericDelayModifier modifier = new PhaseIonosphericDelayModifier(model, frequency); // Use a lambda function to implement "handleStep" function @@ -812,14 +810,13 @@ public void testStateDerivativesWithIonosphericModifier() { final double[][] jacobianRef; // Compute a reference value using finite differences - jacobianRef = Differentiation.differentiate(new StateFunction() { - public double[] value(final SpacecraftState state) { - return measurement. - estimateWithoutDerivatives(0, 0, new SpacecraftState[] { state }). - getEstimatedValue(); - } - }, measurement.getDimension(), propagator.getAttitudeProvider(), - OrbitType.CARTESIAN, PositionAngleType.TRUE, 2.0, 3).value(state); + jacobianRef = Differentiation.differentiate( + state1 -> measurement. + estimateWithoutDerivatives(new SpacecraftState[] { + state1 + }). + getEstimatedValue(), measurement.getDimension(), propagator.getAttitudeProvider(), + OrbitType.CARTESIAN, PositionAngleType.TRUE, 2.0, 3).value(state); Assertions.assertEquals(jacobianRef.length, jacobian.length); Assertions.assertEquals(jacobianRef[0].length, jacobian[0].length); @@ -876,8 +873,8 @@ public double[] value(final SpacecraftState state) { propagator.propagate(measurements.get(measurements.size()-1).getDate()); // Convert lists to double[] and evaluate some statistics - final double relErrorsP[] = errorsP.stream().mapToDouble(Double::doubleValue).toArray(); - final double relErrorsV[] = errorsV.stream().mapToDouble(Double::doubleValue).toArray(); + final double[] relErrorsP = errorsP.stream().mapToDouble(Double::doubleValue).toArray(); + final double[] relErrorsV = errorsV.stream().mapToDouble(Double::doubleValue).toArray(); final double errorsPMedian = new Median().evaluate(relErrorsP); final double errorsPMean = new Mean().evaluate(relErrorsP); @@ -914,7 +911,7 @@ public void testIssue734() { final TopocentricFrame topo = new TopocentricFrame(body, new GeodeticPoint(FastMath.toRadians(51.8), FastMath.toRadians(102.2), 811.2), "BADG"); - final GroundStation station = new GroundStation(topo); + final GroundStation station = new GroundStation(topo, TroposphericModelUtils.STANDARD_ATMOSPHERE_PROVIDER); // Create a phase measurement final Phase phase = new Phase(station, AbsoluteDate.J2000_EPOCH, 119866527.060, Frequency.G01.getWavelength(), 0.02, 1.0, new ObservableSatellite(0)); @@ -932,7 +929,7 @@ public void testIssue734() { Assertions.assertTrue(phase.getAmbiguityDriver().isSelected()); for (ParameterDriver driver : phase.getParametersDrivers()) { // Verify if the current driver corresponds to the phase ambiguity - if (driver.getName() == Phase.AMBIGUITY_NAME) { + if (driver instanceof AmbiguityDriver) { Assertions.assertEquals(1234.0, phase.getAmbiguityDriver().getValue(), Double.MIN_VALUE); Assertions.assertTrue(phase.getAmbiguityDriver().isSelected()); } diff --git a/src/test/java/org/orekit/estimation/measurements/gnss/WindUpTest.java b/src/test/java/org/orekit/estimation/measurements/gnss/WindUpTest.java index 1e5862570a..75b2eb01e1 100644 --- a/src/test/java/org/orekit/estimation/measurements/gnss/WindUpTest.java +++ b/src/test/java/org/orekit/estimation/measurements/gnss/WindUpTest.java @@ -29,6 +29,7 @@ import org.orekit.bodies.CelestialBodyFactory; import org.orekit.bodies.GeodeticPoint; import org.orekit.bodies.OneAxisEllipsoid; +import org.orekit.estimation.EstimationTestUtils; import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.GroundStation; import org.orekit.estimation.measurements.ObservableSatellite; @@ -140,12 +141,13 @@ private void doTest(final Orbit orbit, final AttitudeProvider attitudeProvider, PhaseBuilder builder = new PhaseBuilder(null, station, Frequency.G01.getWavelength(), 0.01 * Frequency.G01.getWavelength(), - 1.0, obsSat); + 1.0, obsSat, + new AmbiguityCache()); generator.addScheduler(new EventBasedScheduler<>(builder, new FixedStepSelector(60.0, TimeScalesFactory.getUTC()), generator.getPropagator(obsSat), - new ElevationDetector(station.getBaseFrame()). - withConstantElevation(FastMath.toRadians(5.0)). + EstimationTestUtils.getElevationDetector(station.getBaseFrame(), + FastMath.toRadians(5.0)). withHandler(new ContinueOnEvent()), SignSemantic.FEASIBLE_MEASUREMENT_WHEN_POSITIVE)); final GatheringSubscriber gatherer = new GatheringSubscriber(); @@ -161,8 +163,7 @@ private void doTest(final Orbit orbit, final AttitudeProvider attitudeProvider, for (ObservedMeasurement m : measurements) { Phase phase = (Phase) m; @SuppressWarnings("unchecked") - EstimatedMeasurementBase estimated = (EstimatedMeasurementBase) m.estimateWithoutDerivatives(0, 0, - new SpacecraftState[] { + EstimatedMeasurementBase estimated = (EstimatedMeasurementBase) m.estimateWithoutDerivatives(new SpacecraftState[] { propagator.propagate(phase.getDate()) }); final double original = estimated.getEstimatedValue()[0]; diff --git a/src/test/java/org/orekit/estimation/measurements/modifiers/AberrationModifierTest.java b/src/test/java/org/orekit/estimation/measurements/modifiers/AberrationModifierTest.java index 5a3a3c4f89..d14edc0f75 100644 --- a/src/test/java/org/orekit/estimation/measurements/modifiers/AberrationModifierTest.java +++ b/src/test/java/org/orekit/estimation/measurements/modifiers/AberrationModifierTest.java @@ -18,7 +18,11 @@ import org.hipparchus.Field; import org.hipparchus.analysis.UnivariateFunction; -import org.hipparchus.analysis.differentiation.*; +import org.hipparchus.analysis.differentiation.DSFactory; +import org.hipparchus.analysis.differentiation.DerivativeStructure; +import org.hipparchus.analysis.differentiation.FiniteDifferencesDifferentiator; +import org.hipparchus.analysis.differentiation.Gradient; +import org.hipparchus.analysis.differentiation.GradientField; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; import org.junit.jupiter.api.Assertions; @@ -38,6 +42,7 @@ import org.orekit.frames.FramesFactory; import org.orekit.frames.TopocentricFrame; import org.orekit.models.earth.ReferenceEllipsoid; +import org.orekit.models.earth.troposphere.TroposphericModelUtils; import org.orekit.orbits.CartesianOrbit; import org.orekit.propagation.SpacecraftState; import org.orekit.time.AbsoluteDate; @@ -70,7 +75,8 @@ static void setup() { 51.8); TopocentricFrame stationFrame = new TopocentricFrame(ReferenceEllipsoid.getWgs84(fixedFrame), stationLocation, "station"); - groundStation = new GroundStation(stationFrame); + groundStation = new GroundStation(stationFrame, + TroposphericModelUtils.STANDARD_ATMOSPHERE_PROVIDER); // Select parameters and set reference date List parameterDrivers = new ArrayList<>(); diff --git a/src/test/java/org/orekit/estimation/measurements/modifiers/InterSatellitesPhaseAmbiguityModifierTest.java b/src/test/java/org/orekit/estimation/measurements/modifiers/InterSatellitesPhaseAmbiguityModifierTest.java new file mode 100644 index 0000000000..a372735582 --- /dev/null +++ b/src/test/java/org/orekit/estimation/measurements/modifiers/InterSatellitesPhaseAmbiguityModifierTest.java @@ -0,0 +1,116 @@ +/* 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.estimation.measurements.modifiers; + +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.orekit.attitudes.LofOffset; +import org.orekit.estimation.Context; +import org.orekit.estimation.EstimationTestUtils; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; +import org.orekit.estimation.measurements.ObservedMeasurement; +import org.orekit.estimation.measurements.gnss.InterSatellitesPhase; +import org.orekit.estimation.measurements.gnss.InterSatellitesPhaseMeasurementCreator; +import org.orekit.frames.LOFType; +import org.orekit.gnss.Frequency; +import org.orekit.orbits.CartesianOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; +import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.BoundedPropagator; +import org.orekit.propagation.EphemerisGenerator; +import org.orekit.propagation.Propagator; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.conversion.NumericalPropagatorBuilder; +import org.orekit.utils.TimeStampedPVCoordinates; + +import java.util.List; + +@Deprecated +public class InterSatellitesPhaseAmbiguityModifierTest { + + private static final Frequency FREQUENCY = Frequency.G01; + + @Test + public void testEffect() { + + Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides"); + + final NumericalPropagatorBuilder propagatorBuilder = + context.createBuilder(OrbitType.KEPLERIAN, PositionAngleType.TRUE, true, + 1.0e-6, 60.0, 0.001); + propagatorBuilder.setAttitudeProvider(new LofOffset(propagatorBuilder.getFrame(), LOFType.LVLH)); + + // create perfect inter-satellites phase measurements without antenna offset + final TimeStampedPVCoordinates original = context.initialOrbit.getPVCoordinates(); + final Orbit closeOrbit = new CartesianOrbit(new TimeStampedPVCoordinates(context.initialOrbit.getDate(), + original.getPosition().add(new Vector3D(1000, 2000, 3000)), + original.getVelocity().add(new Vector3D(-0.03, 0.01, 0.02))), + context.initialOrbit.getFrame(), + context.initialOrbit.getMu()); + final Propagator closePropagator = EstimationTestUtils.createPropagator(closeOrbit, + propagatorBuilder); + final EphemerisGenerator generator = closePropagator.getEphemerisGenerator(); + closePropagator.propagate(context.initialOrbit.getDate().shiftedBy(3.5 * closeOrbit.getKeplerianPeriod())); + final BoundedPropagator ephemeris = generator.getGeneratedEphemeris(); + final Propagator p1 = EstimationTestUtils.createPropagator(context.initialOrbit, + propagatorBuilder); + final int ambiguity1 = 1234; + final int ambiguity2 = -45764; + final double localClockOffset = 0.137e-6; + final double remoteClockOffset = 469.0e-6; + final List> measurements = + EstimationTestUtils.createMeasurements(p1, + new InterSatellitesPhaseMeasurementCreator(ephemeris, + FREQUENCY, + ambiguity1, + localClockOffset, + remoteClockOffset, + Vector3D.ZERO, + Vector3D.ZERO), + 1.0, 3.0, 300.0); + + final Propagator p2 = EstimationTestUtils.createPropagator(context.initialOrbit, + propagatorBuilder); + + InterSatellitesPhaseAmbiguityModifier modifier = new InterSatellitesPhaseAmbiguityModifier(12, ambiguity2); + for (int i = 0; i < measurements.size(); ++i) { + InterSatellitesPhase sr = (InterSatellitesPhase) measurements.get(i); + final SpacecraftState localRefState = p2.propagate(sr.getDate()); + final SpacecraftState remoteRefState = ephemeris.propagate(sr.getDate()); + sr.addModifier(modifier); + EstimatedMeasurementBase evalNoMod = sr.estimateWithoutDerivatives(new SpacecraftState[] { + localRefState, + remoteRefState + }); + // add modifier + sr.addModifier(modifier); + EstimatedMeasurementBase eval = sr.estimateWithoutDerivatives(new SpacecraftState[] { + localRefState, + remoteRefState + }); + + Assertions.assertEquals(ambiguity2, eval.getEstimatedValue()[0] - evalNoMod.getEstimatedValue()[0], 4e-11); + + } + + } + +} + + diff --git a/src/test/java/org/orekit/estimation/measurements/modifiers/IonoModifierTest.java b/src/test/java/org/orekit/estimation/measurements/modifiers/IonoModifierTest.java index cf15bd0509..7a2ed6d9fc 100644 --- a/src/test/java/org/orekit/estimation/measurements/modifiers/IonoModifierTest.java +++ b/src/test/java/org/orekit/estimation/measurements/modifiers/IonoModifierTest.java @@ -81,7 +81,7 @@ public void setUp() throws Exception { model = new KlobucharIonoModel(new double[]{.3820e-07, .1490e-07, -.1790e-06, 0}, new double[]{.1430e+06, 0, -.3280e+06, .1130e+06}); // GPS L1 in HZ - frequency = Frequency.G01.getMHzFrequency() * 1.0e6; + frequency = Frequency.G01.getFrequency(); } @Test @@ -109,7 +109,8 @@ public void testPhaseIonoModifier() { final double satClockOffset = 345.0e-6; final List> measurements = EstimationTestUtils.createMeasurements(propagator, - new PhaseMeasurementCreator(context, Frequency.G01, 0, + new PhaseMeasurementCreator(context, + Frequency.G01, 0, satClockOffset), 1.0, 3.0, 300.0); propagator.clearStepHandlers(); @@ -180,7 +181,8 @@ public void testPhaseEstimatedIonoModifier() { final double satClockOffset = 345.0e-6; final List> measurements = EstimationTestUtils.createMeasurements(propagator, - new PhaseMeasurementCreator(context, Frequency.G01, 0, + new PhaseMeasurementCreator(context, + Frequency.G01, 0, satClockOffset), 1.0, 3.0, 300.0); propagator.clearStepHandlers(); @@ -325,13 +327,13 @@ public void testRangeRateIonoModifier() { final SpacecraftState refstate = propagator.propagate(date); RangeRate rangeRate = (RangeRate) measurement; - EstimatedMeasurementBase evalNoMod = rangeRate.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { refstate }); + EstimatedMeasurementBase evalNoMod = rangeRate.estimateWithoutDerivatives(new SpacecraftState[] { refstate }); // add modifier rangeRate.addModifier(modifier); // - EstimatedMeasurementBase eval = rangeRate.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { refstate }); + EstimatedMeasurementBase eval = rangeRate.estimateWithoutDerivatives(new SpacecraftState[] { refstate }); final double diffMetersSec = eval.getEstimatedValue()[0] - evalNoMod.getEstimatedValue()[0]; // TODO: check threshold @@ -446,14 +448,13 @@ public void testBistaticRangeIonoModifier() { final SpacecraftState refstate = propagator.propagate(biRange.getDate()); // Estimate without modifier - EstimatedMeasurementBase evalNoMod = biRange.estimateWithoutDerivatives(0, 0, - new SpacecraftState[] { refstate }); + EstimatedMeasurementBase evalNoMod = biRange.estimateWithoutDerivatives(new SpacecraftState[] { refstate }); // add modifier biRange.addModifier(modifier); // Estimate with modifier - EstimatedMeasurementBase eval = biRange.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { refstate }); + EstimatedMeasurementBase eval = biRange.estimateWithoutDerivatives(new SpacecraftState[] { refstate }); final double diffMeters = eval.getEstimatedValue()[0] - evalNoMod.getEstimatedValue()[0]; // TODO: check threshold @@ -496,15 +497,13 @@ public void testBistaticRangeRateIonoModifier() { final SpacecraftState refstate = propagator.propagate(biRangeRate.getDate()); // Estimate without modifier - EstimatedMeasurementBase evalNoMod = biRangeRate.estimateWithoutDerivatives(0, 0, - new SpacecraftState[] { refstate }); + EstimatedMeasurementBase evalNoMod = biRangeRate.estimateWithoutDerivatives(new SpacecraftState[] { refstate }); // add modifier biRangeRate.addModifier(modifier); // Estimate with modifier - EstimatedMeasurementBase eval = biRangeRate.estimateWithoutDerivatives(0, 0, - new SpacecraftState[] { refstate }); + EstimatedMeasurementBase eval = biRangeRate.estimateWithoutDerivatives(new SpacecraftState[] { refstate }); final double diffMetersSec = eval.getEstimatedValue()[0] - evalNoMod.getEstimatedValue()[0]; // TODO: check threshold @@ -550,7 +549,7 @@ public void testTDOAIonoModifier() { final SpacecraftState refState = propagator.propagate(tdoa.getDate()); // Estimate without modifier - EstimatedMeasurementBase evalNoMod = tdoa.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { refState }); + EstimatedMeasurementBase evalNoMod = tdoa.estimateWithoutDerivatives(new SpacecraftState[] { refState }); // add modifier tdoa.addModifier(modifier); @@ -600,13 +599,12 @@ public void testAngularIonoModifier() { final SpacecraftState refstate = propagator.propagate(date); AngularAzEl angular = (AngularAzEl) measurement; - EstimatedMeasurementBase evalNoMod = angular.estimateWithoutDerivatives(0, 0, - new SpacecraftState[] { refstate }); + EstimatedMeasurementBase evalNoMod = angular.estimateWithoutDerivatives(new SpacecraftState[] { refstate }); // add modifier angular.addModifier(modifier); // - EstimatedMeasurementBase eval = angular.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { refstate }); + EstimatedMeasurementBase eval = angular.estimateWithoutDerivatives(new SpacecraftState[] { refstate }); final double diffAz = MathUtils.normalizeAngle(eval.getEstimatedValue()[0], evalNoMod.getEstimatedValue()[0]) - evalNoMod.getEstimatedValue()[0]; final double diffEl = MathUtils.normalizeAngle(eval.getEstimatedValue()[1], evalNoMod.getEstimatedValue()[1]) - evalNoMod.getEstimatedValue()[1]; @@ -654,7 +652,7 @@ public void testKlobucharIonoModel() { } - private class MockIonosphericModel implements IonosphericModel { + private static class MockIonosphericModel implements IonosphericModel { /** Driver for the ionospheric delay.*/ private final ParameterDriver ionoDelay; diff --git a/src/test/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaInterSatellitesPhaseModifierTest.java b/src/test/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaInterSatellitesPhaseModifierTest.java index 95864e4a8a..350c7dbdd9 100644 --- a/src/test/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaInterSatellitesPhaseModifierTest.java +++ b/src/test/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaInterSatellitesPhaseModifierTest.java @@ -171,8 +171,7 @@ public void testEffect() { for (int i = 0; i < spacecraftCenteredMeasurements.size(); ++i) { InterSatellitesPhase sr = (InterSatellitesPhase) spacecraftCenteredMeasurements.get(i); sr.addModifier(modifier); - EstimatedMeasurementBase estimated = sr.estimateWithoutDerivatives(0, 0, - new SpacecraftState[] { + EstimatedMeasurementBase estimated = sr.estimateWithoutDerivatives(new SpacecraftState[] { p3.propagate(sr.getDate()), ephemeris.propagate(sr.getDate()) }); diff --git a/src/test/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaInterSatellitesRangeModifierTest.java b/src/test/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaInterSatellitesRangeModifierTest.java index f335da0612..0a571ff4e6 100644 --- a/src/test/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaInterSatellitesRangeModifierTest.java +++ b/src/test/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaInterSatellitesRangeModifierTest.java @@ -158,8 +158,7 @@ public void testEffect() { for (int i = 0; i < spacecraftCenteredMeasurements.size(); ++i) { InterSatellitesRange sr = (InterSatellitesRange) spacecraftCenteredMeasurements.get(i); sr.addModifier(modifier); - EstimatedMeasurementBase estimated = sr.estimateWithoutDerivatives(0, 0, - new SpacecraftState[] { + EstimatedMeasurementBase estimated = sr.estimateWithoutDerivatives(new SpacecraftState[] { p3.propagate(sr.getDate()), ephemeris.propagate(sr.getDate()) }); diff --git a/src/test/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaOneWayGNSSPhaseModifierTest.java b/src/test/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaOneWayGNSSPhaseModifierTest.java index fbe3330195..3273d1f8d7 100644 --- a/src/test/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaOneWayGNSSPhaseModifierTest.java +++ b/src/test/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaOneWayGNSSPhaseModifierTest.java @@ -76,6 +76,7 @@ public void testPreliminary() { final List> spacecraftCenteredMeasurements = EstimationTestUtils.createMeasurements(p1, new OneWayGNSSPhaseCreator(ephemeris, + "remote", FREQUENCY, ambiguity, localClockOffset, @@ -92,6 +93,7 @@ public void testPreliminary() { final List> antennaCenteredMeasurements = EstimationTestUtils.createMeasurements(p2, new OneWayGNSSPhaseCreator(ephemeris, + "remote", FREQUENCY, ambiguity, localClockOffset, @@ -139,6 +141,7 @@ public void testEffect() { final List> spacecraftCenteredMeasurements = EstimationTestUtils.createMeasurements(p1, new OneWayGNSSPhaseCreator(ephemeris, + "remote", FREQUENCY, ambiguity, localClockOffset, @@ -153,6 +156,7 @@ public void testEffect() { final List> antennaCenteredMeasurements = EstimationTestUtils.createMeasurements(p2, new OneWayGNSSPhaseCreator(ephemeris, + "remote", FREQUENCY, ambiguity, localClockOffset, @@ -167,8 +171,7 @@ public void testEffect() { for (int i = 0; i < spacecraftCenteredMeasurements.size(); ++i) { OneWayGNSSPhase sr = (OneWayGNSSPhase) spacecraftCenteredMeasurements.get(i); sr.addModifier(modifier); - EstimatedMeasurementBase estimated = sr.estimateWithoutDerivatives(0, 0, - new SpacecraftState[] { p3.propagate(sr.getDate()) }); + EstimatedMeasurementBase estimated = sr.estimateWithoutDerivatives(new SpacecraftState[] { p3.propagate(sr.getDate()) }); OneWayGNSSPhase ar = (OneWayGNSSPhase) antennaCenteredMeasurements.get(i); Assertions.assertEquals(0.0, sr.getDate().durationFrom(ar.getDate()), 2.0e-8); Assertions.assertEquals(ar.getObservedValue()[0] * FREQUENCY.getWavelength(), estimated.getEstimatedValue()[0] * FREQUENCY.getWavelength(), 6.0e-5); diff --git a/src/test/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaOneWayGNSSRangeModifierTest.java b/src/test/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaOneWayGNSSRangeModifierTest.java index 055ac9749c..f146afaebf 100644 --- a/src/test/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaOneWayGNSSRangeModifierTest.java +++ b/src/test/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaOneWayGNSSRangeModifierTest.java @@ -154,8 +154,7 @@ public void testEffect() { for (int i = 0; i < spacecraftCenteredMeasurements.size(); ++i) { OneWayGNSSRange sr = (OneWayGNSSRange) spacecraftCenteredMeasurements.get(i); sr.addModifier(modifier); - EstimatedMeasurementBase estimated = sr.estimateWithoutDerivatives(0, 0, - new SpacecraftState[] { p3.propagate(sr.getDate()) }); + EstimatedMeasurementBase estimated = sr.estimateWithoutDerivatives(new SpacecraftState[] { p3.propagate(sr.getDate()) }); OneWayGNSSRange ar = (OneWayGNSSRange) antennaCenteredMeasurements.get(i); Assertions.assertEquals(0.0, sr.getDate().durationFrom(ar.getDate()), 2.0e-8); Assertions.assertEquals(ar.getObservedValue()[0], estimated.getEstimatedValue()[0], 2.0e-5); diff --git a/src/test/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaTurnAroundRangeModifierTest.java b/src/test/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaTurnAroundRangeModifierTest.java index dab2dbd47c..501de445f6 100644 --- a/src/test/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaTurnAroundRangeModifierTest.java +++ b/src/test/java/org/orekit/estimation/measurements/modifiers/OnBoardAntennaTurnAroundRangeModifierTest.java @@ -110,8 +110,7 @@ public void testEffect() { for (int i = 0; i < spacecraftCenteredMeasurements.size(); ++i) { TurnAroundRange sr = (TurnAroundRange) spacecraftCenteredMeasurements.get(i); sr.addModifier(modifier); - EstimatedMeasurementBase estimated = sr.estimateWithoutDerivatives(0, 0, - new SpacecraftState[] { p3.propagate(sr.getDate()) }); + EstimatedMeasurementBase estimated = sr.estimateWithoutDerivatives(new SpacecraftState[] { p3.propagate(sr.getDate()) }); TurnAroundRange ar = (TurnAroundRange) antennaCenteredMeasurements.get(i); Assertions.assertEquals(0.0, sr.getDate().durationFrom(ar.getDate()), 2.0e-8); Assertions.assertEquals(ar.getObservedValue()[0], estimated.getEstimatedValue()[0], 5.0e-7); diff --git a/src/test/java/org/orekit/estimation/measurements/modifiers/OneWayGNSSPhaseAmbiguityModifierTest.java b/src/test/java/org/orekit/estimation/measurements/modifiers/OneWayGNSSPhaseAmbiguityModifierTest.java new file mode 100644 index 0000000000..1edb9e0274 --- /dev/null +++ b/src/test/java/org/orekit/estimation/measurements/modifiers/OneWayGNSSPhaseAmbiguityModifierTest.java @@ -0,0 +1,117 @@ +/* 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.estimation.measurements.modifiers; + +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.orekit.attitudes.LofOffset; +import org.orekit.estimation.Context; +import org.orekit.estimation.EstimationTestUtils; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; +import org.orekit.estimation.measurements.ObservedMeasurement; +import org.orekit.estimation.measurements.gnss.OneWayGNSSPhase; +import org.orekit.estimation.measurements.gnss.OneWayGNSSPhaseCreator; +import org.orekit.frames.LOFType; +import org.orekit.gnss.Frequency; +import org.orekit.orbits.CartesianOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; +import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.BoundedPropagator; +import org.orekit.propagation.EphemerisGenerator; +import org.orekit.propagation.Propagator; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.conversion.NumericalPropagatorBuilder; +import org.orekit.utils.TimeStampedPVCoordinates; + +import java.util.List; + +@Deprecated +public class OneWayGNSSPhaseAmbiguityModifierTest { + + private static final Frequency FREQUENCY = Frequency.G01; + + @Test + public void testEffect() { + + Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides"); + + final NumericalPropagatorBuilder propagatorBuilder = + context.createBuilder(OrbitType.KEPLERIAN, PositionAngleType.TRUE, true, + 1.0e-6, 60.0, 0.001); + propagatorBuilder.setAttitudeProvider(new LofOffset(propagatorBuilder.getFrame(), LOFType.LVLH)); + + // create perfect inter-satellites phase measurements without antenna offset + final TimeStampedPVCoordinates original = context.initialOrbit.getPVCoordinates(); + final Orbit closeOrbit = new CartesianOrbit(new TimeStampedPVCoordinates(context.initialOrbit.getDate(), + original.getPosition().add(new Vector3D(1000, 2000, 3000)), + original.getVelocity().add(new Vector3D(-0.03, 0.01, 0.02))), + context.initialOrbit.getFrame(), + context.initialOrbit.getMu()); + final Propagator closePropagator = EstimationTestUtils.createPropagator(closeOrbit, + propagatorBuilder); + final EphemerisGenerator generator = closePropagator.getEphemerisGenerator(); + closePropagator.propagate(context.initialOrbit.getDate().shiftedBy(3.5 * closeOrbit.getKeplerianPeriod())); + final BoundedPropagator ephemeris = generator.getGeneratedEphemeris(); + final Propagator p1 = EstimationTestUtils.createPropagator(context.initialOrbit, + propagatorBuilder); + final int ambiguity1 = 1234; + final int ambiguity2 = -45764; + final double localClockOffset = 0.137e-6; + final double remoteClockOffset = 469.0e-6; + final List> measurements = + EstimationTestUtils.createMeasurements(p1, + new OneWayGNSSPhaseCreator(ephemeris, + "remote", + FREQUENCY, + ambiguity1, + localClockOffset, + remoteClockOffset, + Vector3D.ZERO, + Vector3D.ZERO), + 1.0, 3.0, 300.0); + + final Propagator p2 = EstimationTestUtils.createPropagator(context.initialOrbit, + propagatorBuilder); + + OneWayGNSSPhaseAmbiguityModifier modifier = new OneWayGNSSPhaseAmbiguityModifier(39, ambiguity2); + for (int i = 0; i < measurements.size(); ++i) { + OneWayGNSSPhase sr = (OneWayGNSSPhase) measurements.get(i); + final SpacecraftState localRefState = p2.propagate(sr.getDate()); + final SpacecraftState remoteRefState = ephemeris.propagate(sr.getDate()); + sr.addModifier(modifier); + EstimatedMeasurementBase evalNoMod = sr.estimateWithoutDerivatives(new SpacecraftState[] { + localRefState, + remoteRefState + }); + // add modifier + sr.addModifier(modifier); + EstimatedMeasurementBase eval = sr.estimateWithoutDerivatives(new SpacecraftState[] { + localRefState, + remoteRefState + }); + + Assertions.assertEquals(ambiguity2, eval.getEstimatedValue()[0] - evalNoMod.getEstimatedValue()[0], 4e-11); + + } + + } + +} + + diff --git a/src/test/java/org/orekit/estimation/measurements/modifiers/PhaseAmbiguityModifierTest.java b/src/test/java/org/orekit/estimation/measurements/modifiers/PhaseAmbiguityModifierTest.java new file mode 100644 index 0000000000..e165783545 --- /dev/null +++ b/src/test/java/org/orekit/estimation/measurements/modifiers/PhaseAmbiguityModifierTest.java @@ -0,0 +1,95 @@ +/* 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.estimation.measurements.modifiers; + +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.estimation.measurements.EstimatedMeasurementBase; +import org.orekit.estimation.measurements.GroundStation; +import org.orekit.estimation.measurements.ObservedMeasurement; +import org.orekit.estimation.measurements.gnss.Phase; +import org.orekit.estimation.measurements.gnss.PhaseMeasurementCreator; +import org.orekit.gnss.Frequency; +import org.orekit.orbits.OrbitType; +import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.Propagator; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.conversion.NumericalPropagatorBuilder; +import org.orekit.time.AbsoluteDate; + +import java.util.List; + +@Deprecated +public class PhaseAmbiguityModifierTest { + + @Test + public void testPhaseAmbiguityModifier() { + + Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides"); + + final NumericalPropagatorBuilder propagatorBuilder = + context.createBuilder(OrbitType.KEPLERIAN, PositionAngleType.TRUE, true, + 1.0e-6, 60.0, 0.001); + + // create perfect range measurements + for (final GroundStation station : context.stations) { + station.getClockOffsetDriver().setSelected(true); + station.getEastOffsetDriver().setSelected(true); + station.getNorthOffsetDriver().setSelected(true); + station.getZenithOffsetDriver().setSelected(true); + } + final Propagator propagator = EstimationTestUtils.createPropagator(context.initialOrbit, + propagatorBuilder); + final int ambiguity1 = 1234; + final int ambiguity2 = -45764; + final double groundClockOffset = 12.0e-6; + for (final GroundStation station : context.stations) { + station.getClockOffsetDriver().setValue(groundClockOffset); + } + final double satClockOffset = 345.0e-6; + final List> measurements = + EstimationTestUtils.createMeasurements(propagator, + new PhaseMeasurementCreator(context, + Frequency.G01, + ambiguity1, + satClockOffset), + 1.0, 3.0, 300.0); + propagator.clearStepHandlers(); + + final PhaseAmbiguityModifier modifier = new PhaseAmbiguityModifier(17, ambiguity2); + + for (final ObservedMeasurement measurement : measurements) { + final AbsoluteDate date = measurement.getDate(); + + final SpacecraftState refState = propagator.propagate(date); + + Phase phase = (Phase) measurement; + EstimatedMeasurementBase evalNoMod = phase.estimateWithoutDerivatives(new SpacecraftState[] { refState }); + + + // add modifier + phase.addModifier(modifier); + EstimatedMeasurementBase eval = phase.estimateWithoutDerivatives(new SpacecraftState[] { refState }); + + Assertions.assertEquals(ambiguity2, eval.getEstimatedValue()[0] - evalNoMod.getEstimatedValue()[0]); + + } + } + +} diff --git a/src/test/java/org/orekit/estimation/measurements/modifiers/PhaseCentersPhaseModifierTest.java b/src/test/java/org/orekit/estimation/measurements/modifiers/PhaseCentersPhaseModifierTest.java index c3804779bb..9bdbc00cf2 100644 --- a/src/test/java/org/orekit/estimation/measurements/modifiers/PhaseCentersPhaseModifierTest.java +++ b/src/test/java/org/orekit/estimation/measurements/modifiers/PhaseCentersPhaseModifierTest.java @@ -68,7 +68,8 @@ public void testPreliminary() { final double satClockOffset = 345.0e-6; final List> spacecraftCenteredMeasurements = EstimationTestUtils.createMeasurements(p1, - new PhaseMeasurementCreator(context, Frequency.G01, + new PhaseMeasurementCreator(context, + Frequency.G01, ambiguity, satClockOffset), 1.0, 3.0, 300.0); @@ -78,7 +79,8 @@ public void testPreliminary() { propagatorBuilder); final List> antennaCenteredMeasurements = EstimationTestUtils.createMeasurements(p2, - new PhaseMeasurementCreator(context, Frequency.G01, + new PhaseMeasurementCreator(context, + Frequency.G01, ambiguity, satClockOffset, Vector3D.ZERO, null, new Vector3D(xOffset, 0, 0), null), @@ -112,7 +114,8 @@ public void testEffect() { final Propagator p1 = EstimationTestUtils.createPropagator(context.initialOrbit, propagatorBuilder); final List> spacecraftCenteredMeasurements = EstimationTestUtils.createMeasurements(p1, - new PhaseMeasurementCreator(context, Frequency.G01, + new PhaseMeasurementCreator(context, + Frequency.G01, ambiguity, satClockOffset, Vector3D.ZERO, null, Vector3D.ZERO, null), @@ -135,7 +138,8 @@ public void testEffect() { propagatorBuilder); final List> antennaCenteredMeasurements = EstimationTestUtils.createMeasurements(p2, - new PhaseMeasurementCreator(context, Frequency.G01, + new PhaseMeasurementCreator(context, + Frequency.G01, ambiguity, satClockOffset, stationMeanPosition, stationPCV, satelliteMeanPosition, satellitePCV), @@ -151,8 +155,7 @@ public void testEffect() { for (int i = 0; i < spacecraftCenteredMeasurements.size(); ++i) { Phase sr = (Phase) spacecraftCenteredMeasurements.get(i); sr.addModifier(modifier); - EstimatedMeasurementBase estimated = sr.estimateWithoutDerivatives(0, 0, - new SpacecraftState[] { p3.propagate(sr.getDate()) }); + EstimatedMeasurementBase estimated = sr.estimateWithoutDerivatives(new SpacecraftState[] { p3.propagate(sr.getDate()) }); Phase ar = (Phase) antennaCenteredMeasurements.get(i); Assertions.assertEquals(0.0, sr.getDate().durationFrom(ar.getDate()), 1.0e-8); Assertions.assertEquals(ar.getObservedValue()[0], estimated.getEstimatedValue()[0], 1.1e-5); diff --git a/src/test/java/org/orekit/estimation/measurements/modifiers/PhaseCentersRangeModifierTest.java b/src/test/java/org/orekit/estimation/measurements/modifiers/PhaseCentersRangeModifierTest.java index 15f10eb6db..41c837bf1f 100644 --- a/src/test/java/org/orekit/estimation/measurements/modifiers/PhaseCentersRangeModifierTest.java +++ b/src/test/java/org/orekit/estimation/measurements/modifiers/PhaseCentersRangeModifierTest.java @@ -145,8 +145,7 @@ public void testOneWayEffect() { for (int i = 0; i < spacecraftCenteredMeasurements.size(); ++i) { Range sr = (Range) spacecraftCenteredMeasurements.get(i); sr.addModifier(modifier); - EstimatedMeasurementBase estimated = sr.estimateWithoutDerivatives(0, 0, - new SpacecraftState[] { p3.propagate(sr.getDate()) }); + EstimatedMeasurementBase estimated = sr.estimateWithoutDerivatives(new SpacecraftState[] { p3.propagate(sr.getDate()) }); Range ar = (Range) antennaCenteredMeasurements.get(i); Assertions.assertEquals(0.0, sr.getDate().durationFrom(ar.getDate()), 1.0e-8); Assertions.assertEquals(ar.getObservedValue()[0], estimated.getEstimatedValue()[0], 1.7e-6); @@ -211,8 +210,7 @@ public void testTwoWayEffect() { for (int i = 0; i < spacecraftCenteredMeasurements.size(); ++i) { Range sr = (Range) spacecraftCenteredMeasurements.get(i); sr.addModifier(modifier); - EstimatedMeasurementBase estimated = sr.estimateWithoutDerivatives(0, 0, - new SpacecraftState[] { p3.propagate(sr.getDate()) }); + EstimatedMeasurementBase estimated = sr.estimateWithoutDerivatives(new SpacecraftState[] { p3.propagate(sr.getDate()) }); Range ar = (Range) antennaCenteredMeasurements.get(i); Assertions.assertEquals(0.0, sr.getDate().durationFrom(ar.getDate()), 1.0e-8); Assertions.assertEquals(ar.getObservedValue()[0], estimated.getEstimatedValue()[0], 3.4e-7); diff --git a/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticClockInterSatellitesOneWayRangeRateModifierTest.java b/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticClockInterSatellitesOneWayRangeRateModifierTest.java new file mode 100644 index 0000000000..7b158c87e6 --- /dev/null +++ b/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticClockInterSatellitesOneWayRangeRateModifierTest.java @@ -0,0 +1,91 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.estimation.measurements.modifiers; + +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.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; +import org.orekit.estimation.measurements.EstimationModifier; +import org.orekit.estimation.measurements.gnss.InterSatellitesOneWayRangeRate; +import org.orekit.estimation.measurements.ObservableSatellite; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.analytical.tle.TLE; +import org.orekit.propagation.analytical.tle.TLEPropagator; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.TimeScalesFactory; +import org.orekit.utils.Constants; +import org.orekit.utils.PVCoordinates; + +public class RelativisticClockInterSatellitesOneWayRangeRateModifierTest { + + /** Date. */ + private static AbsoluteDate date; + + /** Spacecraft states. */ + private static SpacecraftState[] states; + + @Test + public void testRelativisticClockCorrection() { + + // Measurement + final PVCoordinates delta = new PVCoordinates(states[1].getPVCoordinates(), + states[0].getPVCoordinates()); + final InterSatellitesOneWayRangeRate range = new InterSatellitesOneWayRangeRate(new ObservableSatellite(0), + new ObservableSatellite(1), + date, + Vector3D.dotProduct(delta.getVelocity(), + delta.getPosition().normalize()), + 1.0, 1.0); + + // Inter-satellites range rate before applying the modifier + final EstimatedMeasurementBase estimatedBefore = range.estimateWithoutDerivatives(states); + + // Inter-satellites range rate before applying the modifier + final EstimationModifier modifier = + new RelativisticClockInterSatellitesOneWayRangeRateModifier(Constants.EIGEN5C_EARTH_MU); + range.addModifier(modifier); + final EstimatedMeasurement estimatedAfter = range.estimate(0, 0, states); + + // Verify + Assertions.assertEquals(1.63e-3, estimatedBefore.getEstimatedValue()[0] - estimatedAfter.getEstimatedValue()[0], 1.0e-5); + Assertions.assertEquals(0, modifier.getParametersDrivers().size()); + + } + + @BeforeEach + public void setUp() { + // Data root + Utils.setDataRoot("regular-data"); + + // Date + date = new AbsoluteDate("2004-01-13T00:00:00.000", TimeScalesFactory.getUTC()); + + // Spacecraft states + states = new SpacecraftState[2]; + final TLE local = new TLE("1 27642U 03002A 04013.91734903 .00000108 00000-0 12227-4 0 3621", + "2 27642 93.9970 6.8623 0003169 80.1383 280.0205 14.90871424 54508"); + final TLE remote = new TLE("1 20061U 89044A 04013.44391333 .00000095 00000-0 10000-3 0 3242", + "2 20061 53.4233 172.2072 0234017 261.4179 95.8975 2.00577231106949"); + states[0] = TLEPropagator.selectExtrapolator(local).propagate(date); + states[1] = TLEPropagator.selectExtrapolator(remote).propagate(date); + } + +} diff --git a/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticClockInterSatellitesPhaseModifierTest.java b/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticClockInterSatellitesPhaseModifierTest.java index 33a7c71255..f08a50c939 100644 --- a/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticClockInterSatellitesPhaseModifierTest.java +++ b/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticClockInterSatellitesPhaseModifierTest.java @@ -25,6 +25,7 @@ import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.ObservableSatellite; +import org.orekit.estimation.measurements.gnss.AmbiguityCache; import org.orekit.estimation.measurements.gnss.InterSatellitesPhase; import org.orekit.gnss.Frequency; import org.orekit.propagation.SpacecraftState; @@ -41,8 +42,9 @@ public class RelativisticClockInterSatellitesPhaseModifierTest { /** Spacecraft states. */ private static SpacecraftState[] states; + @Deprecated @Test - public void testRelativisticClockCorrection() { + public void testRelativisticClockCorrectionDeprecated() { // Measurement final double wavelength = Frequency.G01.getWavelength(); @@ -53,7 +55,33 @@ public void testRelativisticClockCorrection() { wavelength, 1.0, 1.0); // Inter-satellites phase before applying the modifier - final EstimatedMeasurementBase estimatedBefore = phase.estimateWithoutDerivatives(0, 0, states); + final EstimatedMeasurementBase estimatedBefore = phase.estimateWithoutDerivatives(states); + + // Inter-satellites phase after applying the modifier + final EstimationModifier modifier = new RelativisticClockInterSatellitesPhaseModifier(); + phase.addModifier(modifier); + final EstimatedMeasurement estimatedAfter = phase.estimate(0, 0, states); + + // Verify + Assertions.assertEquals(-10.57, (estimatedBefore.getEstimatedValue()[0] - estimatedAfter.getEstimatedValue()[0]) * wavelength, 1.0e-2); + Assertions.assertEquals(0, modifier.getParametersDrivers().size()); + + } + + @Test + public void testRelativisticClockCorrection() { + + // Measurement + final double wavelength = Frequency.G01.getWavelength(); + final InterSatellitesPhase phase = new InterSatellitesPhase(new ObservableSatellite(0), new ObservableSatellite(1), + date, + Vector3D.distance(states[0].getPosition(), + states[1].getPosition()) / wavelength, + wavelength, 1.0, 1.0, + new AmbiguityCache()); + + // Inter-satellites phase before applying the modifier + final EstimatedMeasurementBase estimatedBefore = phase.estimateWithoutDerivatives(states); // Inter-satellites phase after applying the modifier final EstimationModifier modifier = new RelativisticClockInterSatellitesPhaseModifier(); diff --git a/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticClockInterSatellitesRangeModifierTest.java b/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticClockInterSatellitesRangeModifierTest.java index 4f3e9da5b4..ff7a34e0fc 100644 --- a/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticClockInterSatellitesRangeModifierTest.java +++ b/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticClockInterSatellitesRangeModifierTest.java @@ -51,7 +51,7 @@ public void testRelativisticClockCorrection() { 1.0, 1.0); // Inter-satellites range before applying the modifier - final EstimatedMeasurementBase estimatedBefore = range.estimateWithoutDerivatives(0, 0, states); + final EstimatedMeasurementBase estimatedBefore = range.estimateWithoutDerivatives(states); // Inter-satellites range before applying the modifier final EstimationModifier modifier = new RelativisticClockInterSatellitesRangeModifier(); diff --git a/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticClockOneWayGNSSPhaseModifierTest.java b/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticClockOneWayGNSSPhaseModifierTest.java index 5c46f64b87..199b10e976 100644 --- a/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticClockOneWayGNSSPhaseModifierTest.java +++ b/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticClockOneWayGNSSPhaseModifierTest.java @@ -52,7 +52,7 @@ public void testRelativisticClockCorrection() { wavelength, 1.0, 1.0, new ObservableSatellite(0)); // One-way GNSS phase before applying the modifier - final EstimatedMeasurementBase estimatedBefore = phase.estimateWithoutDerivatives(0, 0, states); + final EstimatedMeasurementBase estimatedBefore = phase.estimateWithoutDerivatives(states); // One-way GNSS phase before applying the modifier final EstimationModifier modifier = new RelativisticClockOneWayGNSSPhaseModifier(); diff --git a/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticClockOneWayGNSSRangeModifierTest.java b/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticClockOneWayGNSSRangeModifierTest.java index 0175fee655..ad07b3b003 100644 --- a/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticClockOneWayGNSSRangeModifierTest.java +++ b/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticClockOneWayGNSSRangeModifierTest.java @@ -50,7 +50,7 @@ public void testRelativisticClockCorrection() { 1.0, 1.0, new ObservableSatellite(0)); // Inter-satellites range before applying the modifier - final EstimatedMeasurementBase estimatedBefore = range.estimateWithoutDerivatives(0, 0, states); + final EstimatedMeasurementBase estimatedBefore = range.estimateWithoutDerivatives(states); // Inter-satellites range before applying the modifier final EstimationModifier modifier = new RelativisticClockOneWayGNSSRangeModifier(); diff --git a/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticClockOneWayGNSSRangeRateModifierTest.java b/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticClockOneWayGNSSRangeRateModifierTest.java new file mode 100644 index 0000000000..1bf2379f9a --- /dev/null +++ b/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticClockOneWayGNSSRangeRateModifierTest.java @@ -0,0 +1,91 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.estimation.measurements.modifiers; + +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.estimation.measurements.EstimatedMeasurement; +import org.orekit.estimation.measurements.EstimatedMeasurementBase; +import org.orekit.estimation.measurements.EstimationModifier; +import org.orekit.estimation.measurements.ObservableSatellite; +import org.orekit.estimation.measurements.gnss.OneWayGNSSRangeRate; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.analytical.tle.TLE; +import org.orekit.propagation.analytical.tle.TLEPropagator; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.TimeScalesFactory; +import org.orekit.utils.Constants; +import org.orekit.utils.PVCoordinates; + +public class RelativisticClockOneWayGNSSRangeRateModifierTest { + + /** Date. */ + private static AbsoluteDate date; + + /** Spacecraft states. */ + private static SpacecraftState[] states; + + @Test + public void testRelativisticClockCorrection() { + + // Measurement + final PVCoordinates delta = new PVCoordinates(states[1].getPVCoordinates(), + states[0].getPVCoordinates()); + final OneWayGNSSRangeRate + range = new OneWayGNSSRangeRate(states[1].getOrbit(), 0.0, + date, + Vector3D.dotProduct(delta.getVelocity(), + delta.getPosition().normalize()), + 1.0, 1.0, new ObservableSatellite(0)); + + // Inter-satellites range rate before applying the modifier + final EstimatedMeasurementBase estimatedBefore = range.estimateWithoutDerivatives(states); + + // Inter-satellites range rate before applying the modifier + final EstimationModifier modifier = + new RelativisticClockOneWayGNSSRangeRateModifier(Constants.EIGEN5C_EARTH_MU); + range.addModifier(modifier); + final EstimatedMeasurement estimatedAfter = range.estimate(0, 0, states); + + // Verify + Assertions.assertEquals(1.63e-3, estimatedBefore.getEstimatedValue()[0] - estimatedAfter.getEstimatedValue()[0], 1.0e-5); + Assertions.assertEquals(0, modifier.getParametersDrivers().size()); + + } + + @BeforeEach + public void setUp() { + // Data root + Utils.setDataRoot("regular-data"); + + // Date + date = new AbsoluteDate("2004-01-13T00:00:00.000", TimeScalesFactory.getUTC()); + + // Spacecraft states + states = new SpacecraftState[2]; + final TLE local = new TLE("1 27642U 03002A 04013.91734903 .00000108 00000-0 12227-4 0 3621", + "2 27642 93.9970 6.8623 0003169 80.1383 280.0205 14.90871424 54508"); + final TLE remote = new TLE("1 20061U 89044A 04013.44391333 .00000095 00000-0 10000-3 0 3242", + "2 20061 53.4233 172.2072 0234017 261.4179 95.8975 2.00577231106949"); + states[0] = TLEPropagator.selectExtrapolator(local).propagate(date); + states[1] = TLEPropagator.selectExtrapolator(remote).propagate(date); + } + +} diff --git a/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticClockPhaseModifierTest.java b/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticClockPhaseModifierTest.java index c0475a1de4..4f6c04a52c 100644 --- a/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticClockPhaseModifierTest.java +++ b/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticClockPhaseModifierTest.java @@ -27,10 +27,12 @@ import org.orekit.estimation.measurements.EstimatedMeasurement; import org.orekit.estimation.measurements.GroundStation; import org.orekit.estimation.measurements.ObservableSatellite; +import org.orekit.estimation.measurements.gnss.AmbiguityCache; import org.orekit.estimation.measurements.gnss.Phase; import org.orekit.frames.FramesFactory; import org.orekit.frames.TopocentricFrame; import org.orekit.gnss.Frequency; +import org.orekit.models.earth.troposphere.TroposphericModelUtils; import org.orekit.orbits.CartesianOrbit; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.analytical.tle.TLE; @@ -44,8 +46,9 @@ public class RelativisticClockPhaseModifierTest { + @Deprecated @Test - public void testRelativisticClockCorrection() { + public void testRelativisticClockCorrectionDeprecated() { // Station final OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, @@ -53,7 +56,8 @@ public void testRelativisticClockCorrection() { FramesFactory.getITRF(IERSConventions.IERS_2010, true)); final GeodeticPoint point = new GeodeticPoint(FastMath.toRadians(42.0), FastMath.toRadians(1.0), 100.0); final TopocentricFrame topo = new TopocentricFrame(earth, point, ""); - final GroundStation station = new GroundStation(topo); + final GroundStation station = new GroundStation(topo, + TroposphericModelUtils.STANDARD_ATMOSPHERE_PROVIDER); // Satellite (GPS orbit from TLE) final TLE tle = new TLE("1 28474U 04045A 20252.59334296 -.00000043 00000-0 00000-0 0 9998", @@ -83,9 +87,67 @@ public void testRelativisticClockCorrection() { // Range measurement final Phase phase = new Phase(station, state.getDate(), 26584264.45, Frequency.G01.getWavelength(), 1.0, 1.0, new ObservableSatellite(0)); - final EstimatedMeasurement estimated = new EstimatedMeasurement(phase, 0, 0, - new SpacecraftState[] {state}, - new TimeStampedPVCoordinates[] {state.getPVCoordinates(), stationPV}); + final EstimatedMeasurement estimated = new EstimatedMeasurement<>(phase, 0, 0, + new SpacecraftState[] {state}, + new TimeStampedPVCoordinates[] {state.getPVCoordinates(), stationPV}); + estimated.setEstimatedValue(phase.getObservedValue()[0]); + Assertions.assertEquals(0.0, estimated.getObservedValue()[0] - estimated.getEstimatedValue()[0], 1.0e-3); + + // Measurement modifier + final RelativisticClockPhaseModifier modifier = new RelativisticClockPhaseModifier(); + modifier.modify(estimated); + Assertions.assertEquals(0, modifier.getParametersDrivers().size()); + + // Verify + Assertions.assertEquals(-6.87 / Frequency.G01.getWavelength(), estimated.getObservedValue()[0] - estimated.getEstimatedValue()[0], 1.0e-2); + + } + + @Test + public void testRelativisticClockCorrection() { + + // Station + final OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, + Constants.WGS84_EARTH_FLATTENING, + FramesFactory.getITRF(IERSConventions.IERS_2010, true)); + final GeodeticPoint point = new GeodeticPoint(FastMath.toRadians(42.0), FastMath.toRadians(1.0), 100.0); + final TopocentricFrame topo = new TopocentricFrame(earth, point, ""); + final GroundStation station = new GroundStation(topo, + TroposphericModelUtils.STANDARD_ATMOSPHERE_PROVIDER); + + // Satellite (GPS orbit from TLE) + final TLE tle = new TLE("1 28474U 04045A 20252.59334296 -.00000043 00000-0 00000-0 0 9998", + "2 28474 55.0265 49.5108 0200271 267.9106 149.0797 2.00552216116165"); + final TimeStampedPVCoordinates satPV = TLEPropagator.selectExtrapolator(tle).getPVCoordinates(tle.getDate(), FramesFactory.getEME2000()); + final SpacecraftState state = new SpacecraftState(new CartesianOrbit(satPV, FramesFactory.getEME2000(), Constants.WGS84_EARTH_MU)); + + // Set reference date to station drivers + for (ParameterDriver driver : Arrays.asList(station.getClockOffsetDriver(), + station.getEastOffsetDriver(), + station.getNorthOffsetDriver(), + station.getZenithOffsetDriver(), + station.getPrimeMeridianOffsetDriver(), + station.getPrimeMeridianDriftDriver(), + station.getPolarOffsetXDriver(), + station.getPolarDriftXDriver(), + station.getPolarOffsetYDriver(), + station.getPolarDriftYDriver())) { + if (driver.getReferenceDate() == null) { + driver.setReferenceDate(state.getDate()); + } + } + + // Station PV + final Vector3D zero = Vector3D.ZERO; + final TimeStampedPVCoordinates stationPV = station.getOffsetToInertial(state.getFrame(), state.getDate(), false).transformPVCoordinates(new TimeStampedPVCoordinates(state.getDate(), zero, zero, zero)); + + // Range measurement + final Phase phase = new Phase(station, state.getDate(), 26584264.45, Frequency.G01.getWavelength(), + 1.0, 1.0, new ObservableSatellite(0), + new AmbiguityCache()); + final EstimatedMeasurement estimated = new EstimatedMeasurement<>(phase, 0, 0, + new SpacecraftState[] {state}, + new TimeStampedPVCoordinates[] {state.getPVCoordinates(), stationPV}); estimated.setEstimatedValue(phase.getObservedValue()[0]); Assertions.assertEquals(0.0, estimated.getObservedValue()[0] - estimated.getEstimatedValue()[0], 1.0e-3); diff --git a/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticClockRangeModifierTest.java b/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticClockRangeModifierTest.java index ea4742a07f..c5c11a9e44 100644 --- a/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticClockRangeModifierTest.java +++ b/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticClockRangeModifierTest.java @@ -30,6 +30,7 @@ import org.orekit.estimation.measurements.Range; import org.orekit.frames.FramesFactory; import org.orekit.frames.TopocentricFrame; +import org.orekit.models.earth.troposphere.TroposphericModelUtils; import org.orekit.orbits.CartesianOrbit; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.analytical.tle.TLE; @@ -52,7 +53,8 @@ public void testRelativisticClockCorrection() { FramesFactory.getITRF(IERSConventions.IERS_2010, true)); final GeodeticPoint point = new GeodeticPoint(FastMath.toRadians(42.0), FastMath.toRadians(1.0), 100.0); final TopocentricFrame topo = new TopocentricFrame(earth, point, ""); - final GroundStation station = new GroundStation(topo); + final GroundStation station = new GroundStation(topo, + TroposphericModelUtils.STANDARD_ATMOSPHERE_PROVIDER); // Satellite (GPS orbit from TLE) final TLE tle = new TLE("1 28474U 04045A 20252.59334296 -.00000043 00000-0 00000-0 0 9998", diff --git a/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticClockRangeRateModifierTest.java b/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticClockRangeRateModifierTest.java index 0da26c8a73..61b871a053 100644 --- a/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticClockRangeRateModifierTest.java +++ b/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticClockRangeRateModifierTest.java @@ -30,6 +30,7 @@ import org.orekit.estimation.measurements.RangeRate; import org.orekit.frames.FramesFactory; import org.orekit.frames.TopocentricFrame; +import org.orekit.models.earth.troposphere.TroposphericModelUtils; import org.orekit.orbits.CartesianOrbit; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.analytical.tle.TLE; @@ -52,7 +53,8 @@ public void testRelativisticClockCorrection() { FramesFactory.getITRF(IERSConventions.IERS_2010, true)); final GeodeticPoint point = new GeodeticPoint(FastMath.toRadians(42.0), FastMath.toRadians(1.0), 100.0); final TopocentricFrame topo = new TopocentricFrame(earth, point, ""); - final GroundStation station = new GroundStation(topo); + final GroundStation station = new GroundStation(topo, + TroposphericModelUtils.STANDARD_ATMOSPHERE_PROVIDER); // Satellite (GPS orbit from TLE) final TLE tle = new TLE("1 28474U 04045A 20252.59334296 -.00000043 00000-0 00000-0 0 9998", diff --git a/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockInterSatellitesPhaseModifierTest.java b/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockInterSatellitesPhaseModifierTest.java index f62c6a39b0..0aa457a197 100644 --- a/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockInterSatellitesPhaseModifierTest.java +++ b/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockInterSatellitesPhaseModifierTest.java @@ -25,6 +25,7 @@ import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.ObservableSatellite; +import org.orekit.estimation.measurements.gnss.AmbiguityCache; import org.orekit.estimation.measurements.gnss.InterSatellitesPhase; import org.orekit.gnss.Frequency; import org.orekit.propagation.SpacecraftState; @@ -37,15 +38,16 @@ /** * Check against prediction in - * - * "Springer Handbook oƒ Global Navigation Satellite Systems, Teunissen, Montenbruck" - * + * "Springer Handbook oƒ Global Navigation Satellite Systems, Teunissen, Montenbruck". + *

          * An approximate value is given in terms of delay for Galileo satellites. * As these satellites are close to GPS satellites, we consider the delays to be * of the same order, namely around 62ps. - * + *

          + *

          * The values produced by the modifiers are translated in terms of delay and checked against * the approximate value. + *

          */ public class RelativisticJ2ClockInterSatellitesPhaseModifierTest { @@ -56,23 +58,54 @@ public class RelativisticJ2ClockInterSatellitesPhaseModifierTest { /** Spacecraft states. */ private static SpacecraftState[] states; + @Deprecated + @Test + public void testRelativisticClockCorrectionDeprecated() { + + // Measurement + final double wavelength = Frequency.G01.getWavelength(); + final InterSatellitesPhase phase = new InterSatellitesPhase(new ObservableSatellite(0), new ObservableSatellite(1), + date, + Vector3D.distance(states[0].getPosition(), + states[1].getPosition()) / wavelength, + wavelength, 1.0, 1.0, + new AmbiguityCache()); + + // Inter-satellites phase before applying the modifier + final EstimatedMeasurementBase estimatedBefore = phase.estimateWithoutDerivatives(states); + + // Inter-satellites phase after applying the modifier + final EstimationModifier modifier = new RelativisticJ2ClockInterSatellitesPhaseModifier(Constants.WGS84_EARTH_MU, + Constants.WGS84_EARTH_C20, Constants.WGS84_EARTH_EQUATORIAL_RADIUS ); + phase.addModifier(modifier); + final EstimatedMeasurement estimatedAfter = phase.estimate(0, 0, states); + + // Verify : According to Teunissen and Montenbruck, the delay is supposed to be around 62 ps for Galileo. + // The computed value is equal to 67.284 ps, therefore lying in the supposed range. + Assertions.assertEquals(-0.106217, estimatedBefore.getEstimatedValue()[0] - estimatedAfter.getEstimatedValue()[0], 1.0e-6); + Assertions.assertEquals(0, modifier.getParametersDrivers().size()); + + } + @Test public void testRelativisticClockCorrection() { // Measurement + final AmbiguityCache cache = new AmbiguityCache(); final double wavelength = Frequency.G01.getWavelength(); final InterSatellitesPhase phase = new InterSatellitesPhase(new ObservableSatellite(0), new ObservableSatellite(1), date, Vector3D.distance(states[0].getPosition(), states[1].getPosition()) / wavelength, - wavelength, 1.0, 1.0); + wavelength, 1.0, 1.0, + cache); // Inter-satellites phase before applying the modifier - final EstimatedMeasurementBase estimatedBefore = phase.estimateWithoutDerivatives(0, 0, states); + final EstimatedMeasurementBase estimatedBefore = phase.estimateWithoutDerivatives(states); // Inter-satellites phase after applying the modifier final EstimationModifier modifier = new RelativisticJ2ClockInterSatellitesPhaseModifier(Constants.WGS84_EARTH_MU, - Constants.WGS84_EARTH_C20, Constants.WGS84_EARTH_EQUATORIAL_RADIUS ); + Constants.WGS84_EARTH_C20, Constants.WGS84_EARTH_EQUATORIAL_RADIUS ); phase.addModifier(modifier); final EstimatedMeasurement estimatedAfter = phase.estimate(0, 0, states); diff --git a/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockInterSatellitesRangeModifierTest.java b/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockInterSatellitesRangeModifierTest.java index 50fdcb99e3..0c7ac6da2b 100644 --- a/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockInterSatellitesRangeModifierTest.java +++ b/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockInterSatellitesRangeModifierTest.java @@ -65,7 +65,7 @@ public void testRelativisticClockCorrection() { 1.0, 1.0); // Inter-satellites range before applying the modifier - final EstimatedMeasurementBase estimatedBefore = range.estimateWithoutDerivatives(0, 0, states); + final EstimatedMeasurementBase estimatedBefore = range.estimateWithoutDerivatives(states); // Inter-satellites range before applying the modifier final EstimationModifier modifier = new RelativisticJ2ClockInterSatellitesRangeModifier(Constants.WGS84_EARTH_MU, diff --git a/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockOneWayGNSSPhaseModifierTest.java b/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockOneWayGNSSPhaseModifierTest.java index b2b1421ee1..4aaf0e5189 100644 --- a/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockOneWayGNSSPhaseModifierTest.java +++ b/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockOneWayGNSSPhaseModifierTest.java @@ -25,6 +25,8 @@ import org.orekit.estimation.measurements.EstimatedMeasurementBase; import org.orekit.estimation.measurements.EstimationModifier; import org.orekit.estimation.measurements.ObservableSatellite; +import org.orekit.estimation.measurements.QuadraticClockModel; +import org.orekit.estimation.measurements.gnss.AmbiguityCache; import org.orekit.estimation.measurements.gnss.OneWayGNSSPhase; import org.orekit.gnss.Frequency; import org.orekit.propagation.SpacecraftState; @@ -36,15 +38,16 @@ /** * Check against prediction in - * - * "Springer Handbook oƒ Global Navigation Satellite Systems, Teunissen, Montenbruck" - * + * "Springer Handbook oƒ Global Navigation Satellite Systems, Teunissen, Montenbruck". + *

          * An approximate value is given in terms of delay for Galileo satellites. * As these satellites are close to GPS satellites, we consider the delays to be * of the same order, namely around 62ps. - * + *

          + *

          * The values produced by the modifiers are translated in terms of delay and checked against * the approximate value. + *

          */ public class RelativisticJ2ClockOneWayGNSSPhaseModifierTest { @@ -55,8 +58,9 @@ public class RelativisticJ2ClockOneWayGNSSPhaseModifierTest { /** Spacecraft states. */ private static SpacecraftState[] states; + @Deprecated @Test - public void testRelativisticClockCorrection() { + public void testRelativisticClockCorrectionDeprecated() { // Measurement final double wavelength = Frequency.G01.getWavelength(); @@ -66,11 +70,39 @@ public void testRelativisticClockCorrection() { wavelength, 1.0, 1.0, new ObservableSatellite(0)); // One-way GNSS phase before applying the modifier - final EstimatedMeasurementBase estimatedBefore = phase.estimateWithoutDerivatives(0, 0, states); + final EstimatedMeasurementBase estimatedBefore = phase.estimateWithoutDerivatives(states); + + // One-way GNSS phase before applying the modifier + final EstimationModifier modifier = new RelativisticJ2ClockOneWayGNSSPhaseModifier(Constants.WGS84_EARTH_MU, + Constants.WGS84_EARTH_C20, Constants.WGS84_EARTH_EQUATORIAL_RADIUS ); + phase.addModifier(modifier); + final EstimatedMeasurement estimatedAfter = phase.estimate(0, 0, states); + + // Verify : According to Teunissen and Montenbruck, the delay is supposed to be around 62 ps for Galileo. + // The computed value is equal to 67.284 ps, therefore lying in the supposed range. + Assertions.assertEquals(-0.106217, estimatedBefore.getEstimatedValue()[0] - estimatedAfter.getEstimatedValue()[0], 1.0e-6); + Assertions.assertEquals(0, modifier.getParametersDrivers().size()); + + } + + @Test + public void testRelativisticClockCorrection() { + + // Measurement + final double wavelength = Frequency.G01.getWavelength(); + final OneWayGNSSPhase phase = new OneWayGNSSPhase(states[1].getOrbit(), "remote", + new QuadraticClockModel(date, 0.0, 0.0, 0.0), date, + Vector3D.distance(states[0].getPosition(), + states[1].getPosition()) / wavelength, + wavelength, 1.0, 1.0, new ObservableSatellite(0), + new AmbiguityCache()); + + // One-way GNSS phase before applying the modifier + final EstimatedMeasurementBase estimatedBefore = phase.estimateWithoutDerivatives(states); // One-way GNSS phase before applying the modifier final EstimationModifier modifier = new RelativisticJ2ClockOneWayGNSSPhaseModifier(Constants.WGS84_EARTH_MU, - Constants.WGS84_EARTH_C20, Constants.WGS84_EARTH_EQUATORIAL_RADIUS ); + Constants.WGS84_EARTH_C20, Constants.WGS84_EARTH_EQUATORIAL_RADIUS ); phase.addModifier(modifier); final EstimatedMeasurement estimatedAfter = phase.estimate(0, 0, states); diff --git a/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockOneWayGNSSRangeModifierTest.java b/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockOneWayGNSSRangeModifierTest.java index 723b0efc97..34ca7aff5e 100644 --- a/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockOneWayGNSSRangeModifierTest.java +++ b/src/test/java/org/orekit/estimation/measurements/modifiers/RelativisticJ2ClockOneWayGNSSRangeModifierTest.java @@ -64,7 +64,7 @@ public void testRelativisticJ2ClockCorrection() { 1.0, 1.0, new ObservableSatellite(0)); // Inter-satellites range before applying the modifier - final EstimatedMeasurementBase estimatedBefore = range.estimateWithoutDerivatives(0, 0, states); + final EstimatedMeasurementBase estimatedBefore = range.estimateWithoutDerivatives(states); // Inter-satellites range before applying the modifier final EstimationModifier modifier = new RelativisticJ2ClockOneWayGNSSRangeModifier(Constants.WGS84_EARTH_MU, diff --git a/src/test/java/org/orekit/estimation/measurements/modifiers/ShapiroInterSatellitePhaseModifierTest.java b/src/test/java/org/orekit/estimation/measurements/modifiers/ShapiroInterSatellitePhaseModifierTest.java index 6ed6abf7b8..6aa871a44c 100644 --- a/src/test/java/org/orekit/estimation/measurements/modifiers/ShapiroInterSatellitePhaseModifierTest.java +++ b/src/test/java/org/orekit/estimation/measurements/modifiers/ShapiroInterSatellitePhaseModifierTest.java @@ -100,7 +100,7 @@ private void doTestShapiro(final double expectedMin, final double expectedMean, p3.propagate(sr.getDate()), ephemeris.propagate(sr.getDate()) }; - EstimatedMeasurementBase evalNoMod = sr.estimateWithoutDerivatives(0, 0, states); + EstimatedMeasurementBase evalNoMod = sr.estimateWithoutDerivatives(states); // add modifier sr.addModifier(modifier); @@ -109,7 +109,7 @@ private void doTestShapiro(final double expectedMin, final double expectedMean, found = found || existing == modifier; } Assertions.assertTrue(found); - EstimatedMeasurementBase eval = sr.estimateWithoutDerivatives(0, 0, states); + EstimatedMeasurementBase eval = sr.estimateWithoutDerivatives(states); stat.addValue(eval.getEstimatedValue()[0] - evalNoMod.getEstimatedValue()[0]); diff --git a/src/test/java/org/orekit/estimation/measurements/modifiers/ShapiroInterSatelliteRangeModifierTest.java b/src/test/java/org/orekit/estimation/measurements/modifiers/ShapiroInterSatelliteRangeModifierTest.java index 67770c21a2..16c96d59d2 100644 --- a/src/test/java/org/orekit/estimation/measurements/modifiers/ShapiroInterSatelliteRangeModifierTest.java +++ b/src/test/java/org/orekit/estimation/measurements/modifiers/ShapiroInterSatelliteRangeModifierTest.java @@ -113,7 +113,7 @@ private void doTestShapiro(final boolean twoWay, p3.propagate(sr.getDate()), ephemeris.propagate(sr.getDate()) }; - EstimatedMeasurementBase evalNoMod = sr.estimateWithoutDerivatives(0, 0, states); + EstimatedMeasurementBase evalNoMod = sr.estimateWithoutDerivatives(states); // add modifier sr.addModifier(modifier); @@ -122,7 +122,7 @@ private void doTestShapiro(final boolean twoWay, found = found || existing == modifier; } Assertions.assertTrue(found); - EstimatedMeasurementBase eval = sr.estimateWithoutDerivatives(0, 0, states); + EstimatedMeasurementBase eval = sr.estimateWithoutDerivatives(states); stat.addValue(eval.getEstimatedValue()[0] - evalNoMod.getEstimatedValue()[0]); diff --git a/src/test/java/org/orekit/estimation/measurements/modifiers/ShapiroOneWayGNSSPhaseModifierTest.java b/src/test/java/org/orekit/estimation/measurements/modifiers/ShapiroOneWayGNSSPhaseModifierTest.java index d648be4526..fb81acf8c5 100644 --- a/src/test/java/org/orekit/estimation/measurements/modifiers/ShapiroOneWayGNSSPhaseModifierTest.java +++ b/src/test/java/org/orekit/estimation/measurements/modifiers/ShapiroOneWayGNSSPhaseModifierTest.java @@ -82,6 +82,7 @@ private void doTestShapiro(final double expectedMin, final double expectedMean, List> measurements = EstimationTestUtils.createMeasurements(p1, new OneWayGNSSPhaseCreator(ephemeris, + "remote", FREQUENCY, ambiguity, localClockOffset, @@ -94,13 +95,14 @@ private void doTestShapiro(final double expectedMin, final double expectedMean, final Propagator p3 = EstimationTestUtils.createPropagator(context.initialOrbit, propagatorBuilder); DescriptiveStatistics stat = new DescriptiveStatistics(); - for (int i = 0; i < measurements.size(); ++i) { - OneWayGNSSPhase sr = (OneWayGNSSPhase) measurements.get(i); + for (ObservedMeasurement measurement : measurements) { + OneWayGNSSPhase sr = (OneWayGNSSPhase) measurement; SpacecraftState[] states = new SpacecraftState[] { - p3.propagate(sr.getDate()), - ephemeris.propagate(sr.getDate()) + p3.propagate(sr.getDate()), ephemeris.propagate(sr.getDate()) }; - EstimatedMeasurementBase evalNoMod = sr.estimateWithoutDerivatives(0, 0, states); + EstimatedMeasurementBase + evalNoMod = + sr.estimateWithoutDerivatives(states); // add modifier sr.addModifier(modifier); @@ -109,9 +111,12 @@ private void doTestShapiro(final double expectedMin, final double expectedMean, found = found || existing == modifier; } Assertions.assertTrue(found); - EstimatedMeasurementBase eval = sr.estimateWithoutDerivatives(0, 0, states); + EstimatedMeasurementBase + eval = + sr.estimateWithoutDerivatives(states); - stat.addValue(eval.getEstimatedValue()[0] - evalNoMod.getEstimatedValue()[0]); + stat.addValue( + eval.getEstimatedValue()[0] - evalNoMod.getEstimatedValue()[0]); } final double wavelength = ((OneWayGNSSPhase) measurements.get(0)).getWavelength(); diff --git a/src/test/java/org/orekit/estimation/measurements/modifiers/ShapiroOneWayGNSSRangeModifierTest.java b/src/test/java/org/orekit/estimation/measurements/modifiers/ShapiroOneWayGNSSRangeModifierTest.java index c612f3236e..05a4f3e5ab 100644 --- a/src/test/java/org/orekit/estimation/measurements/modifiers/ShapiroOneWayGNSSRangeModifierTest.java +++ b/src/test/java/org/orekit/estimation/measurements/modifiers/ShapiroOneWayGNSSRangeModifierTest.java @@ -93,7 +93,7 @@ private void doTestShapiro(final double expectedMin, final double expectedMean, p3.propagate(sr.getDate()), ephemeris.propagate(sr.getDate()) }; - EstimatedMeasurementBase evalNoMod = sr.estimateWithoutDerivatives(0, 0, states); + EstimatedMeasurementBase evalNoMod = sr.estimateWithoutDerivatives(states); // add modifier sr.addModifier(modifier); @@ -102,7 +102,7 @@ private void doTestShapiro(final double expectedMin, final double expectedMean, found = found || existing == modifier; } Assertions.assertTrue(found); - EstimatedMeasurementBase eval = sr.estimateWithoutDerivatives(0, 0, states); + EstimatedMeasurementBase eval = sr.estimateWithoutDerivatives(states); stat.addValue(eval.getEstimatedValue()[0] - evalNoMod.getEstimatedValue()[0]); diff --git a/src/test/java/org/orekit/estimation/measurements/modifiers/ShapiroPhaseModifierTest.java b/src/test/java/org/orekit/estimation/measurements/modifiers/ShapiroPhaseModifierTest.java index 594d287480..fbfb1e37ed 100644 --- a/src/test/java/org/orekit/estimation/measurements/modifiers/ShapiroPhaseModifierTest.java +++ b/src/test/java/org/orekit/estimation/measurements/modifiers/ShapiroPhaseModifierTest.java @@ -66,7 +66,8 @@ private void doTestShapiro(final double expectedMin, final double expectedMean, final double satClockOffset = 345.0e-6; List> measurements = EstimationTestUtils.createMeasurements(propagator, - new PhaseMeasurementCreator(context, FREQUENCY, + new PhaseMeasurementCreator(context, + FREQUENCY, ambiguity, satClockOffset), 1.0, 3.0, 300.0); @@ -94,7 +95,7 @@ private void doTestShapiro(final double expectedMin, final double expectedMean, found = found || existing == modifier; } Assertions.assertTrue(found); - EstimatedMeasurementBase eval = phase.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { refstate }); + EstimatedMeasurementBase eval = phase.estimateWithoutDerivatives( new SpacecraftState[] { refstate }); stat.addValue(eval.getEstimatedValue()[0] - evalNoMod.getEstimatedValue()[0]); diff --git a/src/test/java/org/orekit/estimation/measurements/modifiers/ShapiroRangeModifierTest.java b/src/test/java/org/orekit/estimation/measurements/modifiers/ShapiroRangeModifierTest.java index a50072fafb..5450b5d908 100644 --- a/src/test/java/org/orekit/estimation/measurements/modifiers/ShapiroRangeModifierTest.java +++ b/src/test/java/org/orekit/estimation/measurements/modifiers/ShapiroRangeModifierTest.java @@ -100,7 +100,7 @@ private void doTestShapiro(final boolean twoWay, found = found || existing == modifier; } Assertions.assertTrue(found); - EstimatedMeasurementBase eval = range.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { refstate }); + EstimatedMeasurementBase eval = range.estimateWithoutDerivatives( new SpacecraftState[] { refstate }); stat.addValue(eval.getEstimatedValue()[0] - evalNoMod.getEstimatedValue()[0]); diff --git a/src/test/java/org/orekit/estimation/measurements/modifiers/TropoModifierTest.java b/src/test/java/org/orekit/estimation/measurements/modifiers/TropoModifierTest.java index 6c95eadf46..1faf7a49c8 100644 --- a/src/test/java/org/orekit/estimation/measurements/modifiers/TropoModifierTest.java +++ b/src/test/java/org/orekit/estimation/measurements/modifiers/TropoModifierTest.java @@ -16,14 +16,9 @@ */ package org.orekit.estimation.measurements.modifiers; -import java.util.List; -import java.util.Map; - import org.hipparchus.util.MathUtils; import org.hipparchus.util.Precision; -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.orekit.estimation.Context; import org.orekit.estimation.EstimationTestUtils; @@ -50,9 +45,9 @@ import org.orekit.frames.TopocentricFrame; import org.orekit.gnss.Frequency; import org.orekit.models.earth.EarthITU453AtmosphereRefraction; -import org.orekit.models.earth.troposphere.EstimatedTroposphericModel; -import org.orekit.models.earth.troposphere.NiellMappingFunctionModel; +import org.orekit.models.earth.troposphere.EstimatedModel; import org.orekit.models.earth.troposphere.ModifiedSaastamoinenModel; +import org.orekit.models.earth.troposphere.NiellMappingFunctionModel; import org.orekit.orbits.OrbitType; import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.Propagator; @@ -61,17 +56,10 @@ import org.orekit.time.AbsoluteDate; import org.orekit.utils.ParameterDriver; -public class TropoModifierTest { - - @BeforeEach - public void setUp() throws Exception { - - } - - @AfterEach - public void tearDown() { +import java.util.List; +import java.util.Map; - } +public class TropoModifierTest { @Test public void testRangeTropoModifier() { @@ -105,7 +93,7 @@ public void testRangeTropoModifier() { final SpacecraftState refState = propagator.propagate(date); Range range = (Range) measurement; - EstimatedMeasurementBase evalNoMod = range.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { refState }); + EstimatedMeasurementBase evalNoMod = range.estimateWithoutDerivatives(new SpacecraftState[] { refState }); // add modifier @@ -117,6 +105,12 @@ public void testRangeTropoModifier() { final double epsilon = 1e-6; Assertions.assertTrue(Precision.compareTo(diffMeters, 12., epsilon) < 0); Assertions.assertTrue(Precision.compareTo(diffMeters, 0., epsilon) > 0); + Assertions.assertEquals(evalNoMod.getEstimatedValue()[0], + eval.getOriginalEstimatedValue()[0], + 3.0e-14 * evalNoMod.getEstimatedValue()[0]); + Assertions.assertEquals(eval.getEstimatedValue()[0] - eval.getOriginalEstimatedValue()[0], + eval.getAppliedEffects().get(modifier)[0], + 1.0e-15); } } @@ -143,20 +137,20 @@ public void testRangeEstimatedTropoModifier() { final SpacecraftState refState = propagator.propagate(date); Range range = (Range) measurement; - EstimatedMeasurementBase evalNoMod = range.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { refState }); + EstimatedMeasurementBase evalNoMod = range.estimateWithoutDerivatives(new SpacecraftState[] { refState }); // add modifier - final GroundStation stationParameter = ((Range) measurement).getStation(); - final TopocentricFrame baseFrame = stationParameter.getBaseFrame(); - final NiellMappingFunctionModel mappingFunction = new NiellMappingFunctionModel(); - final EstimatedTroposphericModel tropoModel = new EstimatedTroposphericModel(mappingFunction, 5.0); - final RangeTroposphericDelayModifier modifier = new RangeTroposphericDelayModifier(tropoModel); + final GroundStation stationParameter = ((Range) measurement).getStation(); + final TopocentricFrame baseFrame = stationParameter.getBaseFrame(); + final NiellMappingFunctionModel mappingFunction = new NiellMappingFunctionModel(); + final EstimatedModel tropoModel = new EstimatedModel(mappingFunction, 5.0); + final RangeTroposphericDelayModifier modifier = new RangeTroposphericDelayModifier(tropoModel); final ParameterDriver parameterDriver = modifier.getParametersDrivers().get(0); parameterDriver.setSelected(true); - parameterDriver.setName(baseFrame.getName() + EstimatedTroposphericModel.TOTAL_ZENITH_DELAY); + parameterDriver.setName(baseFrame.getName() + EstimatedModel.TOTAL_ZENITH_DELAY); range.addModifier(modifier); - EstimatedMeasurementBase eval = range.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { refState }); + EstimatedMeasurementBase eval = range.estimateWithoutDerivatives(new SpacecraftState[] { refState }); final double diffMeters = eval.getEstimatedValue()[0] - evalNoMod.getEstimatedValue()[0]; @@ -192,7 +186,8 @@ public void testPhaseTropoModifier() { final double satClockOffset = 345.0e-6; final List> measurements = EstimationTestUtils.createMeasurements(propagator, - new PhaseMeasurementCreator(context, Frequency.G01, + new PhaseMeasurementCreator(context, + Frequency.G01, ambiguity, satClockOffset), 1.0, 3.0, 300.0); @@ -206,12 +201,12 @@ public void testPhaseTropoModifier() { final SpacecraftState refState = propagator.propagate(date); Phase phase = (Phase) measurement; - EstimatedMeasurementBase evalNoMod = phase.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { refState }); + EstimatedMeasurementBase evalNoMod = phase.estimateWithoutDerivatives(new SpacecraftState[] { refState }); // add modifier phase.addModifier(modifier); - EstimatedMeasurementBase eval = phase.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { refState }); + EstimatedMeasurementBase eval = phase.estimateWithoutDerivatives(new SpacecraftState[] { refState }); final double diffMeters = (eval.getEstimatedValue()[0] - evalNoMod.getEstimatedValue()[0]) * phase.getWavelength(); @@ -240,7 +235,8 @@ public void testPhaseEstimatedTropoModifier() { final double satClockOffset = 345.0e-6; final List> measurements = EstimationTestUtils.createMeasurements(propagator, - new PhaseMeasurementCreator(context, Frequency.G01, + new PhaseMeasurementCreator(context, + Frequency.G01, ambiguity, satClockOffset), 1.0, 3.0, 300.0); @@ -252,21 +248,21 @@ public void testPhaseEstimatedTropoModifier() { final SpacecraftState refState = propagator.propagate(date); Phase phase = (Phase) measurement; - EstimatedMeasurementBase evalNoMod = phase.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { refState }); + EstimatedMeasurementBase evalNoMod = phase.estimateWithoutDerivatives(new SpacecraftState[] { refState }); // add modifier - final GroundStation stationParameter = phase.getStation(); - final TopocentricFrame baseFrame = stationParameter.getBaseFrame(); - final NiellMappingFunctionModel mappingFunction = new NiellMappingFunctionModel(); - final EstimatedTroposphericModel tropoModel = new EstimatedTroposphericModel(mappingFunction, 5.0); - final PhaseTroposphericDelayModifier modifier = new PhaseTroposphericDelayModifier(tropoModel); + final GroundStation stationParameter = phase.getStation(); + final TopocentricFrame baseFrame = stationParameter.getBaseFrame(); + final NiellMappingFunctionModel mappingFunction = new NiellMappingFunctionModel(); + final EstimatedModel tropoModel = new EstimatedModel(mappingFunction, 5.0); + final PhaseTroposphericDelayModifier modifier = new PhaseTroposphericDelayModifier(tropoModel); final ParameterDriver parameterDriver = modifier.getParametersDrivers().get(0); parameterDriver.setSelected(true); - parameterDriver.setName(baseFrame.getName() + EstimatedTroposphericModel.TOTAL_ZENITH_DELAY); + parameterDriver.setName(baseFrame.getName() + EstimatedModel.TOTAL_ZENITH_DELAY); phase.addModifier(modifier); - EstimatedMeasurementBase eval = phase.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { refState }); + EstimatedMeasurementBase eval = phase.estimateWithoutDerivatives(new SpacecraftState[] { refState }); final double diffMeters = (eval.getEstimatedValue()[0] - evalNoMod.getEstimatedValue()[0]) * phase.getWavelength(); @@ -314,7 +310,7 @@ public void testTurnAroundRangeTropoModifier() { final SpacecraftState refState = propagator.propagate(date); TurnAroundRange turnAroundRange = (TurnAroundRange) measurement; - EstimatedMeasurementBase evalNoMod = turnAroundRange.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { refState }); + EstimatedMeasurementBase evalNoMod = turnAroundRange.estimateWithoutDerivatives(new SpacecraftState[] { refState }); // add modifier turnAroundRange.addModifier(modifier); @@ -364,20 +360,18 @@ public void testBistaticRangeTropoModifier() { final SpacecraftState refState = propagator.propagate(biRange.getDate()); // Estimate without modifier - EstimatedMeasurementBase evalNoMod = biRange.estimateWithoutDerivatives(0, 0, - new SpacecraftState[] { refState }); + EstimatedMeasurementBase evalNoMod = biRange.estimateWithoutDerivatives(new SpacecraftState[] { refState }); // add modifier biRange.addModifier(modifier); // Estimate with modifier - EstimatedMeasurementBase eval = biRange.estimateWithoutDerivatives(0, 0, - new SpacecraftState[] { refState }); + EstimatedMeasurementBase eval = biRange.estimateWithoutDerivatives(new SpacecraftState[] { refState }); final double diffMeters = eval.getEstimatedValue()[0] - evalNoMod.getEstimatedValue()[0]; - Assertions.assertTrue(diffMeters < 9.0); - Assertions.assertTrue(diffMeters > 5.0); + Assertions.assertTrue(diffMeters < 9.1); + Assertions.assertTrue(diffMeters > 5.8); } } @@ -415,15 +409,13 @@ public void testBistaticRangeRateTropoModifier() { final SpacecraftState refState = propagator.propagate(biRangeRate.getDate()); // Estimate without modifier - EstimatedMeasurementBase evalNoMod = biRangeRate.estimateWithoutDerivatives(0, 0, - new SpacecraftState[] { refState }); + EstimatedMeasurementBase evalNoMod = biRangeRate.estimateWithoutDerivatives(new SpacecraftState[] { refState }); // add modifier biRangeRate.addModifier(modifier); // Estimate with modifier - EstimatedMeasurementBase eval = biRangeRate.estimateWithoutDerivatives(0, 0, - new SpacecraftState[] { refState }); + EstimatedMeasurementBase eval = biRangeRate.estimateWithoutDerivatives(new SpacecraftState[] { refState }); final double diffMetersSec = eval.getEstimatedValue()[0] - evalNoMod.getEstimatedValue()[0]; @@ -464,25 +456,23 @@ public void testBistaticRangeRateEstimatedTropoModifier() { final SpacecraftState refState = propagator.propagate(biRangeRate.getDate()); // Estimate without modifier - EstimatedMeasurementBase evalNoMod = biRangeRate.estimateWithoutDerivatives(0, 0, - new SpacecraftState[] { refState }); + EstimatedMeasurementBase evalNoMod = biRangeRate.estimateWithoutDerivatives(new SpacecraftState[] { refState }); // add modifier final NiellMappingFunctionModel mappingFunc = new NiellMappingFunctionModel(); - final EstimatedTroposphericModel tropoModel = new EstimatedTroposphericModel(mappingFunc, 5.0); + final EstimatedModel tropoModel = new EstimatedModel(mappingFunc, 5.0); final BistaticRangeRateTroposphericDelayModifier modifier = new BistaticRangeRateTroposphericDelayModifier(tropoModel); final TopocentricFrame baseFrame = biRangeRate.getReceiverStation().getBaseFrame(); final ParameterDriver parameterDriver = modifier.getParametersDrivers().get(0); parameterDriver.setSelected(true); - parameterDriver.setName(baseFrame.getName() + EstimatedTroposphericModel.TOTAL_ZENITH_DELAY); + parameterDriver.setName(baseFrame.getName() + EstimatedModel.TOTAL_ZENITH_DELAY); biRangeRate.addModifier(modifier); // Estimate with modifier - EstimatedMeasurementBase eval = biRangeRate.estimateWithoutDerivatives(0, 0, - new SpacecraftState[] { refState }); + EstimatedMeasurementBase eval = biRangeRate.estimateWithoutDerivatives(new SpacecraftState[] { refState }); final double diffMetersSec = eval.getEstimatedValue()[0] - evalNoMod.getEstimatedValue()[0]; @@ -527,13 +517,13 @@ public void testTDOATropoModifier() { final SpacecraftState refState = propagator.propagate(tdoa.getDate()); // Estimate without modifier - EstimatedMeasurementBase evalNoMod = tdoa.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { refState }); + EstimatedMeasurementBase evalNoMod = tdoa.estimateWithoutDerivatives(new SpacecraftState[] { refState }); // add modifier tdoa.addModifier(modifier); // Estimate with modifier - EstimatedMeasurementBase eval = tdoa.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { refState }); + EstimatedMeasurementBase eval = tdoa.estimateWithoutDerivatives(new SpacecraftState[] { refState }); final double diffSec = eval.getEstimatedValue()[0] - evalNoMod.getEstimatedValue()[0]; @@ -575,17 +565,17 @@ public void testTDOAEstimatedTropoModifier() { final SpacecraftState refState = propagator.propagate(tdoa.getDate()); // Estimate without modifier - EstimatedMeasurementBase evalNoMod = tdoa.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { refState }); + EstimatedMeasurementBase evalNoMod = tdoa.estimateWithoutDerivatives(new SpacecraftState[] { refState }); // add modifier - final NiellMappingFunctionModel mappingFunct = new NiellMappingFunctionModel(); - final EstimatedTroposphericModel tropoModel = new EstimatedTroposphericModel(mappingFunct, 5.0); - final TDOATroposphericDelayModifier modifier = new TDOATroposphericDelayModifier(tropoModel); + final NiellMappingFunctionModel mappingFunct = new NiellMappingFunctionModel(); + final EstimatedModel tropoModel = new EstimatedModel(mappingFunct, 5.0); + final TDOATroposphericDelayModifier modifier = new TDOATroposphericDelayModifier(tropoModel); final TopocentricFrame baseFrame = tdoa.getPrimeStation().getBaseFrame(); final ParameterDriver parameterDriver = modifier.getParametersDrivers().get(0); parameterDriver.setSelected(true); - parameterDriver.setName(baseFrame.getName() + EstimatedTroposphericModel.TOTAL_ZENITH_DELAY); + parameterDriver.setName(baseFrame.getName() + EstimatedModel.TOTAL_ZENITH_DELAY); tdoa.addModifier(modifier); @@ -596,7 +586,7 @@ public void testTDOAEstimatedTropoModifier() { final double epsilon = 5.e-11; Assertions.assertTrue(Precision.compareTo(diffSec, 4.90e-9, epsilon) < 0); - Assertions.assertTrue(Precision.compareTo(diffSec, -2.20e-9, epsilon) > 0); + Assertions.assertTrue(Precision.compareTo(diffSec, -2.21e-9, epsilon) > 0); } } @@ -633,7 +623,7 @@ public void testRangeRateTropoModifier() { final SpacecraftState refState = propagator.propagate(date); RangeRate rangeRate = (RangeRate) measurement; - EstimatedMeasurementBase evalNoMod = rangeRate.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { refState }); + EstimatedMeasurementBase evalNoMod = rangeRate.estimateWithoutDerivatives(new SpacecraftState[] { refState }); // add modifier rangeRate.addModifier(modifier); @@ -673,22 +663,22 @@ public void testRangeRateEstimatedTropoModifier() { final SpacecraftState refState = propagator.propagate(date); RangeRate rangeRate = (RangeRate) measurement; - EstimatedMeasurementBase evalNoMod = rangeRate.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { refState }); + EstimatedMeasurementBase evalNoMod = rangeRate.estimateWithoutDerivatives(new SpacecraftState[] { refState }); // add modifier - final GroundStation stationParameter = ((RangeRate) measurement).getStation(); - final TopocentricFrame baseFrame = stationParameter.getBaseFrame(); - final NiellMappingFunctionModel mappingFunction = new NiellMappingFunctionModel(); - final EstimatedTroposphericModel tropoModel = new EstimatedTroposphericModel(mappingFunction, 5.0); - final RangeRateTroposphericDelayModifier modifier = new RangeRateTroposphericDelayModifier(tropoModel, false); + final GroundStation stationParameter = ((RangeRate) measurement).getStation(); + final TopocentricFrame baseFrame = stationParameter.getBaseFrame(); + final NiellMappingFunctionModel mappingFunction = new NiellMappingFunctionModel(); + final EstimatedModel tropoModel = new EstimatedModel(mappingFunction, 5.0); + final RangeRateTroposphericDelayModifier modifier = new RangeRateTroposphericDelayModifier(tropoModel, false); final ParameterDriver parameterDriver = modifier.getParametersDrivers().get(0); parameterDriver.setSelected(true); - parameterDriver.setName(baseFrame.getName() + EstimatedTroposphericModel.TOTAL_ZENITH_DELAY); + parameterDriver.setName(baseFrame.getName() + EstimatedModel.TOTAL_ZENITH_DELAY); rangeRate.addModifier(modifier); // - EstimatedMeasurementBase eval = rangeRate.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { refState }); + EstimatedMeasurementBase eval = rangeRate.estimateWithoutDerivatives(new SpacecraftState[] { refState }); final double diffMetersSec = eval.getEstimatedValue()[0] - evalNoMod.getEstimatedValue()[0]; @@ -698,108 +688,6 @@ public void testRangeRateEstimatedTropoModifier() { } } - @Test - public void testAngularTropoModifier() { - - Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides"); - - final NumericalPropagatorBuilder propagatorBuilder = - context.createBuilder(OrbitType.KEPLERIAN, PositionAngleType.TRUE, true, - 1.0e-6, 60.0, 0.001); - - // create perfect angular measurements - for (final GroundStation station : context.stations) { - station.getClockOffsetDriver().setSelected(true); - station.getEastOffsetDriver().setSelected(true); - station.getNorthOffsetDriver().setSelected(true); - station.getZenithOffsetDriver().setSelected(true); - } - final Propagator propagator = EstimationTestUtils.createPropagator(context.initialOrbit, - propagatorBuilder); - final List> measurements = - EstimationTestUtils.createMeasurements(propagator, - new AngularAzElMeasurementCreator(context), - 1.0, 3.0, 300.0); - propagator.clearStepHandlers(); - - final AngularTroposphericDelayModifier modifier = new AngularTroposphericDelayModifier(ModifiedSaastamoinenModel.getStandardModel()); - - for (final ObservedMeasurement measurement : measurements) { - final AbsoluteDate date = measurement.getDate(); - - final SpacecraftState refState = propagator.propagate(date); - - AngularAzEl angular = (AngularAzEl) measurement; - EstimatedMeasurementBase evalNoMod = angular.estimateWithoutDerivatives(0, 0, - new SpacecraftState[] { refState }); - - // add modifier - angular.addModifier(modifier); - // - EstimatedMeasurementBase eval = angular.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { refState }); - - final double diffAz = MathUtils.normalizeAngle(eval.getEstimatedValue()[0], evalNoMod.getEstimatedValue()[0]) - evalNoMod.getEstimatedValue()[0]; - final double diffEl = MathUtils.normalizeAngle(eval.getEstimatedValue()[1], evalNoMod.getEstimatedValue()[1]) - evalNoMod.getEstimatedValue()[1]; - // TODO: check threshold - Assertions.assertEquals(0.0, diffAz, 5.0e-5); - Assertions.assertEquals(0.0, diffEl, 5.0e-6); - } - } - - @Test - public void testAngularEstimatedTropoModifier() { - - Context context = EstimationTestUtils.eccentricContext("regular-data:potential:tides"); - - final NumericalPropagatorBuilder propagatorBuilder = - context.createBuilder(OrbitType.KEPLERIAN, PositionAngleType.TRUE, true, - 1.0e-6, 60.0, 0.001); - - // create perfect angular measurements - for (final GroundStation station : context.stations) { - station.getEastOffsetDriver().setSelected(true); - station.getNorthOffsetDriver().setSelected(true); - station.getZenithOffsetDriver().setSelected(true); - } - final Propagator propagator = EstimationTestUtils.createPropagator(context.initialOrbit, - propagatorBuilder); - final List> measurements = - EstimationTestUtils.createMeasurements(propagator, - new AngularAzElMeasurementCreator(context), - 1.0, 3.0, 300.0); - propagator.clearStepHandlers(); - - for (final ObservedMeasurement measurement : measurements) { - final AbsoluteDate date = measurement.getDate(); - - final SpacecraftState refState = propagator.propagate(date); - - AngularAzEl angular = (AngularAzEl) measurement; - EstimatedMeasurementBase evalNoMod = angular.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { refState }); - - // add modifier - final GroundStation stationParameter = ((AngularAzEl) measurement).getStation(); - final TopocentricFrame baseFrame = stationParameter.getBaseFrame(); - final NiellMappingFunctionModel mappingFunction = new NiellMappingFunctionModel(); - final EstimatedTroposphericModel tropoModel = new EstimatedTroposphericModel(mappingFunction, 5.0); - final AngularTroposphericDelayModifier modifier = new AngularTroposphericDelayModifier(tropoModel); - - final ParameterDriver parameterDriver = modifier.getParametersDrivers().get(0); - parameterDriver.setSelected(true); - parameterDriver.setName(baseFrame.getName() + EstimatedTroposphericModel.TOTAL_ZENITH_DELAY); - angular.addModifier(modifier); - // - EstimatedMeasurementBase eval = angular.estimateWithoutDerivatives(0, 0, - new SpacecraftState[] { refState }); - - final double diffAz = MathUtils.normalizeAngle(eval.getEstimatedValue()[0], evalNoMod.getEstimatedValue()[0]) - evalNoMod.getEstimatedValue()[0]; - final double diffEl = MathUtils.normalizeAngle(eval.getEstimatedValue()[1], evalNoMod.getEstimatedValue()[1]) - evalNoMod.getEstimatedValue()[1]; - - Assertions.assertEquals(0.0, diffAz, 1.9e-5); - Assertions.assertEquals(0.0, diffEl, 2.1e-6); - } - } - @Test public void testAngularRadioRefractionModifier() { @@ -832,7 +720,7 @@ public void testAngularRadioRefractionModifier() { final SpacecraftState refState = propagator.propagate(date); AngularAzEl angular = (AngularAzEl) measurement; - EstimatedMeasurementBase evalNoMod = angular.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { refState }); + EstimatedMeasurementBase evalNoMod = angular.estimateWithoutDerivatives(new SpacecraftState[] { refState }); // get the altitude of the station (in kilometers) final double altitude = angular.getStation().getBaseFrame().getPoint().getAltitude() / 1000.; @@ -841,7 +729,7 @@ public void testAngularRadioRefractionModifier() { // add modifier angular.addModifier(modifier); // - EstimatedMeasurementBase eval = angular.estimateWithoutDerivatives(0, 0, new SpacecraftState[] { refState }); + EstimatedMeasurementBase eval = angular.estimateWithoutDerivatives(new SpacecraftState[] { refState }); final double diffEl = MathUtils.normalizeAngle(eval.getEstimatedValue()[1], evalNoMod.getEstimatedValue()[1]) - evalNoMod.getEstimatedValue()[1]; // TODO: check threshold diff --git a/src/test/java/org/orekit/estimation/sequential/BrouwerLyddaneKalmanEstimatorTest.java b/src/test/java/org/orekit/estimation/sequential/BrouwerLyddaneKalmanEstimatorTest.java index d317ac1999..9b63de1aa0 100644 --- a/src/test/java/org/orekit/estimation/sequential/BrouwerLyddaneKalmanEstimatorTest.java +++ b/src/test/java/org/orekit/estimation/sequential/BrouwerLyddaneKalmanEstimatorTest.java @@ -16,6 +16,8 @@ */ package org.orekit.estimation.sequential; +import java.util.List; + import org.hipparchus.linear.MatrixUtils; import org.hipparchus.linear.RealMatrix; import org.junit.jupiter.api.Test; @@ -26,11 +28,8 @@ import org.orekit.orbits.Orbit; import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.Propagator; -import org.orekit.propagation.analytical.BrouwerLyddanePropagator; import org.orekit.propagation.conversion.BrouwerLyddanePropagatorBuilder; -import java.util.List; - public class BrouwerLyddaneKalmanEstimatorTest { /** @@ -58,8 +57,7 @@ public void testKeplerianPV() { new PVMeasurementCreator(), 0.0, 3.0, 300.0); // Reference propagator for estimation performances - final BrouwerLyddanePropagator referencePropagator = propagatorBuilder. - buildPropagator(propagatorBuilder.getSelectedNormalizedParameters()); + final Propagator referencePropagator = propagatorBuilder.buildPropagator(); // Reference position/velocity at last measurement date final Orbit refOrbit = referencePropagator. diff --git a/src/test/java/org/orekit/estimation/sequential/ExtendedSemiAnalyticalKalmanFilterTest.java b/src/test/java/org/orekit/estimation/sequential/ExtendedSemiAnalyticalKalmanFilterTest.java index 25a25ba951..d3ce2d1ffe 100644 --- a/src/test/java/org/orekit/estimation/sequential/ExtendedSemiAnalyticalKalmanFilterTest.java +++ b/src/test/java/org/orekit/estimation/sequential/ExtendedSemiAnalyticalKalmanFilterTest.java @@ -1,3 +1,20 @@ +/* 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.estimation.sequential; import java.io.File; diff --git a/src/test/java/org/orekit/estimation/sequential/KalmanEstimatorTest.java b/src/test/java/org/orekit/estimation/sequential/KalmanEstimatorTest.java index 0fae2daef8..8aa9a01d0f 100644 --- a/src/test/java/org/orekit/estimation/sequential/KalmanEstimatorTest.java +++ b/src/test/java/org/orekit/estimation/sequential/KalmanEstimatorTest.java @@ -41,8 +41,8 @@ import org.orekit.estimation.measurements.Position; import org.orekit.estimation.measurements.PositionMeasurementCreator; 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; @@ -55,7 +55,6 @@ import org.orekit.propagation.EphemerisGenerator; import org.orekit.propagation.Propagator; import org.orekit.propagation.conversion.NumericalPropagatorBuilder; -import org.orekit.propagation.numerical.NumericalPropagator; import org.orekit.time.AbsoluteDate; import org.orekit.utils.ParameterDriver; import org.orekit.utils.ParameterDriversList; @@ -104,8 +103,7 @@ public void testKeplerianPV() { new PVMeasurementCreator(), 0.0, 3.0, 300.0); // Reference propagator for estimation performances - final NumericalPropagator referencePropagator = propagatorBuilder. - buildPropagator(propagatorBuilder.getSelectedNormalizedParameters()); + final Propagator referencePropagator = propagatorBuilder.buildPropagator(); // Reference position/velocity at last measurement date final Orbit refOrbit = referencePropagator. @@ -174,8 +172,7 @@ public void testKeplerianRange() { 1.0, 4.0, 60.0); // Reference propagator for estimation performances - final NumericalPropagator referencePropagator = propagatorBuilder. - buildPropagator(propagatorBuilder.getSelectedNormalizedParameters()); + final Propagator referencePropagator = propagatorBuilder.buildPropagator(); // Reference position/velocity at last measurement date final Orbit refOrbit = referencePropagator. @@ -271,8 +268,7 @@ public void testKeplerianRangeWithOnBoardAntennaOffset() { } // Reference propagator for estimation performances - final NumericalPropagator referencePropagator = propagatorBuilder. - buildPropagator(propagatorBuilder.getSelectedNormalizedParameters()); + final Propagator referencePropagator = propagatorBuilder.buildPropagator(); // Reference position/velocity at last measurement date final Orbit refOrbit = referencePropagator. @@ -355,8 +351,7 @@ public void testCartesianRangeRate() { 1.0, 3.0, 300.0); // Reference propagator for estimation performances - final NumericalPropagator referencePropagator = propagatorBuilder. - buildPropagator(propagatorBuilder.getSelectedNormalizedParameters()); + final Propagator referencePropagator = propagatorBuilder.buildPropagator(); // Reference position/velocity at last measurement date final Orbit refOrbit = referencePropagator. @@ -435,8 +430,7 @@ public void testCircularAzimuthElevation() { 1.0, 4.0, 60.0); // Reference propagator for estimation performances - final NumericalPropagator referencePropagator = propagatorBuilder. - buildPropagator(propagatorBuilder.getSelectedNormalizedParameters()); + final Propagator referencePropagator = propagatorBuilder.buildPropagator(); // Reference position/velocity at last measurement date final Orbit refOrbit = referencePropagator. @@ -514,8 +508,7 @@ public void testEquinoctialRightAscensionDeclination() { 1.0, 4.0, 60.0); // Reference propagator for estimation performances - final NumericalPropagator referencePropagator = propagatorBuilder. - buildPropagator(propagatorBuilder.getSelectedNormalizedParameters()); + final Propagator referencePropagator = propagatorBuilder.buildPropagator(); // Reference position/velocity at last measurement date final Orbit refOrbit = referencePropagator. @@ -614,8 +607,7 @@ public void testKeplerianRangeAzElAndRangeRate() { measurements.sort(Comparator.naturalOrder()); // Reference propagator for estimation performances - final NumericalPropagator referencePropagator = measPropagatorBuilder. - buildPropagator(measPropagatorBuilder.getSelectedNormalizedParameters()); + final Propagator referencePropagator = measPropagatorBuilder.buildPropagator(); // Reference position/velocity at last measurement date final Orbit refOrbit = referencePropagator. @@ -708,8 +700,7 @@ public void testKeplerianRangeAndRangeRate() { measurements.addAll(measurementsRangeRate); // Reference propagator for estimation performances - final NumericalPropagator referencePropagator = propagatorBuilder. - buildPropagator(propagatorBuilder.getSelectedNormalizedParameters()); + final Propagator referencePropagator = propagatorBuilder.buildPropagator(); // Reference position/velocity at last measurement date final Orbit refOrbit = referencePropagator. @@ -842,12 +833,8 @@ public void testMultiSat() { 1.0e-6); Orbit[] refOrbits = new Orbit[] { - propagatorBuilder1. - buildPropagator(propagatorBuilder1.getSelectedNormalizedParameters()). - propagate(measurements.get(measurements.size()-1).getDate()).getOrbit(), - propagatorBuilder2. - buildPropagator(propagatorBuilder2.getSelectedNormalizedParameters()). - propagate(measurements.get(measurements.size()-1).getDate()).getOrbit() + propagatorBuilder1.buildPropagator().propagate(measurements.get(measurements.size()-1).getDate()).getOrbit(), + propagatorBuilder2.buildPropagator().propagate(measurements.get(measurements.size()-1).getDate()).getOrbit() }; EstimationTestUtils.checkKalmanFit(context, kalman, measurements, refOrbits, new PositionAngleType[] { PositionAngleType.TRUE, PositionAngleType.TRUE }, @@ -1055,12 +1042,8 @@ public void tesIssue696() { 1.0e-6); Orbit[] refOrbits = new Orbit[] { - propagatorBuilder1. - buildPropagator(propagatorBuilder1.getSelectedNormalizedParameters()). - propagate(measurements.get(measurements.size()-1).getDate()).getOrbit(), - propagatorBuilder2. - buildPropagator(propagatorBuilder2.getSelectedNormalizedParameters()). - propagate(measurements.get(measurements.size()-1).getDate()).getOrbit() + propagatorBuilder1.buildPropagator().propagate(measurements.get(measurements.size()-1).getDate()).getOrbit(), + propagatorBuilder2.buildPropagator().propagate(measurements.get(measurements.size()-1).getDate()).getOrbit() }; EstimationTestUtils.checkKalmanFit(context, kalman, measurements, refOrbits, new PositionAngleType[] { PositionAngleType.TRUE, PositionAngleType.TRUE }, diff --git a/src/test/java/org/orekit/estimation/sequential/KalmanModelTest.java b/src/test/java/org/orekit/estimation/sequential/KalmanModelTest.java index eb9923026a..f09692dd36 100644 --- a/src/test/java/org/orekit/estimation/sequential/KalmanModelTest.java +++ b/src/test/java/org/orekit/estimation/sequential/KalmanModelTest.java @@ -39,9 +39,9 @@ import org.orekit.orbits.OrbitType; import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.MatricesHarvester; +import org.orekit.propagation.Propagator; import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.conversion.NumericalPropagatorBuilder; -import org.orekit.propagation.numerical.NumericalPropagator; import org.orekit.time.AbsoluteDate; import org.orekit.utils.PVCoordinates; import org.orekit.utils.ParameterDriver; @@ -229,8 +229,7 @@ public void ModelPhysicalOutputsTest() { // Get the estimated propagator from Kalman filter and propagate it to // range measurement date - NumericalPropagator propagator = - propagatorBuilder.buildPropagator(propagatorBuilder.getSelectedNormalizedParameters()); + final Propagator propagator = propagatorBuilder.buildPropagator(); // Set derivatives computation for the propagator final String equationName = KalmanEstimator.class.getName() + "-derivatives-"; diff --git a/src/test/java/org/orekit/estimation/sequential/SemiAnalyticalKalmanEstimatorTest.java b/src/test/java/org/orekit/estimation/sequential/SemiAnalyticalKalmanEstimatorTest.java index 5aa1ef499d..23731cf093 100644 --- a/src/test/java/org/orekit/estimation/sequential/SemiAnalyticalKalmanEstimatorTest.java +++ b/src/test/java/org/orekit/estimation/sequential/SemiAnalyticalKalmanEstimatorTest.java @@ -16,6 +16,8 @@ */ package org.orekit.estimation.sequential; +import java.util.List; + import org.hipparchus.exception.LocalizedCoreFormats; import org.hipparchus.exception.MathRuntimeException; import org.hipparchus.linear.MatrixUtils; @@ -52,8 +54,6 @@ import org.orekit.utils.ParameterDriver; import org.orekit.utils.ParameterDriversList; -import java.util.List; - public class SemiAnalyticalKalmanEstimatorTest { @Test @@ -160,8 +160,7 @@ public void testKeplerianRange() { final DSSTPropagatorBuilder propagatorBuilder = context.createBuilder(perfectStart, minStep, maxStep, dP); // Reference propagator for estimation performances - final DSSTPropagator referencePropagator = propagatorBuilder. - buildPropagator(propagatorBuilder.getSelectedNormalizedParameters()); + final Propagator referencePropagator = propagatorBuilder.buildPropagator(); // Reference position/velocity at last measurement date final Orbit refOrbit = referencePropagator. @@ -250,8 +249,7 @@ public void testRangeWithZonal() { propagatorBuilder.addForceModel(new DSSTZonal(GravityFieldFactory.getUnnormalizedProvider(2, 0))); // Reference propagator for estimation performances - final DSSTPropagator referencePropagator = propagatorBuilder. - buildPropagator(propagatorBuilder.getSelectedNormalizedParameters()); + final Propagator referencePropagator = propagatorBuilder.buildPropagator(); // Reference position/velocity at last measurement date final Orbit refOrbit = referencePropagator. @@ -481,8 +479,7 @@ public void testWithEstimatedPropagationParameters() { propagatorBuilder.addForceModel(zonal); // Reference propagator for estimation performances - final DSSTPropagator referencePropagator = propagatorBuilder. - buildPropagator(propagatorBuilder.getSelectedNormalizedParameters()); + final Propagator referencePropagator = propagatorBuilder.buildPropagator(); // Reference position/velocity at last measurement date final Orbit refOrbit = referencePropagator. @@ -588,8 +585,7 @@ public void testWithEstimatedMeasurementParameters() { propagatorBuilder.addForceModel(zonal); // Reference propagator for estimation performances - final DSSTPropagator referencePropagator = propagatorBuilder. - buildPropagator(propagatorBuilder.getSelectedNormalizedParameters()); + final Propagator referencePropagator = propagatorBuilder.buildPropagator(); // Reference position/velocity at last measurement date final Orbit refOrbit = referencePropagator. diff --git a/src/test/java/org/orekit/estimation/sequential/SemiAnalyticalKalmanModelTest.java b/src/test/java/org/orekit/estimation/sequential/SemiAnalyticalKalmanModelTest.java index 4a4d2227e5..fa4c4a743f 100644 --- a/src/test/java/org/orekit/estimation/sequential/SemiAnalyticalKalmanModelTest.java +++ b/src/test/java/org/orekit/estimation/sequential/SemiAnalyticalKalmanModelTest.java @@ -1,3 +1,20 @@ +/* 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.estimation.sequential; import org.hipparchus.linear.MatrixUtils; diff --git a/src/test/java/org/orekit/estimation/sequential/SemiAnalyticalUnscentedKalmanEstimatorTest.java b/src/test/java/org/orekit/estimation/sequential/SemiAnalyticalUnscentedKalmanEstimatorTest.java index 1c0fac9534..a27ca9bac8 100644 --- a/src/test/java/org/orekit/estimation/sequential/SemiAnalyticalUnscentedKalmanEstimatorTest.java +++ b/src/test/java/org/orekit/estimation/sequential/SemiAnalyticalUnscentedKalmanEstimatorTest.java @@ -1,3 +1,20 @@ +/* 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.estimation.sequential; import java.util.List; @@ -25,7 +42,6 @@ import org.orekit.propagation.PropagationType; import org.orekit.propagation.Propagator; import org.orekit.propagation.conversion.DSSTPropagatorBuilder; -import org.orekit.propagation.semianalytical.dsst.DSSTPropagator; import org.orekit.propagation.semianalytical.dsst.forces.DSSTTesseral; import org.orekit.propagation.semianalytical.dsst.forces.DSSTZonal; import org.orekit.time.AbsoluteDate; @@ -131,8 +147,7 @@ public void testKeplerianRange() { final DSSTPropagatorBuilder propagatorBuilder = context.createBuilder(perfectStart, minStep, maxStep, dP); // Reference propagator for estimation performances - final DSSTPropagator referencePropagator = propagatorBuilder. - buildPropagator(propagatorBuilder.getSelectedNormalizedParameters()); + final Propagator referencePropagator = propagatorBuilder.buildPropagator(); // Reference position/velocity at last measurement date final Orbit refOrbit = referencePropagator. @@ -173,7 +188,7 @@ public void testKeplerianRange() { expectedDeltaPos, posEps, expectedDeltaVel, velEps); - Assertions.assertEquals(0.0, observer.getMeanResidual(), 4.99e-8); + Assertions.assertEquals(0.0, observer.getMeanResidual(), 5.08e-8); Assertions.assertEquals(6, kalman.getOrbitalParametersDrivers(false).getNbParams()); Assertions.assertEquals(6, kalman.getOrbitalParametersDrivers(true).getNbParams()); Assertions.assertEquals(1, kalman.getPropagationParametersDrivers(false).getNbParams()); @@ -216,8 +231,7 @@ public void testRangeWithZonal() { propagatorBuilder.addForceModel(new DSSTZonal(GravityFieldFactory.getUnnormalizedProvider(2, 0))); // Reference propagator for estimation performances - final DSSTPropagator referencePropagator = propagatorBuilder. - buildPropagator(propagatorBuilder.getSelectedNormalizedParameters()); + final Propagator referencePropagator = propagatorBuilder.buildPropagator(); // Reference position/velocity at last measurement date final Orbit refOrbit = referencePropagator. @@ -309,8 +323,7 @@ public void testRangeWithTesseral() { gravityField.getMaxDegree(), gravityField.getMaxOrder(), FastMath.min(4, gravityField.getMaxDegree() - 2))); // Reference propagator for estimation performances - final DSSTPropagator referencePropagator = propagatorBuilder. - buildPropagator(propagatorBuilder.getSelectedNormalizedParameters()); + final Propagator referencePropagator = propagatorBuilder.buildPropagator(); // Reference position/velocity at last measurement date final Orbit refOrbit = referencePropagator. @@ -344,9 +357,9 @@ public void testRangeWithTesseral() { // Filter the measurements and check the results final double expectedDeltaPos = 0.; - final double posEps = 4.2e-9; + final double posEps = 4.2e-7; final double expectedDeltaVel = 0.; - final double velEps = 1.7e-12; + final double velEps = 3.8e-11; DSSTEstimationTestUtils.checkKalmanFit(context, kalman, measurements, refOrbit, positionAngleType, expectedDeltaPos, posEps, diff --git a/src/test/java/org/orekit/estimation/sequential/KalmanNumericalOrbitDeterminationTest.java b/src/test/java/org/orekit/estimation/sequential/SequentialNumericalOrbitDeterminationTest.java similarity index 67% rename from src/test/java/org/orekit/estimation/sequential/KalmanNumericalOrbitDeterminationTest.java rename to src/test/java/org/orekit/estimation/sequential/SequentialNumericalOrbitDeterminationTest.java index 212208afe4..346c50d281 100644 --- a/src/test/java/org/orekit/estimation/sequential/KalmanNumericalOrbitDeterminationTest.java +++ b/src/test/java/org/orekit/estimation/sequential/SequentialNumericalOrbitDeterminationTest.java @@ -67,7 +67,7 @@ import java.util.Locale; import java.util.NoSuchElementException; -public class KalmanNumericalOrbitDeterminationTest extends AbstractOrbitDetermination { +public class SequentialNumericalOrbitDeterminationTest extends AbstractOrbitDetermination { /** Gravity field. */ private NormalizedSphericalHarmonicsProvider gravityField; @@ -206,15 +206,55 @@ protected void setAttitudeProvider(final NumericalPropagatorBuilder propagatorBu propagatorBuilder.setAttitudeProvider(attitudeProvider); } + @Test - // Orbit determination for Lageos2 based on SLR (range) measurements - public void testLageos2() throws URISyntaxException, IOException { + public void testLageos2Extended() throws URISyntaxException, IOException { + + // Position/velocity accuracy + final double distanceAccuracy = 0.541; + final double velocityAccuracy = 4.12e-3; + + // Batch LS values + //final double[] stationOffSet = { 1.659203, 0.861250, -0.885352 }; + //final double rangeBias = -0.286275; + final double[] stationOffSet = { 0.2983710, -0.137384, 0.0156606 }; + final double rangeBias = -0.00129569; + + // Batch LS values + //final double[] refStatRange = { -2.431135, 2.218644, 0.038483, 0.982017 }; + final double[] refStatRange = { -23.537659, 20.444514, 0.973118, 5.686005 }; + + testLageos2(distanceAccuracy, velocityAccuracy, stationOffSet, rangeBias, refStatRange, false, false); + } + + @Test + public void testLageos2Unscented() throws URISyntaxException, IOException { + + // Position/velocity accuracy + final double distanceAccuracy = 0.482; + final double velocityAccuracy = 3.97e-3; + + // Batch LS values + //final double[] stationOffSet = { 1.659203, 0.861250, -0.885352 }; + //final double rangeBias = -0.286275; + final double[] stationOffSet = { 0.302324, -0.127179, 0.0172399 }; + final double rangeBias = -0.008282; + + // Batch LS values + //final double[] refStatRange = { -2.431135, 2.218644, 0.038483, 0.982017 }; + final double[] refStatRange = { -16.560410, 21.362738, 0.665356, 5.657324 }; + + testLageos2(distanceAccuracy, velocityAccuracy, stationOffSet, rangeBias, refStatRange, false, true); + } + - // Print results on console - final boolean print = false; + // Orbit determination for Lageos2 based on SLR (range) measurements + protected void testLageos2(final double distanceAccuracy, final double velocityAccuracy, + final double[] stationOffSet, final double rangeBias, final double[] refStatRange, + final boolean print, final boolean isUnscented) throws URISyntaxException, IOException { // input in resources directory - final String inputPath = KalmanNumericalOrbitDeterminationTest.class.getClassLoader().getResource("orbit-determination/Lageos2/kalman_od_test_Lageos2.in").toURI().getPath(); + final String inputPath = SequentialNumericalOrbitDeterminationTest.class.getClassLoader().getResource("orbit-determination/Lageos2/kalman_od_test_Lageos2.in").toURI().getPath(); final File input = new File(inputPath); // configure Orekit data acces @@ -250,11 +290,7 @@ public void testLageos2() throws URISyntaxException, IOException { ResultKalman kalmanLageos2 = runKalman(input, orbitType, print, cartesianOrbitalP, cartesianOrbitalQ, null, null, - measurementP, measurementQ, false); - - // Definition of the accuracy for the test - final double distanceAccuracy = 0.86; - final double velocityAccuracy = 4.12e-3; + measurementP, measurementQ, isUnscented); // Tests // Note: The reference initial orbit is the same as in the batch LS tests @@ -295,42 +331,182 @@ public void testLageos2() throws URISyntaxException, IOException { "ΔV [m/s]", dV); } + // Accuracy for tests + final double parametersAccuracy = 1e-6; + // Test on measurements parameters - final List list = new ArrayList(); - list.addAll(kalmanLageos2.getMeasurementsParameters().getDrivers()); + final List list = new ArrayList(kalmanLageos2.getMeasurementsParameters().getDrivers()); sortParametersChanges(list); - // Batch LS values - //final double[] stationOffSet = { 1.659203, 0.861250, -0.885352 }; - //final double rangeBias = -0.286275; - final double[] stationOffSet = { 0.298867, -0.137456, 0.013315 }; - final double rangeBias = 0.002390; - Assertions.assertEquals(stationOffSet[0], list.get(0).getValue(), distanceAccuracy); - Assertions.assertEquals(stationOffSet[1], list.get(1).getValue(), distanceAccuracy); - Assertions.assertEquals(stationOffSet[2], list.get(2).getValue(), distanceAccuracy); - Assertions.assertEquals(rangeBias, list.get(3).getValue(), distanceAccuracy); + + Assertions.assertEquals(stationOffSet[0], list.get(0).getValue(), parametersAccuracy); + Assertions.assertEquals(stationOffSet[1], list.get(1).getValue(), parametersAccuracy); + Assertions.assertEquals(stationOffSet[2], list.get(2).getValue(), parametersAccuracy); + Assertions.assertEquals(rangeBias, list.get(3).getValue(), parametersAccuracy); //test on statistic for the range residuals final long nbRange = 258; - // Batch LS values - //final double[] RefStatRange = { -2.431135, 2.218644, 0.038483, 0.982017 }; - final double[] RefStatRange = { -23.561314, 20.436464, 0.964164, 5.687187 }; Assertions.assertEquals(nbRange, kalmanLageos2.getRangeStat().getN()); - Assertions.assertEquals(RefStatRange[0], kalmanLageos2.getRangeStat().getMin(), distanceAccuracy); - Assertions.assertEquals(RefStatRange[1], kalmanLageos2.getRangeStat().getMax(), distanceAccuracy); - Assertions.assertEquals(RefStatRange[2], kalmanLageos2.getRangeStat().getMean(), distanceAccuracy); - Assertions.assertEquals(RefStatRange[3], kalmanLageos2.getRangeStat().getStandardDeviation(), distanceAccuracy); + Assertions.assertEquals(refStatRange[0], kalmanLageos2.getRangeStat().getMin(), parametersAccuracy); + Assertions.assertEquals(refStatRange[1], kalmanLageos2.getRangeStat().getMax(), parametersAccuracy); + Assertions.assertEquals(refStatRange[2], kalmanLageos2.getRangeStat().getMean(), parametersAccuracy); + Assertions.assertEquals(refStatRange[3], kalmanLageos2.getRangeStat().getStandardDeviation(), parametersAccuracy); } @Test - // Orbit determination for range, azimuth elevation measurements - public void testW3B() throws URISyntaxException, IOException { + public void testW3BExtended() throws URISyntaxException, IOException { + // Batch LS result: -0.2154; + final double dragCoef = 0.1932; + + // Batch LS results: 8.002e-6 + final double leakAccelerationNorm0 = 5.994e-6; + + // Batch LS results: 3.058e-10 + final double leakAccelerationNorm1 = 1.836e-10; + + // Batch LS results + // final double[] CastleAzElBias = { 0.062701342, -0.003613508 }; + // final double CastleRangeBias = 11274.4677; + final double[] castleAzElBias = { 0.062636, -0.003672}; + final double castleRangeBias = 11289.3471; + + // Batch LS results + // final double[] FucAzElBias = { -0.053526137, 0.075483886 }; + // final double FucRangeBias = 13467.8256; + final double[] fucAzElBias = { -0.053298, 0.075589 }; + final double fucRangeBias = 13482.0805; + + // Batch LS results + // final double[] KumAzElBias = { -0.023574208, -0.054520756 }; + // final double KumRangeBias = 13512.57594; + final double[] kumAzElBias = { -0.022805, -0.055057 }; + final double kumRangeBias = 13502.6845; + + // Batch LS results + // final double[] PreAzElBias = { 0.030201539, 0.009747877 }; + // final double PreRangeBias = 13594.11889; + final double[] preAzElBias = { 0.030353, 0.009658 }; + final double preRangeBias = 13609.2762; + + // Batch LS results + // final double[] UraAzElBias = { 0.167814449, -0.12305252 }; + // final double UraRangeBias = 13450.26738; + final double[] uraAzElBias = { 0.167519, -0.122842 }; + final double uraRangeBias = 13441.6666; + + //statistics for the range residual (min, max, mean, std) + final double[] refStatRange = { -12.9805, 18.0538, -1.1332, 5.3125 }; + + //statistics for the azimuth residual (min, max, mean, std) + final double[] refStatAzi = { -0.041442, 0.023473, -0.004426, 0.009911 }; + + //statistics for the elevation residual (min, max, mean, std) + final double[] refStatEle = { -0.025399, 0.043346, 0.001011, 0.010636 }; + + // Expected covariance + final double dragVariance = 0.016350; + final double leakXVariance = 2.047E-13; + final double leakYVariance = 5.462E-13; + final double leakZVariance = 1.71778E-11; + + // Prediction position/velocity accuracies + // FIXME: debug - Comparison with batch LS is bad + final double predictionDistanceAccuracy = 234.57; + final double predictionVelocityAccuracy = 0.086; + + testW3B(dragCoef, leakAccelerationNorm0, leakAccelerationNorm1, + castleAzElBias, castleRangeBias, fucAzElBias, fucRangeBias, kumAzElBias, kumRangeBias, + preAzElBias, preRangeBias, uraAzElBias, uraRangeBias, + refStatRange, refStatAzi, refStatEle, dragVariance, + leakXVariance, leakYVariance, leakZVariance, + 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 }; - // Print results on console - final boolean print = false; + //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, + final double[] fucAzElBias, final double fucRangeBias, + final double[] kumAzElBias, final double kumRangeBias, + final double[] preAzElBias, final double preRangeBias, + final double[] uraAzElBias, final double uraRangeBias, + final double[] refStatRange, final double[] refStatAzi, final double[] refStatEle, + final double dragVariance, + final double leakXVariance, final double leakYVariance, final double leakZVariance, + final double predictionDistanceAccuracy, final double predictionVelocityAccuracy, + final boolean print, final boolean isUnscented) throws URISyntaxException, IOException { // input in resources directory - final String inputPath = KalmanNumericalOrbitDeterminationTest.class.getClassLoader().getResource("orbit-determination/W3B/od_test_W3.in").toURI().getPath(); + final String inputPath = SequentialNumericalOrbitDeterminationTest.class.getClassLoader().getResource("orbit-determination/W3B/od_test_W3.in").toURI().getPath(); final File input = new File(inputPath); // Configure Orekit data access @@ -390,14 +566,14 @@ public void testW3B() throws URISyntaxException, IOException { ResultKalman kalmanW3B = runKalman(input, orbitType, print, cartesianOrbitalP, cartesianOrbitalQ, propagationP, propagationQ, - measurementP, measurementQ, false); + measurementP, measurementQ, isUnscented); // Tests // ----- // Definition of the accuracy for the test - final double distanceAccuracy = 0.1; - final double angleAccuracy = 1e-5; // degrees + final double distanceAccuracy = 1e-4; + final double angleAccuracy = 1e-6; // degrees // Number of measurements processed final int numberOfMeas = 521; @@ -406,129 +582,91 @@ public void testW3B() throws URISyntaxException, IOException { // Test on propagator parameters // ----------------------------- - - // Batch LS result - // final double dragCoef = -0.2154; - final double dragCoef = 0.1931; final ParameterDriversList propagatorParameters = kalmanW3B.getPropagatorParameters(); - Assertions.assertEquals(dragCoef, propagatorParameters.getDrivers().get(0).getValue(), 1e-3); + Assertions.assertEquals(dragCoef, propagatorParameters.getDrivers().get(0).getValue(), 1e-4); final Vector3D leakAcceleration0 = new Vector3D(propagatorParameters.getDrivers().get(1).getValue(), propagatorParameters.getDrivers().get(3).getValue(), propagatorParameters.getDrivers().get(5).getValue()); - // Batch LS results - //Assertions.assertEquals(8.002e-6, leakAcceleration0.getNorm(), 1.0e-8); - Assertions.assertEquals(5.994e-6, leakAcceleration0.getNorm(), 1.0e-8); + Assertions.assertEquals(leakAccelerationNorm0, leakAcceleration0.getNorm(), 1.0e-9); + final Vector3D leakAcceleration1 = new Vector3D(propagatorParameters.getDrivers().get(2).getValue(), propagatorParameters.getDrivers().get(4).getValue(), propagatorParameters.getDrivers().get(6).getValue()); - // Batch LS results - //Assertions.assertEquals(3.058e-10, leakAcceleration1.getNorm(), 1.0e-12); - Assertions.assertEquals(1.831e-10, leakAcceleration1.getNorm(), 1.0e-12); + Assertions.assertEquals(leakAccelerationNorm1, leakAcceleration1.getNorm(), 1.0e-13); // Test on measurements parameters // ------------------------------- - final List list = new ArrayList(); - list.addAll(kalmanW3B.getMeasurementsParameters().getDrivers()); + final List list = new ArrayList(kalmanW3B.getMeasurementsParameters().getDrivers()); sortParametersChanges(list); // Station CastleRock - // Batch LS results -// final double[] CastleAzElBias = { 0.062701342, -0.003613508 }; -// final double CastleRangeBias = 11274.4677; - final double[] CastleAzElBias = { 0.062635, -0.003672}; - final double CastleRangeBias = 11289.3678; - Assertions.assertEquals(CastleAzElBias[0], FastMath.toDegrees(list.get(0).getValue()), angleAccuracy); - Assertions.assertEquals(CastleAzElBias[1], FastMath.toDegrees(list.get(1).getValue()), angleAccuracy); - Assertions.assertEquals(CastleRangeBias, list.get(2).getValue(), distanceAccuracy); + Assertions.assertEquals(castleAzElBias[0], FastMath.toDegrees(list.get(0).getValue()), angleAccuracy); + Assertions.assertEquals(castleAzElBias[1], FastMath.toDegrees(list.get(1).getValue()), angleAccuracy); + Assertions.assertEquals(castleRangeBias, list.get(2).getValue(), distanceAccuracy); // Station Fucino - // Batch LS results -// final double[] FucAzElBias = { -0.053526137, 0.075483886 }; -// final double FucRangeBias = 13467.8256; - final double[] FucAzElBias = { -0.053298, 0.075589 }; - final double FucRangeBias = 13482.0715; - Assertions.assertEquals(FucAzElBias[0], FastMath.toDegrees(list.get(3).getValue()), angleAccuracy); - Assertions.assertEquals(FucAzElBias[1], FastMath.toDegrees(list.get(4).getValue()), angleAccuracy); - Assertions.assertEquals(FucRangeBias, list.get(5).getValue(), distanceAccuracy); + Assertions.assertEquals(fucAzElBias[0], FastMath.toDegrees(list.get(3).getValue()), angleAccuracy); + Assertions.assertEquals(fucAzElBias[1], FastMath.toDegrees(list.get(4).getValue()), angleAccuracy); + Assertions.assertEquals(fucRangeBias, list.get(5).getValue(), distanceAccuracy); // Station Kumsan - // Batch LS results -// final double[] KumAzElBias = { -0.023574208, -0.054520756 }; -// final double KumRangeBias = 13512.57594; - final double[] KumAzElBias = { -0.022805, -0.055057 }; - final double KumRangeBias = 13502.7459; - Assertions.assertEquals(KumAzElBias[0], FastMath.toDegrees(list.get(6).getValue()), angleAccuracy); - Assertions.assertEquals(KumAzElBias[1], FastMath.toDegrees(list.get(7).getValue()), angleAccuracy); - Assertions.assertEquals(KumRangeBias, list.get(8).getValue(), distanceAccuracy); + Assertions.assertEquals(kumAzElBias[0], FastMath.toDegrees(list.get(6).getValue()), angleAccuracy); + Assertions.assertEquals(kumAzElBias[1], FastMath.toDegrees(list.get(7).getValue()), angleAccuracy); + Assertions.assertEquals(kumRangeBias, list.get(8).getValue(), distanceAccuracy); // Station Pretoria - // Batch LS results -// final double[] PreAzElBias = { 0.030201539, 0.009747877 }; -// final double PreRangeBias = 13594.11889; - final double[] PreAzElBias = { 0.030353, 0.009658 }; - final double PreRangeBias = 13609.2516; - Assertions.assertEquals(PreAzElBias[0], FastMath.toDegrees(list.get( 9).getValue()), angleAccuracy); - Assertions.assertEquals(PreAzElBias[1], FastMath.toDegrees(list.get(10).getValue()), angleAccuracy); - Assertions.assertEquals(PreRangeBias, list.get(11).getValue(), distanceAccuracy); + Assertions.assertEquals(preAzElBias[0], FastMath.toDegrees(list.get( 9).getValue()), angleAccuracy); + Assertions.assertEquals(preAzElBias[1], FastMath.toDegrees(list.get(10).getValue()), angleAccuracy); + Assertions.assertEquals(preRangeBias, list.get(11).getValue(), distanceAccuracy); // Station Uralla - // Batch LS results -// final double[] UraAzElBias = { 0.167814449, -0.12305252 }; -// final double UraRangeBias = 13450.26738; - final double[] UraAzElBias = { 0.167519, -0.122842 }; - final double UraRangeBias = 13441.7019; - Assertions.assertEquals(UraAzElBias[0], FastMath.toDegrees(list.get(12).getValue()), angleAccuracy); - Assertions.assertEquals(UraAzElBias[1], FastMath.toDegrees(list.get(13).getValue()), angleAccuracy); - Assertions.assertEquals(UraRangeBias, list.get(14).getValue(), distanceAccuracy); + Assertions.assertEquals(uraAzElBias[0], FastMath.toDegrees(list.get(12).getValue()), angleAccuracy); + Assertions.assertEquals(uraAzElBias[1], FastMath.toDegrees(list.get(13).getValue()), angleAccuracy); + Assertions.assertEquals(uraRangeBias, list.get(14).getValue(), distanceAccuracy); + // Test on statistic for the range residuals final long nbRange = 182; - //statistics for the range residual (min, max, mean, std) - final double[] RefStatRange = { -12.981, 18.046, -1.133, 5.312 }; Assertions.assertEquals(nbRange, kalmanW3B.getRangeStat().getN()); - Assertions.assertEquals(RefStatRange[0], kalmanW3B.getRangeStat().getMin(), distanceAccuracy); - Assertions.assertEquals(RefStatRange[1], kalmanW3B.getRangeStat().getMax(), distanceAccuracy); - Assertions.assertEquals(RefStatRange[2], kalmanW3B.getRangeStat().getMean(), distanceAccuracy); - Assertions.assertEquals(RefStatRange[3], kalmanW3B.getRangeStat().getStandardDeviation(), distanceAccuracy); + Assertions.assertEquals(refStatRange[0], kalmanW3B.getRangeStat().getMin(), distanceAccuracy); + Assertions.assertEquals(refStatRange[1], kalmanW3B.getRangeStat().getMax(), distanceAccuracy); + Assertions.assertEquals(refStatRange[2], kalmanW3B.getRangeStat().getMean(), distanceAccuracy); + Assertions.assertEquals(refStatRange[3], kalmanW3B.getRangeStat().getStandardDeviation(), distanceAccuracy); //test on statistic for the azimuth residuals final long nbAzi = 339; - //statistics for the azimuth residual (min, max, mean, std) - final double[] RefStatAzi = { -0.041441, 0.023473, -0.004426, 0.009911 }; Assertions.assertEquals(nbAzi, kalmanW3B.getAzimStat().getN()); - Assertions.assertEquals(RefStatAzi[0], kalmanW3B.getAzimStat().getMin(), angleAccuracy); - Assertions.assertEquals(RefStatAzi[1], kalmanW3B.getAzimStat().getMax(), angleAccuracy); - Assertions.assertEquals(RefStatAzi[2], kalmanW3B.getAzimStat().getMean(), angleAccuracy); - Assertions.assertEquals(RefStatAzi[3], kalmanW3B.getAzimStat().getStandardDeviation(), angleAccuracy); + Assertions.assertEquals(refStatAzi[0], kalmanW3B.getAzimStat().getMin(), angleAccuracy); + Assertions.assertEquals(refStatAzi[1], kalmanW3B.getAzimStat().getMax(), angleAccuracy); + Assertions.assertEquals(refStatAzi[2], kalmanW3B.getAzimStat().getMean(), angleAccuracy); + Assertions.assertEquals(refStatAzi[3], kalmanW3B.getAzimStat().getStandardDeviation(), angleAccuracy); //test on statistic for the elevation residuals final long nbEle = 339; - final double[] RefStatEle = { -0.025399, 0.043345, 0.001011, 0.010636 }; Assertions.assertEquals(nbEle, kalmanW3B.getElevStat().getN()); - Assertions.assertEquals(RefStatEle[0], kalmanW3B.getElevStat().getMin(), angleAccuracy); - Assertions.assertEquals(RefStatEle[1], kalmanW3B.getElevStat().getMax(), angleAccuracy); - Assertions.assertEquals(RefStatEle[2], kalmanW3B.getElevStat().getMean(), angleAccuracy); - Assertions.assertEquals(RefStatEle[3], kalmanW3B.getElevStat().getStandardDeviation(), angleAccuracy); + Assertions.assertEquals(refStatEle[0], kalmanW3B.getElevStat().getMin(), angleAccuracy); + Assertions.assertEquals(refStatEle[1], kalmanW3B.getElevStat().getMax(), angleAccuracy); + Assertions.assertEquals(refStatEle[2], kalmanW3B.getElevStat().getMean(), angleAccuracy); + Assertions.assertEquals(refStatEle[3], kalmanW3B.getElevStat().getStandardDeviation(), angleAccuracy); RealMatrix covariances = kalmanW3B.getCovariances(); Assertions.assertEquals(28, covariances.getRowDimension()); Assertions.assertEquals(28, covariances.getColumnDimension()); // drag coefficient variance - Assertions.assertEquals(0.016349, covariances.getEntry(6, 6), 1.0e-5); + Assertions.assertEquals(dragVariance, covariances.getEntry(6, 6), 1.0e-6); // leak-X constant term variance - Assertions.assertEquals(2.047303E-13, covariances.getEntry(7, 7), 1.0e-16); + Assertions.assertEquals(leakXVariance, covariances.getEntry(7, 7), 1.0e-16); // leak-Y constant term variance - Assertions.assertEquals(5.462497E-13, covariances.getEntry(9, 9), 1.0e-15); + Assertions.assertEquals(leakYVariance, covariances.getEntry(9, 9), 1.0e-16); // leak-Z constant term variance - Assertions.assertEquals(1.717781E-11, covariances.getEntry(11, 11), 1.0e-15); - + Assertions.assertEquals(leakZVariance, covariances.getEntry(11, 11), 1.0e-16); // Test on orbital parameters // Done at the end to avoid changing the estimated propagation parameters @@ -573,12 +711,8 @@ public void testW3B() throws URISyntaxException, IOException { // Check distances final double dP = Vector3D.distance(refPos, estimatedPos); final double dV = Vector3D.distance(refVel, estimatedVel); - - // FIXME: debug - Comparison with batch LS is bad - final double debugDistanceAccuracy = 234.82; - final double debugVelocityAccuracy = 0.086; - Assertions.assertEquals(0.0, Vector3D.distance(refPos, estimatedPos), debugDistanceAccuracy); - Assertions.assertEquals(0.0, Vector3D.distance(refVel, estimatedVel), debugVelocityAccuracy); + Assertions.assertEquals(0.0, Vector3D.distance(refPos, estimatedPos), predictionDistanceAccuracy); + Assertions.assertEquals(0.0, Vector3D.distance(refVel, estimatedVel), predictionVelocityAccuracy); // Print orbit deltas if (print) { diff --git a/src/test/java/org/orekit/estimation/sequential/TLEKalmanEstimatorTest.java b/src/test/java/org/orekit/estimation/sequential/TLEKalmanEstimatorTest.java index a5aca8c959..6e3115707b 100644 --- a/src/test/java/org/orekit/estimation/sequential/TLEKalmanEstimatorTest.java +++ b/src/test/java/org/orekit/estimation/sequential/TLEKalmanEstimatorTest.java @@ -16,6 +16,9 @@ */ package org.orekit.estimation.sequential; +import java.util.ArrayList; +import java.util.List; + import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.linear.MatrixUtils; import org.hipparchus.linear.RealMatrix; @@ -30,8 +33,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; @@ -44,9 +47,6 @@ import org.orekit.utils.ParameterDriver; import org.orekit.utils.ParameterDriversList; -import java.util.ArrayList; -import java.util.List; - public class TLEKalmanEstimatorTest { @Test @@ -86,8 +86,7 @@ public void testPV() { new PVMeasurementCreator(), 0.0, 3.0, 300.0); // Reference propagator for estimation performances - final TLEPropagator referencePropagator = propagatorBuilder. - buildPropagator(propagatorBuilder.getSelectedNormalizedParameters()); + final Propagator referencePropagator = propagatorBuilder.buildPropagator(); // Reference position/velocity at last measurement date final Orbit refOrbit = referencePropagator. @@ -149,8 +148,7 @@ public void testRange() { 1.0, 4.0, 60.0); // Reference propagator for estimation performances - final TLEPropagator referencePropagator = propagatorBuilder. - buildPropagator(propagatorBuilder.getSelectedNormalizedParameters()); + final Propagator referencePropagator = propagatorBuilder.buildPropagator(); // Reference position/velocity at last measurement date final Orbit refOrbit = referencePropagator. @@ -230,8 +228,7 @@ public void testRangeWithOnBoardAntennaOffset() { } // Reference propagator for estimation performances - final TLEPropagator referencePropagator = propagatorBuilder. - buildPropagator(propagatorBuilder.getSelectedNormalizedParameters()); + final Propagator referencePropagator = propagatorBuilder.buildPropagator(); // Reference position/velocity at last measurement date final Orbit refOrbit = referencePropagator. @@ -305,8 +302,7 @@ public void testRangeAndRangeRate() { measurements.addAll(measurementsRangeRate); // Reference propagator for estimation performances - final TLEPropagator referencePropagator = propagatorBuilder. - buildPropagator(propagatorBuilder.getSelectedNormalizedParameters()); + final Propagator referencePropagator = propagatorBuilder.buildPropagator(); // Reference position/velocity at last measurement date final Orbit refOrbit = referencePropagator. diff --git a/src/test/java/org/orekit/estimation/sequential/TLEKalmanOrbitDeterminationTest.java b/src/test/java/org/orekit/estimation/sequential/TLEKalmanOrbitDeterminationTest.java index ddb0cf6940..fce2e1a684 100644 --- a/src/test/java/org/orekit/estimation/sequential/TLEKalmanOrbitDeterminationTest.java +++ b/src/test/java/org/orekit/estimation/sequential/TLEKalmanOrbitDeterminationTest.java @@ -205,7 +205,7 @@ public void testLageos2() throws URISyntaxException, IOException { final boolean print = false; // input in resources directory - final String inputPath = KalmanNumericalOrbitDeterminationTest.class.getClassLoader().getResource("orbit-determination/Lageos2/tle_od_test_Lageos2.in").toURI().getPath(); + final String inputPath = TLEKalmanOrbitDeterminationTest.class.getClassLoader().getResource("orbit-determination/Lageos2/tle_od_test_Lageos2.in").toURI().getPath(); final File input = new File(inputPath); // configure Orekit data acces @@ -319,7 +319,7 @@ public void testGNSS() throws URISyntaxException, IOException { final boolean print = false; // input in resources directory - final String inputPath = KalmanNumericalOrbitDeterminationTest.class.getClassLoader().getResource("orbit-determination/analytical/tle_od_test_GPS07.in").toURI().getPath(); + final String inputPath = TLEKalmanOrbitDeterminationTest.class.getClassLoader().getResource("orbit-determination/analytical/tle_od_test_GPS07.in").toURI().getPath(); final File input = new File(inputPath); // configure Orekit data acces @@ -361,7 +361,7 @@ public void testGNSS() throws URISyntaxException, IOException { // Definition of the accuracy for the test // Initial TLE error at last measurement date is 1053.6m - final double distanceAccuracy = 67.33; + final double distanceAccuracy = 67.48; // Tests diff --git a/src/test/java/org/orekit/estimation/sequential/UnscentedKalmanEstimatorTest.java b/src/test/java/org/orekit/estimation/sequential/UnscentedKalmanEstimatorTest.java index f8c927d489..31682d94f1 100644 --- a/src/test/java/org/orekit/estimation/sequential/UnscentedKalmanEstimatorTest.java +++ b/src/test/java/org/orekit/estimation/sequential/UnscentedKalmanEstimatorTest.java @@ -40,8 +40,8 @@ import org.orekit.estimation.measurements.PVMeasurementCreator; import org.orekit.estimation.measurements.Position; 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.Bias; import org.orekit.frames.FramesFactory; import org.orekit.frames.TopocentricFrame; @@ -54,7 +54,6 @@ import org.orekit.propagation.EphemerisGenerator; import org.orekit.propagation.Propagator; import org.orekit.propagation.conversion.NumericalPropagatorBuilder; -import org.orekit.propagation.numerical.NumericalPropagator; import org.orekit.time.AbsoluteDate; import org.orekit.utils.Constants; import org.orekit.utils.IERSConventions; @@ -126,8 +125,7 @@ public void testPV() { new PVMeasurementCreator(), 0.0, 1.0, 300.0); // Reference propagator for estimation performances - final NumericalPropagator referencePropagator = propagatorBuilder. - buildPropagator(propagatorBuilder.getSelectedNormalizedParameters()); + final Propagator referencePropagator = propagatorBuilder.buildPropagator(); // Reference position/velocity at last measurement date final Orbit refOrbit = referencePropagator. @@ -147,12 +145,12 @@ public void testPV() { // Filter the measurements and check the results final double expectedDeltaPos = 0.; - final double posEps = 3.63e-6; + final double posEps = 2.7e-7; final double expectedDeltaVel = 0.; - final double velEps = 1.42e-9; - final double[] expectedsigmasPos = {1.762E-7, 1.899E-7, 7.398E-7}; + final double velEps = 9.7e-11; + final double[] expectedsigmasPos = {0.0, 0.0, 0.0}; final double sigmaPosEps = 1.0e-10; - final double[] expectedSigmasVel = {0.90962E-10, 2.61847E-10, 0.37545E-10}; + final double[] expectedSigmasVel = {0.0, 0.0, 0.0}; final double sigmaVelEps = 1.0e-15; UnscentedEstimationTestUtils.checkKalmanFit(context, kalman, measurements, refOrbit, positionAngleType, @@ -202,8 +200,7 @@ public void testShiftedPV() { 0.0, 1.0, 300.0); // Reference propagator for estimation performances - final NumericalPropagator referencePropagator = propagatorBuilder. - buildPropagator(propagatorBuilder.getSelectedNormalizedParameters()); + final Propagator referencePropagator = propagatorBuilder.buildPropagator(); // Reference position/velocity at last measurement date final Orbit refOrbit = referencePropagator. @@ -276,8 +273,7 @@ public void testCartesianRange() { new TwoWayRangeMeasurementCreator(context), 0.0, 1.0, 60.0); // Reference propagator for estimation performances - final NumericalPropagator referencePropagator = propagatorBuilder. - buildPropagator(propagatorBuilder.getSelectedNormalizedParameters()); + final Propagator referencePropagator = propagatorBuilder.buildPropagator(); // Reference position/velocity at last measurement date final Orbit refOrbit = referencePropagator. @@ -298,12 +294,12 @@ public void testCartesianRange() { // Filter the measurements and check the results final double expectedDeltaPos = 0.; - final double posEps = 8.35e-7; + final double posEps = 6.2e-8; final double expectedDeltaVel = 0.; - final double velEps = 3.39e-10; - final double[] expectedsigmasPos = {0.1938703E-8, 12.7585598E-8, 17.0372647E-8}; + final double velEps = 2.7e-11; + final double[] expectedsigmasPos = {0.0, 0.0, 0.0}; final double sigmaPosEps = 1.0e-15; - final double[] expectedSigmasVel = {3.32084E-11, 0.3787E-11, 8.0020E-11}; + final double[] expectedSigmasVel = {0.0, 0.0, 0.0}; final double sigmaVelEps = 1.0e-15; UnscentedEstimationTestUtils.checkKalmanFit(context, kalman, measurements, refOrbit, positionAngleType, @@ -349,8 +345,7 @@ public void testKeplerianRange() { new TwoWayRangeMeasurementCreator(context), 0.0, 1.0, 60.0); // Reference propagator for estimation performances - final NumericalPropagator referencePropagator = propagatorBuilder. - buildPropagator(propagatorBuilder.getSelectedNormalizedParameters()); + final Propagator referencePropagator = propagatorBuilder.buildPropagator(); // Reference position/velocity at last measurement date final Orbit refOrbit = referencePropagator. @@ -371,12 +366,12 @@ public void testKeplerianRange() { // Filter the measurements and check the results final double expectedDeltaPos = 0.; - final double posEps = 1.74e-6; + final double posEps = 4.0e-8; final double expectedDeltaVel = 0.; - final double velEps = 6.06e-10; - final double[] expectedsigmasPos = {8.869538E-9, 1.18524507E-7, 4.32132152E-8}; + final double velEps = 1.4e-11; + final double[] expectedsigmasPos = {0.0, 0.0, 0.0}; final double sigmaPosEps = 1.0e-15; - final double[] expectedSigmasVel = {1.5213E-11, 7.738E-12, 4.0380E-11}; + final double[] expectedSigmasVel = {0.0, 0.0, 0.0}; final double sigmaVelEps = 1.0e-15; UnscentedEstimationTestUtils.checkKalmanFit(context, kalman, measurements, refOrbit, positionAngleType, @@ -426,8 +421,7 @@ public void testCartesianRangeRate() { 1.0, 3.0, 300.0); // Reference propagator for estimation performances - final NumericalPropagator referencePropagator = propagatorBuilder. - buildPropagator(propagatorBuilder.getSelectedNormalizedParameters()); + final Propagator referencePropagator = propagatorBuilder.buildPropagator(); // Reference position/velocity at last measurement date final Orbit refOrbit = referencePropagator. @@ -462,12 +456,12 @@ public void testCartesianRangeRate() { // Filter the measurements and check the results final double expectedDeltaPos = 0.; - final double posEps = 5.43e-6; + final double posEps = 2.0e-6; final double expectedDeltaVel = 0.; - final double velEps = 1.96e-9; + final double velEps = 7.3e-10; final double[] expectedSigmasPos = {0.324407, 1.347014, 1.743326}; final double sigmaPosEps = 1e-6; - final double[] expectedSigmasVel = {2.85688e-4, 5.765933e-4, 5.056124e-4}; + final double[] expectedSigmasVel = {2.85688e-4, 5.765934e-4, 5.056124e-4}; final double sigmaVelEps = 1e-10; UnscentedEstimationTestUtils.checkKalmanFit(context, kalman, measurements, refOrbit, positionAngleType, @@ -514,8 +508,7 @@ public void testCartesianAzimuthElevation() { 0.0, 1.0, 60.0); // Reference propagator for estimation performances - final NumericalPropagator referencePropagator = propagatorBuilder. - buildPropagator(propagatorBuilder.getSelectedNormalizedParameters()); + final Propagator referencePropagator = propagatorBuilder.buildPropagator(); // Reference position/velocity at last measurement date final Orbit refOrbit = referencePropagator. @@ -603,8 +596,7 @@ public void testCircularAzimuthElevation() { 0.0, 1.0, 60.0); // Reference propagator for estimation performances - final NumericalPropagator referencePropagator = propagatorBuilder. - buildPropagator(propagatorBuilder.getSelectedNormalizedParameters()); + final Propagator referencePropagator = propagatorBuilder.buildPropagator(); // Reference position/velocity at last measurement date final Orbit refOrbit = referencePropagator. @@ -640,12 +632,12 @@ public void testCircularAzimuthElevation() { // Filter the measurements and check the results final double expectedDeltaPos = 0.; - final double posEps = 6.05e-7; + final double posEps = 1.5e-7; final double expectedDeltaVel = 0.; - final double velEps = 2.07e-10; - final double[] expectedSigmasPos = {0.012134, 0.511243, 0.264925}; + final double velEps = 5.2e-11; + final double[] expectedSigmasPos = {0.045182, 0.615288, 0.295163}; final double sigmaPosEps = 1e-6; - final double[] expectedSigmasVel = {5.72891E-5, 1.58811E-5, 15.98658E-5}; + final double[] expectedSigmasVel = {7.25356E-5, 3.11525E-5, 19.81870E-5}; final double sigmaVelEps = 1e-10; UnscentedEstimationTestUtils.checkKalmanFit(context, kalman, measurements, refOrbit, positionAngleType, @@ -746,24 +738,20 @@ public void testMultiSat() { 1.0e-6); Orbit[] refOrbits = new Orbit[] { - propagatorBuilder1. - buildPropagator(propagatorBuilder1.getSelectedNormalizedParameters()). - propagate(measurements.get(measurements.size()-1).getDate()).getOrbit(), - propagatorBuilder2. - buildPropagator(propagatorBuilder2.getSelectedNormalizedParameters()). - propagate(measurements.get(measurements.size()-1).getDate()).getOrbit() + propagatorBuilder1.buildPropagator().propagate(measurements.get(measurements.size()-1).getDate()).getOrbit(), + propagatorBuilder2.buildPropagator().propagate(measurements.get(measurements.size()-1).getDate()).getOrbit() }; UnscentedEstimationTestUtils.checkKalmanFit(context, kalman, measurements, refOrbits, new PositionAngleType[] { PositionAngleType.TRUE, PositionAngleType.TRUE }, new double[] { 38.3, 172.3 }, new double[] { 0.1, 0.1 }, new double[] { 0.015, 0.068 }, new double[] { 1.0e-3, 1.0e-3 }, new double[][] { - { 1.5e-7, 0.6e-7, 4.2e-7 }, - { 1.5e-7, 0.5e-7, 4.2e-7 } + { 0.0, 0.0, 0.0 }, + { 0.0, 0.0, 0.0 } }, new double[] { 1e-8, 1e-8 }, new double[][] { - { 1.9e-11, 17.5e-11, 3.1e-11 }, - { 2.0e-11, 17.5e-11, 2.8e-11 } + { 0.0, 0.0, 0.0 }, + { 0.0, 0.0, 0.0 } }, new double[] { 1.0e-12, 1.0e-12 }); // after the call to estimate, the parameters lacking a user-specified reference date diff --git a/src/test/java/org/orekit/estimation/sequential/UnscentedKalmanModelTest.java b/src/test/java/org/orekit/estimation/sequential/UnscentedKalmanModelTest.java index 80f6532c81..434c9e2e49 100644 --- a/src/test/java/org/orekit/estimation/sequential/UnscentedKalmanModelTest.java +++ b/src/test/java/org/orekit/estimation/sequential/UnscentedKalmanModelTest.java @@ -21,6 +21,7 @@ import java.util.List; import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.linear.LUDecomposition; import org.hipparchus.linear.MatrixUtils; import org.hipparchus.linear.RealMatrix; import org.hipparchus.linear.RealVector; @@ -94,7 +95,7 @@ public class UnscentedKalmanModelTest { private ParameterDriver srpCoefDriver; /** Tolerance for the test. */ - private final double tol = 5.0e-9; + private final double tol = 1.0e-16; @BeforeEach public void setup() { @@ -114,7 +115,7 @@ public void setup() { this.pv = new PV(date0, context.initialOrbit.getPosition(), context.initialOrbit.getPVCoordinates().getVelocity(), - new double[] {1., 1., 1., 1.e-3, 1.e-3, 1.e-3}, 1., + new double[] {1., 2., 3., 1e-3, 2e-3, 3e-3}, 1., sat); // Create one 0m range measurement at t0 + 10s @@ -128,7 +129,7 @@ public void setup() { new double[] {100.}, new double[] {10.}, new double[] {0.}, - new double[] {100.}); + new double[] {150.}); this.satRangeBiasDriver = satRangeBias.getParametersDrivers().get(0); satRangeBiasDriver.setSelected(true); satRangeBiasDriver.setReferenceDate(date); @@ -198,8 +199,18 @@ public void ModelPhysicalOutputsTest() { // Predicted orbit is equal to initial orbit at t0 Orbit orbitPred = orbit0; + // Expected measurement matrix for a PV measurement is the 6-sized identity matrix for cartesian orbital parameters + // + zeros for other estimated parameters + RealMatrix expH = MatrixUtils.createRealMatrix(6, M); + for (int i = 0; i < 6; i++) { + expH.setEntry(i, i, 1.); + } + + // Expected state transition matrix + // State transition matrix is the identity matrix at t0 + RealMatrix expPhi = MatrixUtils.createRealIdentityMatrix(M); // Add PV measurement and check model afterwards - checkModelAfterMeasurementAdded(1, pv, Ppred, orbitPred); + checkModelAfterMeasurementAdded(1, pv, Ppred, orbitPred, expPhi, expH); } /** Check the model physical outputs at t0 before any measurement is added. */ @@ -221,20 +232,26 @@ private void checkModelAtT0() { // Measurement number Assertions.assertEquals(0, model.getCurrentMeasurementNumber()); - // Physical state + // Normalized state - is zeros + final RealVector stateN = model.getEstimate().getState(); + Assertions.assertArrayEquals(new double[M], stateN.toArray(), tol); + + // Physical state - = initialized + final RealVector x = model.getPhysicalEstimatedState(); final RealVector expX = MatrixUtils.createRealVector(M); final double[] orbitState0 = new double[6]; orbitType.mapOrbitToArray(orbit0, positionAngleType, orbitState0, null); expX.setSubVector(0, MatrixUtils.createRealVector(orbitState0)); expX.setEntry(6, srpCoefDriver.getReferenceValue()); expX.setEntry(7, satRangeBiasDriver.getReferenceValue()); - Assertions.assertArrayEquals(model.getPhysicalEstimatedState().toArray(), expX.toArray(), tol); - Assertions.assertArrayEquals(model.getEstimate().getState().toArray(), expX.toArray(), tol); + final double[] dX = x.subtract(expX).toArray(); + Assertions.assertArrayEquals(new double[M], dX, tol); - // Covariance - filled with 1 + // Normalized covariance - diagonal final double[][] Pn = model.getEstimate().getCovariance().getData(); - final double[][] expPn = covMatrixProvider.getInitialCovarianceMatrix(null).getData(); + final double[][] expPn = new double[M][M]; for (int i = 0; i < M; i++) { + expPn[i][i] = 1.; Assertions.assertArrayEquals(expPn[i], Pn[i], tol, "Failed on line " + i); } @@ -263,7 +280,9 @@ private void checkModelAtT0() { private void checkModelAfterMeasurementAdded(final int expMeasurementNumber, final ObservedMeasurement meas, final RealMatrix expPpred, - final Orbit expOrbitPred) { + final Orbit expOrbitPred, + final RealMatrix expPhi, + final RealMatrix expH) { // Predicted value of SRP coef and sat range bias // (= value before adding measurement to the filter) @@ -301,6 +320,22 @@ private void checkModelAfterMeasurementAdded(final int expMeasurementNumber, R.setEntry(i, i, measSigmas[i] * measSigmas[i]); } + // Innovation matrix + final RealMatrix expS = expH.multiply(expPpred.multiplyTransposed(expH)).add(R); + final RealMatrix S = model.getPhysicalInnovationCovarianceMatrix(); + final double[][] dS = S.subtract(expS).getData(); + for (int i = 0; i < N; i++) { + Assertions.assertArrayEquals(new double[N], dS[i], tol*1e8, "Failed on line \" + i"); + } + + // Kalman gain + final RealMatrix expK = expPpred.multiplyTransposed(expH).multiply(new LUDecomposition(expS).getSolver().getInverse()); + final RealMatrix K = model.getPhysicalKalmanGain(); + final double[][] dK = K.subtract(expK).getData(); + for (int i = 0; i < M; i++) { + Assertions.assertArrayEquals(new double[N], dK[i], tol*1e5, "Failed on line " + i); + } + // Predicted orbit final Orbit orbitPred = model.getPredictedSpacecraftStates()[0].getOrbit(); final PVCoordinates pvOrbitPred = orbitPred.getPVCoordinates(); @@ -312,7 +347,7 @@ private void checkModelAfterMeasurementAdded(final int expMeasurementNumber, // Predicted measurement final double[] measPred = model.getPredictedMeasurement().getEstimatedValue(); - Assertions.assertArrayEquals(expMeasPred, measPred, 3.0e-6); + Assertions.assertArrayEquals(expMeasPred, measPred, 1e-6); // Predicted state final double[] orbitPredState = new double[6]; @@ -324,6 +359,27 @@ private void checkModelAfterMeasurementAdded(final int expMeasurementNumber, expXpred.setEntry(6, srpCoefPred); expXpred.setEntry(7, satRangeBiasPred); + + // Innovation vector + final RealVector observedMeas = MatrixUtils.createRealVector(model.getPredictedMeasurement().getObservedValue()); + final RealVector predictedMeas = MatrixUtils.createRealVector(model.getPredictedMeasurement().getEstimatedValue()); + final RealVector innovation = observedMeas.subtract(predictedMeas); + + // Corrected state + final RealVector expectedXcor = expXpred.add(expK.operate(innovation)); + final RealVector Xcor = model.getPhysicalEstimatedState(); + final double[] dXcor = Xcor.subtract(expectedXcor).toArray(); + Assertions.assertArrayEquals(new double[M], dXcor, tol); + + // Corrected covariance + final RealMatrix expectedPcor = + (MatrixUtils.createRealIdentityMatrix(M). + subtract(expK.multiply(expH))).multiply(expPpred); + final RealMatrix Pcor = model.getPhysicalEstimatedCovarianceMatrix(); + final double[][] dPcor = Pcor.subtract(expectedPcor).getData(); + for (int i = 0; i < M; i++) { + Assertions.assertArrayEquals(new double[M], dPcor[i], tol*1e8, "Failed on line " + i); + } } /** Get an array of the scales of the estimated parameters. @@ -364,8 +420,8 @@ private double[] getParametersScale(final NumericalPropagatorBuilder builder, } /** Create a covariance matrix provider with initial and process noise matrix constant and identical. - * Each element Pij of the initial covariance matrix equals: - * Pij = scales[i]*scales[j] + * Each diagonal element Pii of the initial covariance matrix equals: + * Pii = scales[i]*scales[i] * @param scales scales of the estimated parameters * @return covariance matrix provider */ @@ -374,9 +430,7 @@ private CovarianceMatrixProvider setInitialCovarianceMatrix(final double[] scale final int n = scales.length; final RealMatrix cov = MatrixUtils.createRealMatrix(n, n); for (int i = 0; i < n; i++) { - for (int j = 0; j < n; j++) { - cov.setEntry(i, j, scales[i] * scales[j]); - } + cov.setEntry(i, i, scales[i] * scales[i]); } return new ConstantProcessNoise(cov); } diff --git a/src/test/java/org/orekit/estimation/sequential/UnscentedKalmanOrbitDeterminationTest2.java b/src/test/java/org/orekit/estimation/sequential/UnscentedKalmanOrbitDeterminationTest2.java deleted file mode 100644 index 5dec658345..0000000000 --- a/src/test/java/org/orekit/estimation/sequential/UnscentedKalmanOrbitDeterminationTest2.java +++ /dev/null @@ -1,306 +0,0 @@ -/* 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.estimation.sequential; - -import java.io.File; -import java.io.IOException; -import java.net.URISyntaxException; -import java.util.List; -import java.util.Locale; -import java.util.NoSuchElementException; - -import org.hipparchus.geometry.euclidean.threed.Vector3D; -import org.hipparchus.linear.MatrixUtils; -import org.hipparchus.linear.RealMatrix; -import org.hipparchus.util.FastMath; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.orekit.KeyValueFileParser; -import org.orekit.Utils; -import org.orekit.attitudes.AttitudeProvider; -import org.orekit.bodies.CelestialBody; -import org.orekit.bodies.OneAxisEllipsoid; -import org.orekit.estimation.common.AbstractOrbitDetermination; -import org.orekit.estimation.common.ParameterKey; -import org.orekit.estimation.common.ResultKalman; -import org.orekit.forces.ForceModel; -import org.orekit.forces.drag.DragForce; -import org.orekit.forces.drag.DragSensitive; -import org.orekit.forces.empirical.AccelerationModel; -import org.orekit.forces.empirical.ParametricAcceleration; -import org.orekit.forces.empirical.PolynomialAccelerationModel; -import org.orekit.forces.gravity.HolmesFeatherstoneAttractionModel; -import org.orekit.forces.gravity.OceanTides; -import org.orekit.forces.gravity.Relativity; -import org.orekit.forces.gravity.SolidTides; -import org.orekit.forces.gravity.ThirdBodyAttraction; -import org.orekit.forces.gravity.potential.GravityFieldFactory; -import org.orekit.forces.gravity.potential.ICGEMFormatReader; -import org.orekit.forces.gravity.potential.NormalizedSphericalHarmonicsProvider; -import org.orekit.forces.radiation.KnockeRediffusedForceModel; -import org.orekit.forces.radiation.RadiationSensitive; -import org.orekit.forces.radiation.SolarRadiationPressure; -import org.orekit.models.earth.atmosphere.Atmosphere; -import org.orekit.orbits.Orbit; -import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngleType; -import org.orekit.propagation.conversion.NumericalPropagatorBuilder; -import org.orekit.propagation.conversion.ODEIntegratorBuilder; -import org.orekit.time.TimeScalesFactory; -import org.orekit.utils.IERSConventions; -import org.orekit.utils.ParameterDriver; - -public class UnscentedKalmanOrbitDeterminationTest2 extends AbstractOrbitDetermination { - /** Gravity field. */ - private NormalizedSphericalHarmonicsProvider gravityField; - - /** {@inheritDoc} */ - @Override - protected void createGravityField(final KeyValueFileParser parser) - throws NoSuchElementException { - - final int degree = parser.getInt(ParameterKey.CENTRAL_BODY_DEGREE); - final int order = FastMath.min(degree, parser.getInt(ParameterKey.CENTRAL_BODY_ORDER)); - gravityField = GravityFieldFactory.getNormalizedProvider(degree, order); - - } - - /** {@inheritDoc} */ - @Override - protected double getMu() { - return gravityField.getMu(); - } - - /** {@inheritDoc} */ - @Override - protected NumericalPropagatorBuilder createPropagatorBuilder(final Orbit referenceOrbit, - final ODEIntegratorBuilder builder, - final double positionScale) { - return new NumericalPropagatorBuilder(referenceOrbit, builder, PositionAngleType.MEAN, - positionScale); - } - - /** {@inheritDoc} */ - @Override - protected void setMass(final NumericalPropagatorBuilder propagatorBuilder, - final double mass) { - propagatorBuilder.setMass(mass); - } - - /** {@inheritDoc} */ - @Override - protected List setGravity(final NumericalPropagatorBuilder propagatorBuilder, - final OneAxisEllipsoid body) { - final ForceModel gravityModel = new HolmesFeatherstoneAttractionModel(body.getBodyFrame(), gravityField); - propagatorBuilder.addForceModel(gravityModel); - return gravityModel.getParametersDrivers(); - } - - /** {@inheritDoc} */ - @Override - protected List setOceanTides(final NumericalPropagatorBuilder propagatorBuilder, - final IERSConventions conventions, - final OneAxisEllipsoid body, - final int degree, final int order) { - final ForceModel tidesModel = new OceanTides(body.getBodyFrame(), - gravityField.getAe(), gravityField.getMu(), - degree, order, conventions, - TimeScalesFactory.getUT1(conventions, true)); - propagatorBuilder.addForceModel(tidesModel); - return tidesModel.getParametersDrivers(); - } - - /** {@inheritDoc} */ - @Override - protected List setSolidTides(final NumericalPropagatorBuilder propagatorBuilder, - final IERSConventions conventions, - final OneAxisEllipsoid body, - final CelestialBody[] solidTidesBodies) { - final ForceModel tidesModel = new SolidTides(body.getBodyFrame(), - gravityField.getAe(), gravityField.getMu(), - gravityField.getTideSystem(), conventions, - TimeScalesFactory.getUT1(conventions, true), - solidTidesBodies); - propagatorBuilder.addForceModel(tidesModel); - return tidesModel.getParametersDrivers(); - } - - /** {@inheritDoc} */ - @Override - protected List setThirdBody(final NumericalPropagatorBuilder propagatorBuilder, - final CelestialBody thirdBody) { - final ForceModel thirdBodyModel = new ThirdBodyAttraction(thirdBody); - propagatorBuilder.addForceModel(thirdBodyModel); - return thirdBodyModel.getParametersDrivers(); - } - - /** {@inheritDoc} */ - @Override - protected List setDrag(final NumericalPropagatorBuilder propagatorBuilder, - final Atmosphere atmosphere, final DragSensitive spacecraft) { - final ForceModel dragModel = new DragForce(atmosphere, spacecraft); - propagatorBuilder.addForceModel(dragModel); - return dragModel.getParametersDrivers(); - } - - /** {@inheritDoc} */ - @Override - protected List setSolarRadiationPressure(final NumericalPropagatorBuilder propagatorBuilder, final CelestialBody sun, - final OneAxisEllipsoid body, final RadiationSensitive spacecraft) { - final ForceModel srpModel = new SolarRadiationPressure(sun, body, spacecraft); - propagatorBuilder.addForceModel(srpModel); - return srpModel.getParametersDrivers(); - } - - /** {@inheritDoc} */ - @Override - protected List setAlbedoInfrared(final NumericalPropagatorBuilder propagatorBuilder, - final CelestialBody sun, final double equatorialRadius, - final double angularResolution, - final RadiationSensitive spacecraft) { - final ForceModel albedoIR = new KnockeRediffusedForceModel(sun, spacecraft, equatorialRadius, angularResolution); - propagatorBuilder.addForceModel(albedoIR); - return albedoIR.getParametersDrivers(); - } - - /** {@inheritDoc} */ - @Override - protected List setRelativity(final NumericalPropagatorBuilder propagatorBuilder) { - final ForceModel relativityModel = new Relativity(gravityField.getMu()); - propagatorBuilder.addForceModel(relativityModel); - return relativityModel.getParametersDrivers(); - } - - /** {@inheritDoc} */ - @Override - protected List setPolynomialAcceleration(final NumericalPropagatorBuilder propagatorBuilder, - final String name, final Vector3D direction, final int degree) { - final AccelerationModel accModel = new PolynomialAccelerationModel(name, null, degree); - final ForceModel polynomialModel = new ParametricAcceleration(direction, true, accModel); - propagatorBuilder.addForceModel(polynomialModel); - return polynomialModel.getParametersDrivers(); - } - - /** {@inheritDoc} */ - @Override - protected void setAttitudeProvider(final NumericalPropagatorBuilder propagatorBuilder, - final AttitudeProvider attitudeProvider) { - propagatorBuilder.setAttitudeProvider(attitudeProvider); - } - - @Test - // Orbit determination for Lageos2 based on SLR (range) measurements - public void testLageos2Slr() throws URISyntaxException, IOException { - - // Print results on console - final boolean print = false; - - // input in resources directory - final String inputPath = UnscentedKalmanOrbitDeterminationTest2.class.getClassLoader().getResource("orbit-determination/Lageos2/unscented_kalman_od_test_Lageos2.in").toURI().getPath(); - final File input = new File(inputPath); - - // configure Orekit data acces - Utils.setDataRoot("orbit-determination/february-2016:potential/icgem-format"); - GravityFieldFactory.addPotentialCoefficientsReader(new ICGEMFormatReader("eigen-6s-truncated", true)); - - // Choice of an orbit type to use - // Default for test is Cartesian - final OrbitType orbitType = OrbitType.CARTESIAN; - - // Initial orbital Cartesian covariance matrix - // These covariances are derived from the deltas between initial and reference orbits - // So in a way they are "perfect"... - // Cartesian covariance matrix initialization - final RealMatrix cartesianOrbitalP = MatrixUtils.createRealDiagonalMatrix(new double [] { - 1e4, 4e3, 1, 5e-3, 6e-5, 1e-4 - }); - // Orbital Cartesian process noise matrix (Q) - final RealMatrix cartesianOrbitalQ = MatrixUtils.createRealDiagonalMatrix(new double [] { - 3e-4, 3e-4, 3e-4, 3e-7, 3e-7, 3e-7 - }); - - // Initial measurement covariance matrix and process noise matrix - final RealMatrix measurementP = MatrixUtils.createRealDiagonalMatrix(new double [] { - 1., 1., 1., 1. - }); - final RealMatrix measurementQ = MatrixUtils.createRealDiagonalMatrix(new double [] { - 1e-6, 1e-6, 1e-6, 1e-6 - }); - - // Kalman orbit determination run. - ResultKalman kalmanLageos2 = runKalman(input, orbitType, print, - cartesianOrbitalP, cartesianOrbitalQ, - null, null, - measurementP, measurementQ, true); - - // Definition of the reference parameters for the tests - - final double distanceAccuracy = 1.68; - final double velocityAccuracy = 2.71e-3; - final double[] RefStatRange = { -1.698412, 1.529126, 0.029547, 0.338275 }; - - // Tests - // Note: The reference initial orbit is the same as in the batch LS tests - // ----- - - // Number of measurements processed - final int numberOfMeas = 258; - Assertions.assertEquals(numberOfMeas, kalmanLageos2.getNumberOfMeasurements()); - - // Estimated position and velocity - final Vector3D estimatedPos = kalmanLageos2.getEstimatedPV().getPosition(); - final Vector3D estimatedVel = kalmanLageos2.getEstimatedPV().getVelocity(); - - // Reference position and velocity at initial date (same as in batch LS test) - final Vector3D refPos0 = new Vector3D(-5532131.956902, 10025696.592156, -3578940.040009); - final Vector3D refVel0 = new Vector3D(-3871.275109, -607.880985, 4280.972530); - - // Run the reference until Kalman last date - final Orbit refOrbit = runReference(input, orbitType, refPos0, refVel0, null, - kalmanLageos2.getEstimatedPV().getDate()); - final Vector3D refPos = refOrbit.getPosition(); - final Vector3D refVel = refOrbit.getPVCoordinates().getVelocity(); - - // Check distances - 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); - - // Print orbit deltas - if (print) { - System.out.println("Test performances:"); - System.out.format("\t%-30s\n", - "ΔEstimated / Reference"); - System.out.format(Locale.US, "\t%-10s %20.6f\n", - "ΔP [m]", dP); - System.out.format(Locale.US, "\t%-10s %20.6f\n", - "ΔV [m/s]", dV); - } - - - //test on statistic for the range residuals - final long nbRange = 258; - Assertions.assertEquals(nbRange, kalmanLageos2.getRangeStat().getN()); - Assertions.assertEquals(RefStatRange[0], kalmanLageos2.getRangeStat().getMin(), distanceAccuracy); - Assertions.assertEquals(RefStatRange[1], kalmanLageos2.getRangeStat().getMax(), distanceAccuracy); - Assertions.assertEquals(RefStatRange[2], kalmanLageos2.getRangeStat().getMean(), distanceAccuracy); - Assertions.assertEquals(RefStatRange[3], kalmanLageos2.getRangeStat().getStandardDeviation(), distanceAccuracy); - - } - -} diff --git a/src/test/java/org/orekit/files/ccsds/section/CommentsContainerTest.java b/src/test/java/org/orekit/files/ccsds/section/CommentsContainerTest.java new file mode 100644 index 0000000000..50f5484b10 --- /dev/null +++ b/src/test/java/org/orekit/files/ccsds/section/CommentsContainerTest.java @@ -0,0 +1,50 @@ +package org.orekit.files.ccsds.section; + +import java.util.ArrayList; +import java.util.List; + +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.data.DataSource; +import org.orekit.files.ccsds.ndm.ParsedUnitsBehavior; +import org.orekit.files.ccsds.ndm.ParserBuilder; +import org.orekit.files.ccsds.ndm.odm.opm.Opm; + +public class CommentsContainerTest { + + @BeforeEach + public void setUp() { + Utils.setDataRoot("regular-data"); + } + + @Test + void testSetComments() { + + final String expectedOldComment = "GEOCENTRIC, CARTESIAN, EARTH FIXED"; + final String expectedNewComment1 = "NEW COMMENT 1"; + final String expectedNewComment2 = "LAST NEW COMMENT!"; + + // Parse OPM and check comments in metadata + final String opmName = "/ccsds/odm/opm/OPMExample1.txt"; + final DataSource source1 = new DataSource(opmName, () -> getClass().getResourceAsStream(opmName)); + final Opm original = new ParserBuilder(). + withParsedUnitsBehavior(ParsedUnitsBehavior.STRICT_COMPLIANCE). + buildOpmParser(). + parseMessage(source1); + + Assertions.assertEquals(1, original.getMetadata().getComments().size()); + Assertions.assertEquals(expectedOldComment, original.getMetadata().getComments().get(0)); + + // Set new comments and check + List newComments = new ArrayList<>(); + newComments.add(expectedNewComment1); + newComments.add(expectedNewComment2); + original.getMetadata().setComments(newComments); + + Assertions.assertEquals(2, original.getMetadata().getComments().size()); + Assertions.assertEquals(expectedNewComment1, original.getMetadata().getComments().get(0)); + Assertions.assertEquals(expectedNewComment2, original.getMetadata().getComments().get(1)); + } +} diff --git a/src/test/java/org/orekit/files/rinex/HatanakaCompressFilterTest.java b/src/test/java/org/orekit/files/rinex/HatanakaCompressFilterTest.java index 3db75e42c0..3f874da1dc 100644 --- a/src/test/java/org/orekit/files/rinex/HatanakaCompressFilterTest.java +++ b/src/test/java/org/orekit/files/rinex/HatanakaCompressFilterTest.java @@ -56,7 +56,7 @@ public void setUp() { } @Test - public void testNotFiltered() throws IOException { + public void testNotFiltered() { final String name = "rinex/aaaa0000.00o"; final DataSource raw = new DataSource(name, @@ -123,7 +123,7 @@ private void doTestWrong(final String name, final OrekitMessages expectedError) } @Test - public void testRinex2MoreThan12Satellites() throws IOException, NoSuchAlgorithmException { + public void testRinex2MoreThan12Satellites() throws NoSuchAlgorithmException { final String name = "rinex/bogi1210.09d.Z"; final DataSource raw = new DataSource(name.substring(name.indexOf('/') + 1), @@ -156,7 +156,7 @@ public void testRinex2MoreThan12Satellites() throws IOException, NoSuchAlgorithm } @Test - public void testHatanakaRinex2() throws IOException, NoSuchAlgorithmException { + public void testHatanakaRinex2() throws NoSuchAlgorithmException { final String name = "rinex/arol0090.01d.Z"; final DataSource raw = new DataSource(name.substring(name.indexOf('/') + 1), @@ -213,7 +213,7 @@ public void testHatanakaRinex2() throws IOException, NoSuchAlgorithmException { } @Test - public void testCompressedRinex3() throws IOException, NoSuchAlgorithmException { + public void testCompressedRinex3() throws NoSuchAlgorithmException { //Tests Rinex 3 with Hatanaka compression final String name = "rinex/GANP00SVK_R_20151890000_01H_10M_MO.crx.gz"; @@ -271,13 +271,13 @@ public void testCompressedRinex3() throws IOException, NoSuchAlgorithmException } @Test - public void testClockReset() throws IOException, NoSuchAlgorithmException { + public void testClockReset() throws NoSuchAlgorithmException { final String name = "rinex/clckReset_U_20190320000_10M_10M_MO.crx"; final DataSource raw = new DataSource(name.substring(name.indexOf('/') + 1), () -> Utils.class.getClassLoader().getResourceAsStream(name)); Digester digester = new Digester(new HatanakaCompressFilter().filter(raw), - "b54f4ec3fb860a032f93f569199224e247b35ceba309bc96478779ff7120455a"); + "9ab0ec7c8547110a7612ec7740d787ee1062e37428cee1092163ba9bb03afeb4"); RinexObservationParser parser = new RinexObservationParser(); List ods = parser.parse(digester.getDigestedSource()).getObservationDataSets(); @@ -297,7 +297,7 @@ public void testClockReset() throws IOException, NoSuchAlgorithmException { } @Test - public void testWith5thOrderDifferencesClockOffsetReinitialization() throws IOException, NoSuchAlgorithmException { + public void testWith5thOrderDifferencesClockOffsetReinitialization() throws NoSuchAlgorithmException { // the following file has several specific features with respect to Hatanaka compression // - we created it using 5th order differences instead of standard 3rd order @@ -307,7 +307,7 @@ public void testWith5thOrderDifferencesClockOffsetReinitialization() throws IOEx final DataSource raw = new DataSource(name.substring(name.indexOf('/') + 1), () -> Utils.class.getClassLoader().getResourceAsStream(name)); Digester digester = new Digester(new HatanakaCompressFilter().filter(new GzipFilter().filter(raw)), - "6ac7c9160d2131581156b2ea88c0c5844b88e1369c3a03956cb77f919c5cca3e"); + "623908ad1c356759fd1e88cbce802aa41c26112b08a51f2b5aa34a33add6fecd"); RinexObservationParser parser = new RinexObservationParser(); List ods = parser.parse(digester.getDigestedSource()).getObservationDataSets(); @@ -325,7 +325,7 @@ public void testWith5thOrderDifferencesClockOffsetReinitialization() throws IOEx } @Test - public void testSplice() throws IOException, NoSuchAlgorithmException { + public void testSplice() throws NoSuchAlgorithmException { final String name = "rinex/aber0440.16d.Z"; final DataSource raw = new DataSource(name.substring(name.indexOf('/') + 1), @@ -350,7 +350,7 @@ public void testSplice() throws IOException, NoSuchAlgorithmException { } @Test - public void testVerySmallValue() throws IOException, NoSuchAlgorithmException { + public void testVerySmallValue() throws NoSuchAlgorithmException { final String name = "rinex/abmf0440.16d.Z"; final DataSource raw = new DataSource(name.substring(name.indexOf('/') + 1), @@ -377,7 +377,7 @@ public void testVerySmallValue() throws IOException, NoSuchAlgorithmException { } @Test - public void testMultipleOf5Observations() throws IOException, NoSuchAlgorithmException { + public void testMultipleOf5Observations() throws NoSuchAlgorithmException { final String name = "rinex/arev0440.16d.Z"; final DataSource raw = new DataSource(name.substring(name.indexOf('/') + 1), @@ -404,7 +404,7 @@ public void testMultipleOf5Observations() throws IOException, NoSuchAlgorithmExc } @Test - public void testSingleByteReads() throws IOException, NoSuchAlgorithmException { + public void testSingleByteReads() throws IOException { final String name = "rinex/arev0440.16d.Z"; final DataSource raw = new DataSource(name.substring(name.indexOf('/') + 1), @@ -424,8 +424,8 @@ public void testSingleByteReads() throws IOException, NoSuchAlgorithmException { public void testDifferential3rdOrder() { doTestDifferential(15, 3, 3, new long[] { - 40517356773l, -991203l, -38437l, - 3506l, -630l, 2560l + 40517356773L, -991203L, -38437L, + 3506L, -630L, 2560L }, new String[] { " 40517356.773", " 40516365.570", " 40515335.930", " 40514271.359", " 40513171.227", " 40512038.094" @@ -436,8 +436,8 @@ public void testDifferential3rdOrder() { public void testDifferential5thOrder() { doTestDifferential(12, 5, 5, new long[] { - 23439008766l, -19297641l, 30704l, 3623l, -8215l, - 14517l, -6644l, -2073l, 4164l, -2513l + 23439008766L, -19297641L, 30704L, 3623L, -8215L, + 14517L, -6644L, -2073L, 4164L, -2513L }, new String[] { "234390.08766", "234197.11125", "234004.44188", "233812.11578", "233620.08703", "233428.37273", "233236.98656", "233045.91805", "232855.17422", "232664.75445" @@ -538,7 +538,7 @@ private void doTestText(final int fieldLength, final String[] compressed, final } @Test - public void testManyObservations() throws IOException, NoSuchAlgorithmException { + public void testManyObservations() throws NoSuchAlgorithmException { final String name = "rinex/THTG00PYF_R_20160440000_60S_30S_MO.crx.gz"; final DataSource raw = new DataSource(name.substring(name.indexOf('/') + 1), @@ -565,7 +565,7 @@ public void testManyObservations() throws IOException, NoSuchAlgorithmException } @Test - public void testSeptentrioMissingType() throws IOException, NoSuchAlgorithmException { + public void testSeptentrioMissingType() throws NoSuchAlgorithmException { final String name = "rinex/TLSG00FRA_R_20160440000_30S_30S_MO.crx.gz"; final DataSource raw = new DataSource(name.substring(name.indexOf('/') + 1), @@ -592,7 +592,7 @@ public void testSeptentrioMissingType() throws IOException, NoSuchAlgorithmExcep } @Test - public void testSeptentrioPhaseShiftWithS() throws IOException, NoSuchAlgorithmException { + public void testSeptentrioPhaseShiftWithS() throws NoSuchAlgorithmException { final String name = "rinex/VILL00ESP_R_20160440000_01D_30S_MO.crx.gz"; final DataSource raw = new DataSource(name.substring(name.indexOf('/') + 1), @@ -619,7 +619,7 @@ public void testSeptentrioPhaseShiftWithS() throws IOException, NoSuchAlgorithmE } @Test - public void testIssue892() throws IOException, NoSuchAlgorithmException { + public void testIssue892() throws NoSuchAlgorithmException { //Tests Rinex 3 with Hatanaka compression final String name = "rinex/DJIG00DJI_R_20191820000_01D_30S_MO.crx.gz"; @@ -673,7 +673,7 @@ public void testIssue892() throws IOException, NoSuchAlgorithmException { } @Test - public void testPrnNbObs() throws IOException, NoSuchAlgorithmException { + public void testPrnNbObs() throws NoSuchAlgorithmException { //Tests Rinex 3 with Hatanaka compression final String name = "rinex/YEBE00ESP_R_20230891800_01H_30S_MO.crx.gz"; diff --git a/src/test/java/org/orekit/files/rinex/clock/ClockFileParserTest.java b/src/test/java/org/orekit/files/rinex/clock/ClockFileParserTest.java index 140b52af0e..cf45a5e33d 100644 --- a/src/test/java/org/orekit/files/rinex/clock/ClockFileParserTest.java +++ b/src/test/java/org/orekit/files/rinex/clock/ClockFileParserTest.java @@ -21,6 +21,7 @@ import java.net.URISyntaxException; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; @@ -29,6 +30,9 @@ import org.junit.jupiter.api.Test; import org.orekit.Utils; import org.orekit.data.DataContext; +import org.orekit.data.DataSource; +import org.orekit.data.FiltersManager; +import org.orekit.data.GzipFilter; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitIllegalArgumentException; import org.orekit.errors.OrekitMessages; @@ -38,10 +42,14 @@ import org.orekit.files.rinex.clock.RinexClock.ReferenceClock; import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; +import org.orekit.frames.ITRFVersion; import org.orekit.gnss.ObservationType; import org.orekit.gnss.SatelliteSystem; import org.orekit.gnss.TimeSystem; import org.orekit.time.AbsoluteDate; +import org.orekit.time.ClockModel; +import org.orekit.time.ClockOffset; +import org.orekit.time.SampledClockModel; import org.orekit.time.TimeScale; import org.orekit.time.TimeScalesFactory; import org.orekit.utils.IERSConventions; @@ -57,7 +65,7 @@ public void setUp() { /** First example given in the 3.04 RINEX clock file format. */ @Test - public void testParseExple1V304() throws URISyntaxException, IOException { + public void testParseExple1V304() throws URISyntaxException { // Parse file final String ex = "/gnss/clock/Exple_analysis_1_304.clk"; @@ -118,14 +126,14 @@ public void testParseExple1V304() throws URISyntaxException, IOException { // In this case, time system in properlydefined, check getEpoch() methods // Get data line final ClockDataLine dataLine = file.getClockData().get(id).get(0); - Assertions.assertTrue(dataLine.getEpoch().equals(dataLine.getEpoch(file.getTimeScale()))); + Assertions.assertEquals(dataLine.getEpoch(), dataLine.getEpoch(file.getTimeScale())); } /** Second example given in the 3.04 RINEX clock file format. * PCVS block is not placed where it should be. */ @Test - public void testParseExple2V304() throws URISyntaxException, IOException { + public void testParseExple2V304() throws URISyntaxException { // Parse file final String ex = "/gnss/clock/Exple_analysis_2_304.clk"; @@ -195,7 +203,7 @@ public void testParseExple2V304() throws URISyntaxException, IOException { * It embeds calibration data and misses time system ID. */ @Test - public void testParseExpleCalibrationV304() throws URISyntaxException, IOException { + public void testParseExpleCalibrationV304() throws URISyntaxException { // Parse file final String ex = "/gnss/clock/Exple_calibration_304.clk"; @@ -258,7 +266,7 @@ public void testParseExpleCalibrationV304() throws URISyntaxException, IOExcepti * Creation date also does not match expected format. */ @Test - public void testParseExple1V300() throws URISyntaxException, IOException { + public void testParseExple1V300() throws URISyntaxException { // Parse file final String ex = "/gnss/clock/mit19044_truncated_300.clk"; @@ -320,7 +328,7 @@ public void testParseExple1V300() throws URISyntaxException, IOException { * PCVS block is not placed where it should be. */ @Test - public void testParseExple2V300() throws URISyntaxException, IOException { + public void testParseExple2V300() throws URISyntaxException { // Parse file final String ex = "/gnss/clock/igr21101_truncated_300.clk"; @@ -384,9 +392,34 @@ public void testParseExple2V300() throws URISyntaxException, IOException { clockBias, clockBiasSigma, clockRate, clockRateSigma, clockAcceleration, clockAccelerationSigma); } + @Test + public void testClockModel() throws URISyntaxException { + + // Parse file + final String ex = "/gnss/clock/cod17381_truncated_200.clk"; + + final RinexClockParser parser = new RinexClockParser(); + final String fileName = Paths.get(getClass().getResource(ex).toURI()).toString(); + final RinexClock file = parser.parse(fileName); + final ClockModel clockModel = file.extractClockModel("AMC2", 2); + + // points exactly on files entries + final ClockOffset c1 = clockModel.getOffset(new AbsoluteDate(2013, 4, 29, 0, 0, 0.0, file.getTimeScale())); + final ClockOffset c2 = clockModel.getOffset(new AbsoluteDate(2013, 4, 29, 0, 0, 30.0, file.getTimeScale())); + Assertions.assertEquals(0.192309152524E-08, c1.getOffset(), 1.0e-21); + Assertions.assertEquals(0.192333320310E-08, c2.getOffset(), 1.0e-21); + + // intermediate point + final ClockOffset c = clockModel.getOffset(new AbsoluteDate(2013, 4, 29, 0, 0, 12.0, file.getTimeScale())); + Assertions.assertEquals(0.1923188196384e-08, c.getOffset(), 1.0e-21); + Assertions.assertEquals(8.05592866666e-15, c.getRate(), 1.0e-26); + Assertions.assertEquals( 0.0, c.getAcceleration(), 1.0e-40); + + } + /** An example of the 2.00 RINEX clock file format. */ @Test - public void testParseExple1V200() throws URISyntaxException, IOException { + public void testParseExple1V200() throws URISyntaxException { // Parse file final String ex = "/gnss/clock/emr10491_truncated_200.clk"; @@ -446,7 +479,7 @@ public void testParseExple1V200() throws URISyntaxException, IOException { /** Another example of the 2.00 RINEX clock file format with another date time zone foramt. */ @Test - public void testParseExple2V200() throws URISyntaxException, IOException { + public void testParseExple2V200() throws URISyntaxException { // Parse file final String ex = "/gnss/clock/jpl11456_truncated_200.clk"; @@ -510,7 +543,7 @@ public void testParseExple2V200() throws URISyntaxException, IOException { /** Another example of the 2.00 RINEX clock file format with another date time zone foramt. */ @Test - public void testParseExple3V200() throws URISyntaxException, IOException { + public void testParseExple3V200() throws URISyntaxException { // Parse file final String ex = "/gnss/clock/cod17381_truncated_200.clk"; @@ -551,7 +584,7 @@ public void testParseExple3V200() throws URISyntaxException, IOException { final String id = "AMC2"; final ClockDataType type = ClockDataType.AR; final TimeScale timeScale = TimeScalesFactory.getGPS(); - final AbsoluteDate dataEpoch = new AbsoluteDate(2013, 04, 29, 0, 0, 30.0, timeScale); + final AbsoluteDate dataEpoch = new AbsoluteDate(2013, 4, 29, 0, 0, 30.0, timeScale); final int numberOfValues = 1; final double clockBias = 0.192333320310E-08; final double clockBiasSigma = 0.0; @@ -572,7 +605,7 @@ public void testParseExple3V200() throws URISyntaxException, IOException { /** Test parsing file with observation type continuation line. */ @Test - public void testParseWithObsTypeContinuationLine() throws URISyntaxException, IOException { + public void testParseWithObsTypeContinuationLine() throws URISyntaxException { // Parse file final String ex = "/gnss/clock/Exple_analysis_1_304_more_obs_types.clk"; @@ -597,7 +630,7 @@ public void testParseWithObsTypeContinuationLine() throws URISyntaxException, IO /** Check receiver inforamtion. */ @Test - public void testParsedReceivers() throws URISyntaxException, IOException { + public void testParsedReceivers() throws URISyntaxException { // Parse file final String ex = "/gnss/clock/Exple_analysis_2_304.clk"; @@ -627,11 +660,15 @@ public void testParsedReceivers() throws URISyntaxException, IOException { /** Test default frame loader. */ @Test - public void testDefaultFrameLoader() throws URISyntaxException, IOException { + public void testDefaultFrameLoader() throws URISyntaxException { // Get frames corresponding to default frame loader - final Frame itrf1996 = FramesFactory.getITRF(IERSConventions.IERS_1996, false); - final Frame itrf2010 = FramesFactory.getITRF(IERSConventions.IERS_2010, false); + final Frame itrf1996 = FramesFactory.getITRF(ITRFVersion.ITRF_1996, + IERSConventions.IERS_1996, + false); + final Frame itrf2014 = FramesFactory.getITRF(ITRFVersion.ITRF_2014, + IERSConventions.IERS_2010, + false); // Get default clock file parser final RinexClockParser parser = new RinexClockParser(); @@ -641,19 +678,19 @@ public void testDefaultFrameLoader() throws URISyntaxException, IOException { final String fileName1 = Paths.get(getClass().getResource(ex1).toURI()).toString(); final RinexClock file1 = parser.parse(fileName1); - // Parse file with default expected frame ITRF 2010 + // Parse file with default expected frame ITRF 2014 final String ex2 = "/gnss/clock/Exple_analysis_2_304.clk"; final String fileName2 = Paths.get(getClass().getResource(ex2).toURI()).toString(); final RinexClock file2 = parser.parse(fileName2); // Check frames - Assertions.assertTrue(itrf1996.equals(file1.getFrame())); - Assertions.assertTrue(itrf2010.equals(file2.getFrame())); + Assertions.assertSame(itrf1996, file1.getFrame()); + Assertions.assertSame(itrf2014, file2.getFrame()); } /** Test the reference clocks. */ @Test - public void testReferenceClocks() throws IOException, URISyntaxException { + public void testReferenceClocks() throws URISyntaxException { // Parse file final String ex = "/gnss/clock/Exple_analysis_1_304.clk"; @@ -701,7 +738,7 @@ public void testReferenceClocks() throws IOException, URISyntaxException { /** Test the satelite list. */ @Test - public void testSatelliteList() throws IOException, URISyntaxException { + public void testSatelliteList() throws URISyntaxException { // Parse file final String ex = "/gnss/clock/Exple_analysis_1_304.clk"; @@ -724,7 +761,7 @@ public void testSatelliteList() throws IOException, URISyntaxException { /** Test two same receivers and satellite. */ @Test - public void testSameReceiversAndSatellites() throws IOException, URISyntaxException { + public void testSameReceiversAndSatellites() throws URISyntaxException { // Parse file final String ex = "/gnss/clock/two_same_receivers_and_satellites.clk"; @@ -738,7 +775,7 @@ public void testSameReceiversAndSatellites() throws IOException, URISyntaxExcept /** Test the clock data type list. */ @Test - public void testClockDataTypes() throws IOException, URISyntaxException { + public void testClockDataTypes() throws URISyntaxException { // Parse file final String ex = "/gnss/clock/Exple_calibration_304.clk"; @@ -750,7 +787,7 @@ public void testClockDataTypes() throws IOException, URISyntaxException { final List dataTypes = file.getClockDataTypes(); // Expected list - final List expected = new ArrayList(); + final List expected = new ArrayList<>(); expected.add(ClockDataType.CR); expected.add(ClockDataType.DR); @@ -759,9 +796,95 @@ public void testClockDataTypes() throws IOException, URISyntaxException { } } + /** Test the reference clocks. */ + @Test + public void testSplice() { + + // Parse file& + final RinexClockParser parser = new RinexClockParser(); + final String ex1 = "/gnss/clock/part-1.clk"; + final RinexClock clk1 = parser.parse(new DataSource(ex1, () -> getClass().getResourceAsStream(ex1))); + Assertions.assertEquals(0, + new AbsoluteDate(2020, 9, 1, 0, 0, 0.0, clk1.getTimeScale()).durationFrom(clk1.getEarliestEpoch()), + 1.0e-15); + Assertions.assertEquals(0, + new AbsoluteDate(2020, 9, 1, 0, 4, 30.0, clk1.getTimeScale()).durationFrom(clk1.getLatestEpoch()), + 1.0e-15); + Assertions.assertEquals(5, clk1.getNumberOfReceivers()); + Assertions.assertEquals(7, clk1.getNumberOfSatellites()); + + final String ex2 = "/gnss/clock/part-2.clk"; + final RinexClock clk2 = parser.parse(new DataSource(ex2, () -> getClass().getResourceAsStream(ex2))); + Assertions.assertEquals(0, + new AbsoluteDate(2020, 9, 1, 0, 5, 0.0, clk2.getTimeScale()).durationFrom(clk2.getEarliestEpoch()), + 1.0e-15); + Assertions.assertEquals(0, + new AbsoluteDate(2020, 9, 1, 0, 9, 30.0, clk2.getTimeScale()).durationFrom(clk2.getLatestEpoch()), + 1.0e-15); + Assertions.assertEquals(4, clk2.getNumberOfReceivers()); + Assertions.assertEquals(7, clk2.getNumberOfSatellites()); + + final String ex3 = "/gnss/clock/part-3.clk"; + final RinexClock clk3 = parser.parse(new DataSource(ex3, () -> getClass().getResourceAsStream(ex3))); + Assertions.assertEquals(0, + new AbsoluteDate(2020, 9, 1, 0, 10, 0.0, clk3.getTimeScale()).durationFrom(clk3.getEarliestEpoch()), + 1.0e-15); + Assertions.assertEquals(0, + new AbsoluteDate(2020, 9, 1, 0, 14, 30.0, clk3.getTimeScale()).durationFrom(clk3.getLatestEpoch()), + 1.0e-15); + Assertions.assertEquals(5, clk3.getNumberOfReceivers()); + Assertions.assertEquals(6, clk3.getNumberOfSatellites()); + + // Splice all files + try { + RinexClock.splice(Arrays.asList(clk1, clk2, clk3), 10.0); + Assertions.fail("an exception should have been thrown"); + } catch (OrekitException oe) { + Assertions.assertEquals(OrekitMessages.TOO_LONG_TIME_GAP_BETWEEN_DATA_POINTS, + oe.getSpecifier()); + Assertions.assertEquals(30.0, (Double)oe.getParts()[0], 1.0e-10); + } + + // we intentionally provide the files in non-chronological order for testing purposes + final RinexClock spliced = RinexClock.splice(Arrays.asList(clk3, clk1, clk2), 60.0); + Assertions.assertEquals(0, + new AbsoluteDate(2020, 9, 1, 0, 0, 0.0, spliced.getTimeScale()).durationFrom(spliced.getEarliestEpoch()), + 1.0e-15); + Assertions.assertEquals(0, + new AbsoluteDate(2020, 9, 1, 0, 14, 30.0, spliced.getTimeScale()).durationFrom(spliced.getLatestEpoch()), + 1.0e-15); + Assertions.assertEquals(4, spliced.getNumberOfReceivers()); + Assertions.assertEquals(5, spliced.getNumberOfSatellites()); + for (final String id : Arrays.asList("CHPI", "GLPS", "KITG", "OWMG", + "G17", "G27", "R17", "R23", "J01")) { + SampledClockModel cm = spliced.extractClockModel(id, 2); + Assertions.assertEquals(spliced.getEarliestEpoch(), cm.getValidityStart()); + Assertions.assertEquals(spliced.getLatestEpoch(), cm.getValidityEnd()); + Assertions.assertEquals(30, cm.getCache().getAll().size()); + } + + final AbsoluteDate middleDate = new AbsoluteDate(2020, 9, 1, 0, 4, 45.0, spliced.getTimeScale()); + try { + clk1.extractClockModel("J01", 2).getOffset(middleDate); + Assertions.fail("an exception should have been thrown"); + } catch (OrekitException oe) { + Assertions.assertEquals(OrekitMessages.UNABLE_TO_GENERATE_NEW_DATA_AFTER, oe.getSpecifier()); + } + try { + clk2.extractClockModel("J01", 2).getOffset(middleDate); + Assertions.fail("an exception should have been thrown"); + } catch (OrekitException oe) { + Assertions.assertEquals(OrekitMessages.UNABLE_TO_GENERATE_NEW_DATA_BEFORE, oe.getSpecifier()); + } + Assertions.assertEquals(-1.83536264e-4, + spliced.extractClockModel("J01", 2).getOffset(middleDate).getOffset(), + 1.0e-12); + + } + /** Test parsing error exception. */ @Test - public void testParsingErrorException() throws IOException { + public void testParsingErrorException() { try { final String ex = "/gnss/clock/error_in_line_4.clk"; final RinexClockParser parser = new RinexClockParser(); @@ -776,7 +899,7 @@ public void testParsingErrorException() throws IOException { /** Test missing block error exception. */ @Test - public void testMissingBlockException() throws IOException { + public void testMissingBlockException() { try { final String ex = "/gnss/clock/missing_block_end_of_header.clk"; final RinexClockParser parser = new RinexClockParser(); @@ -791,7 +914,7 @@ public void testMissingBlockException() throws IOException { /** Unsupported clock file version exception throwing test. */ @Test - public void testUnsupportedVersion() throws IOException { + public void testUnsupportedVersion() { try { final String ex = "/gnss/clock/unsupported_clock_file_version.clk"; final RinexClockParser parser = new RinexClockParser(); @@ -806,7 +929,7 @@ public void testUnsupportedVersion() throws IOException { /** Wrong clock data type exception throwing test. */ @Test - public void testWrongClockDataType() throws IOException { + public void testWrongClockDataType() { try { final String ex = "/gnss/clock/wrong_clock_data_type.clk"; final RinexClockParser parser = new RinexClockParser(); @@ -821,7 +944,7 @@ public void testWrongClockDataType() throws IOException { /** Unknown time system exception throwing test. */ @Test - public void testUnknownTimeSystem() throws IOException { + public void testUnknownTimeSystem() { try { final String ex = "/gnss/clock/unknown_time_system.clk"; final RinexClockParser parser = new RinexClockParser(); @@ -860,7 +983,7 @@ public void testTimeSystem() { /** Test parsing file of issue #845 (https://gitlab.orekit.org/orekit/orekit/-/issues/845). */ @Test - public void testIssue845() throws URISyntaxException, IOException { + public void testIssue845() throws URISyntaxException { // Parse file final String ex = "/gnss/clock/issue845.clk"; @@ -875,6 +998,26 @@ public void testIssue845() throws URISyntaxException, IOException { Assertions.assertEquals("IGS14", file.getFrameName()); } + @Test + public void testMixedSystem() throws IOException { + + // Parse file + final String ex = "/gnss/clock/issue1356.clk.gz"; + final DataSource original = new DataSource(ex, () -> getClass().getResourceAsStream(ex)); + final FiltersManager filtersManager = new FiltersManager(); + filtersManager.addFilter(new GzipFilter()); + final DataSource source = filtersManager.applyRelevantFilters(original); + + final RinexClock file = new RinexClockParser().parse(source); + + Assertions.assertEquals(3.0, file.getFormatVersion(), 1.0e-3); + Assertions.assertEquals(SatelliteSystem.MIXED, file.getSatelliteSystem()); + Assertions.assertEquals(54, file.getSatellites().size()); + Assertions.assertEquals(10, file.getReceivers().size()); + Assertions.assertTrue(file.getListAppliedDCBS().isEmpty()); + Assertions.assertTrue(file.getListAppliedPCVS().isEmpty()); + } + /** Check the content of a clock file. */ private void checkClockFileContent(final RinexClock file, final double version, final SatelliteSystem satelliteSystem, final TimeSystem timeSystem, @@ -894,8 +1037,8 @@ private void checkClockFileContent(final RinexClock file, // Check header Assertions.assertEquals(version, file.getFormatVersion(), 1E-3); - Assertions.assertTrue(satelliteSystem == file.getSatelliteSystem()); - Assertions.assertTrue(timeSystem == file.getTimeSystem()); + Assertions.assertSame(satelliteSystem, file.getSatelliteSystem()); + Assertions.assertSame(timeSystem, file.getTimeSystem()); Assertions.assertEquals(programName, file.getProgramName()); Assertions.assertEquals(agencyName, file.getAgencyName()); Assertions.assertEquals(comments, file.getComments()); @@ -908,7 +1051,7 @@ private void checkClockFileContent(final RinexClock file, Assertions.assertEquals(creationTimeString, file.getCreationTimeString()); Assertions.assertEquals(creationZoneString, file.getCreationTimeZoneString()); if (null != creationDate) { - Assertions.assertTrue(file.getCreationDate().equals(creationDate)); + Assertions.assertEquals(file.getCreationDate(), creationDate); } Assertions.assertEquals(numberOfLeapSeconds, file.getNumberOfLeapSeconds()); Assertions.assertEquals(numberOfLeapSecondsGPS, file.getNumberOfLeapSecondsGNSS()); @@ -923,6 +1066,7 @@ private void checkClockFileContent(final RinexClock file, Assertions.assertEquals(frameString, file.getFrameName()); Assertions.assertEquals(numberOfReceivers, file.getNumberOfReceivers()); Assertions.assertEquals(numberOfSatellites, file.getNumberOfSatellites()); + Assertions.assertEquals(timeScale.getName(), file.getTimeScale().getName()); // Check total number of data lines Assertions.assertEquals(numberOfDataLines, file.getTotalNumberOfDataLines()); @@ -959,8 +1103,8 @@ private void checkReferenceClock(final ReferenceClock referenceClock, Assertions.assertEquals(referenceName, referenceClock.getReferenceName()); Assertions.assertEquals(clockId, referenceClock.getClockID()); Assertions.assertEquals(clockConstraint, referenceClock.getClockConstraint(), 1e-12); - Assertions.assertTrue(startDate.equals(referenceClock.getStartDate())); - Assertions.assertTrue(endDate.equals(referenceClock.getEndDate())); + Assertions.assertEquals(startDate, referenceClock.getStartDate()); + Assertions.assertEquals(endDate, referenceClock.getEndDate()); } } diff --git a/src/test/java/org/orekit/files/rinex/observation/RinexObservationParserTest.java b/src/test/java/org/orekit/files/rinex/observation/RinexObservationParserTest.java index 3a8cd691f9..9e6e329049 100644 --- a/src/test/java/org/orekit/files/rinex/observation/RinexObservationParserTest.java +++ b/src/test/java/org/orekit/files/rinex/observation/RinexObservationParserTest.java @@ -37,6 +37,8 @@ import org.orekit.gnss.ObservationType; import org.orekit.gnss.SatelliteSystem; import org.orekit.time.AbsoluteDate; +import org.orekit.time.ClockOffset; +import org.orekit.time.SampledClockModel; import org.orekit.time.TimeScale; import org.orekit.time.TimeScalesFactory; @@ -50,13 +52,24 @@ public void setUp() { @Test public void testDefaultLoadRinex2() { - Assertions.assertEquals(24, load("rinex/aiub0000.00o").getObservationDataSets().size()); + final RinexObservation ro = load("rinex/aiub0000.00o"); + Assertions.assertEquals(24, ro.getObservationDataSets().size()); + final SampledClockModel clockModel = ro.extractClockModel(2); + Assertions.assertEquals(6, clockModel.getCache().getAll().size()); + final ClockOffset offset = + clockModel.getOffset(new AbsoluteDate(2001, 3, 24, 13, 11, 57.0, + TimeScalesFactory.getGPS())); + Assertions.assertEquals(-0.123456888, offset.getOffset(), 1.0e-15); + Assertions.assertEquals(-1.1e-8, offset.getRate(), 1.0e-18); + Assertions.assertEquals( 0.0, offset.getAcceleration(), 1.0e-20); } @Test public void testDefaultLoadRinex3() { Utils.setDataRoot("regular-data:rinex"); - Assertions.assertEquals(5, load("rinex/brca083.06o").getObservationDataSets().size()); + final RinexObservation ro = load("rinex/brca083.06o"); + Assertions.assertEquals(5, ro.getObservationDataSets().size()); + Assertions.assertNull(ro.extractClockModel(2)); } @Test @@ -82,7 +95,7 @@ public void testWrongVersion() { 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(), 0.001); + Assertions.assertEquals(9.99, (Double) oe.getParts()[0], 0.001); } } @@ -207,9 +220,11 @@ public void testRinex2Header() { Assertions.assertEquals(0.0, header.getEccentricities().getX(), 1.0e-4); Assertions.assertEquals(0.0, header.getEccentricities().getY(), 1.0e-4); Assertions.assertEquals(30.0, header.getInterval(), 1.0e-15); - Assertions.assertEquals(-1, header.getClkOffset()); + Assertions.assertFalse(header.getClockOffsetApplied()); Assertions.assertEquals(18, header.getLeapSeconds()); - Assertions.assertEquals(0.0, new AbsoluteDate(2017, 1, 11, TimeScalesFactory.getGPS()).durationFrom(header.getTFirstObs()), 1.0e-15); + Assertions.assertEquals(loaded.getObservationDataSets().get(0).getRcvrClkOffset(), + new AbsoluteDate(2017, 1, 11, TimeScalesFactory.getGPS()).durationFrom(header.getTFirstObs()), + 1.0e-15); Assertions.assertTrue(Double.isInfinite(header.getTLastObs().durationFrom(header.getTFirstObs()))); } @@ -247,7 +262,7 @@ public void testRinex3Header() { Assertions.assertNull(header.getCenterMass()); Assertions.assertEquals("DBHZ", header.getSignalStrengthUnit()); Assertions.assertEquals(15.0, header.getInterval(), 1.0e-15); - Assertions.assertEquals(-1, header.getClkOffset()); + Assertions.assertFalse(header.getClockOffsetApplied()); Assertions.assertEquals(0, header.getListAppliedDCBS().size()); Assertions.assertEquals(0, header.getListAppliedPCVS().size()); Assertions.assertEquals(3, header.getPhaseShiftCorrections().size()); @@ -270,6 +285,20 @@ public void testRinex3Header() { } + @Deprecated + @Test + public void testDeprecatedMethods() { + final RinexObservation loaded = load("rinex/aaaa0000.00o"); + final RinexObservationHeader header = loaded.getHeader(); + Assertions.assertFalse(header.getClockOffsetApplied()); + header.setClkOffset(2); + Assertions.assertTrue(header.getClockOffsetApplied()); + Assertions.assertEquals(1, header.getClkOffset()); + header.setClkOffset(0); + Assertions.assertFalse(header.getClockOffsetApplied()); + Assertions.assertEquals(0, header.getClkOffset()); + } + @Test public void testGPSFile() { @@ -279,31 +308,31 @@ public void testGPSFile() { Assertions.assertEquals(44, list.size()); - checkObservation(list.get(0), + checkObservation(list.get(0), false, 2017, 1, 11, 0, 0, 0, TimeScalesFactory.getGPS(), SatelliteSystem.GPS, 2, -0.03, typesobs, ObservationType.L1, 124458652.886, 4, 0); - checkObservation(list.get(0), + checkObservation(list.get(0), false, 2017, 1, 11, 0, 0, 0, TimeScalesFactory.getGPS(), SatelliteSystem.GPS, 2, -0.03, typesobs, ObservationType.P1, Double.NaN, 0, 0); - checkObservation(list.get(3), + checkObservation(list.get(3), false, 2017, 1, 11, 0, 0, 0, TimeScalesFactory.getGPS(), SatelliteSystem.GPS, 6, -0.03, typesobs, ObservationType.S2, 42.300, 4, 0); - checkObservation(list.get(11), + checkObservation(list.get(11), false, 2017, 1, 11, 0, 0, 30, TimeScalesFactory.getGPS(), SatelliteSystem.GPS, 2, -0.08, typesobs, ObservationType.C1, 23688342.361, 4, 0); - checkObservation(list.get(23), + checkObservation(list.get(23), false, 2017, 1, 11, 0, 1, 0, TimeScalesFactory.getGPS(), SatelliteSystem.GPS, 3, 0, typesobs, ObservationType.P2, 25160656.959, 4, 0); - checkObservation(list.get(23), + checkObservation(list.get(23), false, 2017, 1, 11, 0, 1, 0, TimeScalesFactory.getGPS(), SatelliteSystem.GPS, 3, 0, typesobs, ObservationType.P1, Double.NaN, 0, 0); - checkObservation(list.get(43), + checkObservation(list.get(43), false, 2017, 1, 11, 0, 1, 30, TimeScalesFactory.getGPS(), SatelliteSystem.GPS, 30, 0, typesobs, ObservationType.S1, 41.6, 4, 0); @@ -339,31 +368,31 @@ public void testGPSGlonassFile() { Assertions.assertEquals(24, list.size()); - checkObservation(list.get(0), + checkObservation(list.get(0), true, 2001, 3, 24, 13, 10, 36, TimeScalesFactory.getGPS(), SatelliteSystem.GPS, 12, -.123456789, typesobs2, ObservationType.P1, 23629347.915, 0, 0); - checkObservation(list.get(1), + checkObservation(list.get(1), true, 2001, 3, 24, 13, 10, 36, TimeScalesFactory.getGPS(), SatelliteSystem.GPS, 9, -.123456789, typesobs2, ObservationType.L1, -0.12, 0, 9); - checkObservation(list.get(2), + checkObservation(list.get(2), true, 2001, 3, 24, 13, 10, 36, TimeScalesFactory.getGPS(), SatelliteSystem.GPS, 6, -.123456789, typesobs2, ObservationType.P2, 20607605.848, 4, 4); - checkObservation(list.get(3), + checkObservation(list.get(3), true, 2001, 3, 24, 13, 10, 54, TimeScalesFactory.getGPS(), SatelliteSystem.GPS, 12, -.123456789, typesobs2, ObservationType.L2, -41981.375, 0, 0); - checkObservation(list.get(6), + checkObservation(list.get(6), true, 2001, 3, 24, 13, 10, 54, TimeScalesFactory.getGPS(), SatelliteSystem.GLONASS, 21, -.123456789, typesobs2, ObservationType.P1, 21345678.576, 0, 0); - checkObservation(list.get(7), + checkObservation(list.get(7), true, 2001, 3, 24, 13, 10, 54, TimeScalesFactory.getGPS(), SatelliteSystem.GLONASS, 22, -.123456789, typesobs2, ObservationType.P2, Double.NaN, 0, 0); - checkObservation(list.get(23), + checkObservation(list.get(23), true, 2001, 3, 24, 13, 14, 48, TimeScalesFactory.getGPS(), SatelliteSystem.GPS, 6, -.123456234, typesobs2, ObservationType.L1, 267583.678, 1, 7); @@ -380,31 +409,31 @@ public void testMultipleConstellationsFile() { String[] typesobsE = {"C1X","L1X","S1X","C5X","L5X","S5X","C7X","L7X","S7X","C8X","L8X","S8X"}; String[] typesobsC = {"C1I","L1I","S1I","C7I","L7I","S7I","C6I","L6I","S6I"}; Assertions.assertEquals(51, list.size()); - checkObservation(list.get(0), + checkObservation(list.get(0), false, 2016, 1, 11, 0, 0, 0, TimeScalesFactory.getGPS(), SatelliteSystem.GLONASS, 10, 0.0, typesobsR, ObservationType.C1C, 23544632.969, 0, 6); - checkObservation(list.get(1), + checkObservation(list.get(1), false, 2016, 1, 11, 0, 0, 0, TimeScalesFactory.getGPS(), SatelliteSystem.GPS, 27, 0.0, typesobsG, ObservationType.C1C, 22399181.883, 0, 7); - checkObservation(list.get(9), + checkObservation(list.get(9), false, 2016, 1, 11, 0, 0, 0, TimeScalesFactory.getGPS(), SatelliteSystem.GPS, 3, 0.0, typesobsG, ObservationType.S5X, 47.600, 0, 0); - checkObservation(list.get(10), + checkObservation(list.get(10), false, 2016, 1, 11, 0, 0, 0, TimeScalesFactory.getGPS(), SatelliteSystem.GALILEO, 14, 0.0, typesobsE, ObservationType.L8X, 76221970.869, 0, 8); - checkObservation(list.get(25), + checkObservation(list.get(25), false, 2016, 1, 11, 0, 0, 0, TimeScalesFactory.getGPS(), SatelliteSystem.BEIDOU, 12, 0.0, typesobsC, ObservationType.S7I, 31.100, 0, 0); - checkObservation(list.get(25), + checkObservation(list.get(25), false, 2016, 1, 11, 0, 0, 0, TimeScalesFactory.getGPS(), SatelliteSystem.BEIDOU, 12, 0.0, typesobsC, ObservationType.S7I, 31.100, 0, 0); - checkObservation(list.get(50), + checkObservation(list.get(50), false, 2016, 1, 11, 0, 0, 15, TimeScalesFactory.getGPS(), SatelliteSystem.BEIDOU, 11, 0.0, typesobsC, ObservationType.C7I, 23697971.738, 0, 7); @@ -425,43 +454,43 @@ public void testMultipleConstellationsGlonassScaleFactorFile() { String[] typesobsJ2 = {"C1C","L1C","S1C","C2L","L2L","S2L","C5Q","L5Q","S5Q"}; Assertions.assertEquals(36, list.size()); - checkObservation(list.get(0), + checkObservation(list.get(0), false, 2018, 1, 29, 0, 0, 0, TimeScalesFactory.getGPS(), SatelliteSystem.GPS, 30, 0.0, typesobsG2, ObservationType.C1C, 20422534.056, 0, 8); - checkObservation(list.get(2), + checkObservation(list.get(2), false, 2018, 1, 29, 0, 0, 0, TimeScalesFactory.getGPS(), SatelliteSystem.GLONASS, 10, 0.0, typesobsR2, ObservationType.S2C, 49.250, 0, 0); - checkObservation(list.get(2), + checkObservation(list.get(2), false, 2018, 1, 29, 0, 0, 0, TimeScalesFactory.getGPS(), SatelliteSystem.GLONASS, 10, 0.0, typesobsR2, ObservationType.C1C, 19186.904493, 0, 9); - checkObservation(list.get(7), + checkObservation(list.get(7), false, 2018, 1, 29, 0, 0, 0, TimeScalesFactory.getGPS(), SatelliteSystem.GALILEO, 5, 0.0, typesobsE2, ObservationType.L8Q, 103747111.324, 0, 8); - checkObservation(list.get(13), + checkObservation(list.get(13), false, 2018, 1, 29, 0, 0, 0, TimeScalesFactory.getGPS(), SatelliteSystem.BEIDOU, 4, 0.0, typesobsC2, ObservationType.C7I, 41010665.465, 0, 5); - checkObservation(list.get(13), + checkObservation(list.get(13), false, 2018, 1, 29, 0, 0, 0, TimeScalesFactory.getGPS(), SatelliteSystem.BEIDOU, 4, 0.0, typesobsC2, ObservationType.L2I, Double.NaN, 0, 0); - checkObservation(list.get(12), + checkObservation(list.get(12), false, 2018, 1, 29, 0, 0, 0, TimeScalesFactory.getGPS(), SatelliteSystem.SBAS, 138, 0.0, typesobsS2, ObservationType.C1C, 40430827.124, 0, 6); - checkObservation(list.get(12), + checkObservation(list.get(12), false, 2018, 1, 29, 0, 0, 0, TimeScalesFactory.getGPS(), SatelliteSystem.SBAS, 138, 0.0, typesobsS2, ObservationType.S5I, 39.750, 0, 0); - checkObservation(list.get(34), + checkObservation(list.get(34), false, 2018, 1, 29, 0, 0, 0, TimeScalesFactory.getGPS(), SatelliteSystem.QZSS, 193, 0.0, typesobsJ2, ObservationType.L2L, 168639076.823, 0, 6); - checkObservation(list.get(32), + checkObservation(list.get(32), false, 2018, 1, 29, 0, 0, 0, TimeScalesFactory.getGPS(), SatelliteSystem.GLONASS, 2, 0.0, typesobsR2, ObservationType.S1C, 0.0445, 0, 0); @@ -477,27 +506,27 @@ public void testMultipleConstellationsGalileoScaleFactorFile() { String[] typesobsE4 = {"C1C","L1C","S1C","C6C","L6C","S6C","C5Q","L5Q","S5Q","C7Q","L7Q","S7Q","C8Q","L8Q","S8Q"}; Assertions.assertEquals(36, list.size()); - checkObservation(list.get(0), + checkObservation(list.get(0), false, 2018, 1, 29, 0, 0, 0, TimeScalesFactory.getGPS(), SatelliteSystem.GPS, 30, 0.0, typesobsG4, ObservationType.C1C, 20422534.056, 0, 8); - checkObservation(list.get(2), + checkObservation(list.get(2), false, 2018, 1, 29, 0, 0, 0, TimeScalesFactory.getGPS(), SatelliteSystem.GLONASS, 10, 0.0, typesobsR4, ObservationType.S2C, 49.250, 0, 0); - checkObservation(list.get(2), + checkObservation(list.get(2), false, 2018, 1, 29, 0, 0, 0, TimeScalesFactory.getGPS(), SatelliteSystem.GLONASS, 10, 0.0, typesobsR4, ObservationType.C1C, 19186904.493, 0, 9); - checkObservation(list.get(7), + checkObservation(list.get(7), false, 2018, 1, 29, 0, 0, 0, TimeScalesFactory.getGPS(), SatelliteSystem.GALILEO, 5, 0.0, typesobsE4, ObservationType.L8Q, 103747.111324, 0, 8); - checkObservation(list.get(26), + checkObservation(list.get(26), false, 2018, 1, 29, 0, 0, 0, TimeScalesFactory.getGPS(), SatelliteSystem.GALILEO, 8, 0.0, typesobsE4, ObservationType.C1C, 23499.584944, 0, 7); - checkObservation(list.get(26), + checkObservation(list.get(26), false, 2018, 1, 29, 0, 0, 0, TimeScalesFactory.getGPS(), SatelliteSystem.GALILEO, 8, 0.0, typesobsE4, ObservationType.S8Q, 0.051, 0, 0); @@ -839,7 +868,7 @@ public void testUnknownFrequency() { Assertions.fail("an exception should have been thrown"); } catch (OrekitException oe) { Assertions.assertEquals(OrekitMessages.UNKNOWN_RINEX_FREQUENCY, oe.getSpecifier()); - Assertions.assertEquals("AAA", (String) oe.getParts()[0]); + Assertions.assertEquals("AAA", oe.getParts()[0]); Assertions.assertEquals(14, ((Integer) oe.getParts()[2]).intValue()); } } @@ -1095,6 +1124,7 @@ public void testGlonass() { } private void checkObservation(final ObservationDataSet obser, + final boolean clockOffsetApplied, final int year, final int month, final int day, final int hour, final int minute, final double second, final TimeScale timescale, @@ -1108,7 +1138,9 @@ private void checkObservation(final ObservationDataSet obser, Assertions.assertEquals(system, obser.getSatellite().getSystem()); Assertions.assertEquals(prnNumber, obser.getSatellite().getPRN()); - Assertions.assertEquals(date, obser.getDate()); + Assertions.assertEquals(clockOffsetApplied ? 0.0 : rcvrClkOffset, + date.durationFrom(obser.getDate()), + 1.0e-15); Assertions.assertEquals(rcvrClkOffset, obser.getRcvrClkOffset(), 1.E-17); for (int i = 0; i < typesObs.length; i++) { final ObservationData od = obser.getObservationData().get(i); 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 db59a4333e..63a2de1fc5 100644 --- a/src/test/java/org/orekit/files/rinex/observation/RinexObservationWriterTest.java +++ b/src/test/java/org/orekit/files/rinex/observation/RinexObservationWriterTest.java @@ -24,6 +24,7 @@ import java.util.Map; import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.util.FastMath; import org.hipparchus.util.Precision; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -32,6 +33,7 @@ import org.orekit.data.DataSource; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; +import org.orekit.estimation.measurements.QuadraticClockModel; import org.orekit.files.rinex.AppliedDCBS; import org.orekit.files.rinex.AppliedPCVS; import org.orekit.files.rinex.section.RinexComment; @@ -52,6 +54,7 @@ public void testWriteHeaderTwice() throws IOException { final RinexObservation robs = load("rinex/bbbb0000.00o"); 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()); writer.writeHeader(robs.getHeader()); @@ -67,6 +70,7 @@ public void testNoWriteHeader() throws IOException { final RinexObservation robs = load("rinex/aiub0000.00o"); final CharArrayWriter caw = new CharArrayWriter(); try (RinexObservationWriter writer = new RinexObservationWriter(caw, "dummy")) { + writer.setReceiverClockModel(robs.extractClockModel(2)); writer.writeObservationDataSet(robs.getObservationDataSets().get(0)); Assertions.fail("an exception should have been thrown"); } catch (OrekitException oe) { @@ -77,52 +81,52 @@ public void testNoWriteHeader() throws IOException { @Test public void testRoundTripRinex2A() throws IOException { - doTestRoundTrip("rinex/aiub0000.00o"); + doTestRoundTrip("rinex/aiub0000.00o", 0.0); } @Test public void testRoundTripRinex2B() throws IOException { - doTestRoundTrip("rinex/cccc0000.07o"); + doTestRoundTrip("rinex/cccc0000.07o", 0.0); } @Test public void testRoundTripRinex3A() throws IOException { - doTestRoundTrip("rinex/bbbb0000.00o"); + doTestRoundTrip("rinex/bbbb0000.00o", 0.0); } @Test public void testRoundTripRinex3B() throws IOException { - doTestRoundTrip("rinex/dddd0000.01o"); + doTestRoundTrip("rinex/dddd0000.01o", 0.0); } @Test public void testRoundTripDcbs() throws IOException { - doTestRoundTrip("rinex/dcbs.00o"); + doTestRoundTrip("rinex/dcbs.00o", 0.0); } @Test public void testRoundTripPcvs() throws IOException { - doTestRoundTrip("rinex/pcvs.00o"); + doTestRoundTrip("rinex/pcvs.00o", 0.0); } @Test public void testRoundTripScaleFactor() throws IOException { - doTestRoundTrip("rinex/bbbb0000.00o"); + doTestRoundTrip("rinex/bbbb0000.00o", 0.0); } @Test public void testRoundTripObsScaleFactor() throws IOException { - doTestRoundTrip("rinex/ice12720-scaled.07o"); + doTestRoundTrip("rinex/ice12720-scaled.07o", 0.0); } @Test public void testRoundTripLeapSecond() throws IOException { - doTestRoundTrip("rinex/jnu10110.17o"); + doTestRoundTrip("rinex/jnu10110.17o", 0.0); } @Test public void testContinuationPhaseShift() throws IOException { - doTestRoundTrip("rinex/continuation-phase-shift.23o"); + doTestRoundTrip("rinex/continuation-phase-shift.23o", 0.0); } private RinexObservation load(final String name) { @@ -130,12 +134,19 @@ private RinexObservation load(final String name) { return new RinexObservationParser().parse(dataSource); } - private void doTestRoundTrip(final String resourceName) throws IOException { + private void doTestRoundTrip(final String resourceName, double expectedDt) throws IOException { final RinexObservation robs = load(resourceName); final CharArrayWriter caw = new CharArrayWriter(); try (RinexObservationWriter writer = new RinexObservationWriter(caw, "dummy")) { - writer.writeCompleteFile(robs); + writer.setReceiverClockModel(robs.extractClockModel(2)); + RinexObservation patched = load(resourceName); + patched.getHeader().setClockOffsetApplied(robs.getHeader().getClockOffsetApplied()); + if (FastMath.abs(expectedDt) > 1.0e-15) { + writer.setReceiverClockModel(new QuadraticClockModel(robs.getHeader().getTFirstObs(), + expectedDt, 0.0, 0.0)); + } + writer.writeCompleteFile(patched); } // reparse the written file @@ -143,12 +154,13 @@ private void doTestRoundTrip(final String resourceName) throws IOException { final DataSource source = new DataSource("", () -> new ByteArrayInputStream(bytes)); final RinexObservation rebuilt = new RinexObservationParser().parse(source); - checkRinexFile(robs, rebuilt); + checkRinexFile(robs, rebuilt, expectedDt); } - private void checkRinexFile(final RinexObservation first, final RinexObservation second) { - checkRinexHeader(first.getHeader(), second.getHeader()); + private void checkRinexFile(final RinexObservation first, final RinexObservation second, + final double expectedDt) { + checkRinexHeader(first.getHeader(), second.getHeader(), expectedDt); // we may have lost comments in events observations Assertions.assertTrue(first.getComments().size() >= second.getComments().size()); for (int i = 0; i < second.getComments().size(); ++i) { @@ -156,18 +168,19 @@ private void checkRinexFile(final RinexObservation first, final RinexObservation } Assertions.assertEquals(first.getObservationDataSets().size(), second.getObservationDataSets().size()); for (int i = 0; i < first.getObservationDataSets().size(); ++i) { - checkRinexObs(first.getObservationDataSets().get(i), second.getObservationDataSets().get(i)); + checkRinexObs(first.getObservationDataSets().get(i), second.getObservationDataSets().get(i), expectedDt); } } - private void checkRinexHeader(final RinexObservationHeader first, final RinexObservationHeader second) { + private void checkRinexHeader(final RinexObservationHeader first, final RinexObservationHeader second, + final double expectedDt) { Assertions.assertEquals(first.getFormatVersion(), second.getFormatVersion(), 0.001); Assertions.assertEquals(first.getSatelliteSystem(), second.getSatelliteSystem()); Assertions.assertEquals(first.getProgramName(), second.getProgramName()); Assertions.assertEquals(first.getRunByName(), second.getRunByName()); Assertions.assertEquals(first.getCreationDateComponents(), second.getCreationDateComponents()); Assertions.assertEquals(first.getCreationTimeZone(), second.getCreationTimeZone()); - checkDate(first.getCreationDate(), second.getCreationDate()); + checkDate(first.getCreationDate(), second.getCreationDate(), 0.0); Assertions.assertEquals(first.getDoi(), second.getDoi()); Assertions.assertEquals(first.getLicense(), second.getLicense()); Assertions.assertEquals(first.getStationInformation(), second.getStationInformation()); @@ -184,10 +197,10 @@ private void checkRinexHeader(final RinexObservationHeader first, final RinexObs Assertions.assertEquals(first.getAntennaHeight(), second.getAntennaHeight(), 1.0e-12); Assertions.assertEquals(first.getEccentricities().getX(), second.getEccentricities().getX(), 1.0e-12); Assertions.assertEquals(first.getEccentricities().getY(), second.getEccentricities().getY(), 1.0e-12); - Assertions.assertEquals(first.getClkOffset(), second.getClkOffset(), 1.0e-12); + Assertions.assertEquals(first.getClockOffsetApplied(), second.getClockOffsetApplied()); Assertions.assertEquals(first.getInterval(), second.getInterval(), 1.0e-12); - checkDate(first.getTFirstObs(), second.getTFirstObs()); - checkDate(first.getTLastObs(), second.getTLastObs()); + checkDate(first.getTFirstObs(), second.getTFirstObs(), expectedDt); + checkDate(first.getTLastObs(), second.getTLastObs(), expectedDt); Assertions.assertEquals(first.getLeapSeconds(), second.getLeapSeconds()); Assertions.assertEquals(first.getMarkerType(), second.getMarkerType()); checkVector(first.getAntennaReferencePoint(), second.getAntennaReferencePoint()); @@ -243,10 +256,10 @@ private void checkRinexHeader(final RinexObservationHeader first, final RinexObs Assertions.assertEquals(firstT.get(i), secondT.get(i)); } } - Precision.equalsIncludingNaN(first.getC1cCodePhaseBias(), second.getC1cCodePhaseBias(), 1.0e-12); - Precision.equalsIncludingNaN(first.getC1pCodePhaseBias(), second.getC1pCodePhaseBias(), 1.0e-12); - Precision.equalsIncludingNaN(first.getC2cCodePhaseBias(), second.getC2cCodePhaseBias(), 1.0e-12); - Precision.equalsIncludingNaN(first.getC2pCodePhaseBias(), second.getC2pCodePhaseBias(), 1.0e-12); + Assertions.assertTrue(Precision.equalsIncludingNaN(first.getC1cCodePhaseBias(), second.getC1cCodePhaseBias(), 1.0e-12)); + Assertions.assertTrue(Precision.equalsIncludingNaN(first.getC1pCodePhaseBias(), second.getC1pCodePhaseBias(), 1.0e-12)); + Assertions.assertTrue(Precision.equalsIncludingNaN(first.getC2cCodePhaseBias(), second.getC2cCodePhaseBias(), 1.0e-12)); + Assertions.assertTrue(Precision.equalsIncludingNaN(first.getC2pCodePhaseBias(), second.getC2pCodePhaseBias(), 1.0e-12)); } @@ -255,10 +268,11 @@ private void checkRinexComments(final RinexComment first, final RinexComment sec Assertions.assertEquals(first.getText(), second.getText()); } - private void checkRinexObs(final ObservationDataSet first, final ObservationDataSet second) { + private void checkRinexObs(final ObservationDataSet first, final ObservationDataSet second, + final double expectedDt) { Assertions.assertEquals(first.getSatellite().getSystem(), second.getSatellite().getSystem()); Assertions.assertEquals(first.getSatellite().getPRN(), second.getSatellite().getPRN()); - checkDate(first.getDate(), second.getDate()); + checkDate(first.getDate(), second.getDate(), expectedDt); Assertions.assertEquals(first.getEventFlag(), second.getEventFlag()); Assertions.assertEquals(first.getObservationData().size(), second.getObservationData().size()); for (int i = 0; i < first.getObservationData().size(); ++i) { @@ -268,7 +282,7 @@ private void checkRinexObs(final ObservationDataSet first, final ObservationData Assertions.assertEquals(firstO.getLossOfLockIndicator(), secondO.getLossOfLockIndicator()); Assertions.assertEquals(firstO.getSignalStrength(), secondO.getSignalStrength()); } - Precision.equalsIncludingNaN(first.getRcvrClkOffset(), second.getRcvrClkOffset(), 1.0e-12); + Assertions.assertTrue(Precision.equalsIncludingNaN(first.getRcvrClkOffset(), second.getRcvrClkOffset(), 1.0e-12)); } private void checkDCB(final AppliedDCBS first, final AppliedDCBS second) { @@ -308,11 +322,14 @@ private void checkGlonassChannel(final GlonassSatelliteChannel first, final Glon Assertions.assertEquals(first.getK(), second.getK()); } - private void checkDate(final AbsoluteDate first, final AbsoluteDate second) { + private void checkDate(final AbsoluteDate first, final AbsoluteDate second, + final double expectedDt) { if (first == null) { Assertions.assertNull(second); - } else { + } else if (Double.isInfinite(first.durationFrom(AbsoluteDate.ARBITRARY_EPOCH))) { Assertions.assertEquals(first, second); + } else { + Assertions.assertEquals(expectedDt, second.durationFrom(first), 1.0e-6); } } diff --git a/src/test/java/org/orekit/files/sinex/SinexLoaderTest.java b/src/test/java/org/orekit/files/sinex/SinexLoaderTest.java index 0080235aef..6b04feafe7 100644 --- a/src/test/java/org/orekit/files/sinex/SinexLoaderTest.java +++ b/src/test/java/org/orekit/files/sinex/SinexLoaderTest.java @@ -24,15 +24,18 @@ import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; import org.orekit.files.sinex.Station.ReferenceSystem; +import org.orekit.models.earth.displacement.PsdCorrection; import org.orekit.time.AbsoluteDate; import org.orekit.time.DateComponents; import org.orekit.time.TimeComponents; import org.orekit.time.TimeScale; import org.orekit.time.TimeScalesFactory; import org.orekit.utils.Constants; +import org.orekit.utils.TimeSpanMap; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.List; public class SinexLoaderTest { @@ -171,7 +174,7 @@ public void testIssue867() { Assertions.assertEquals(0.0, refStation7237.distance(station7237.getEccentricities(new AbsoluteDate("2021-12-06T17:30:00.000", TimeScalesFactory.getUTC()))), 1.0e-15); Assertions.assertEquals(0.0, refStation7237.distance(station7237.getEccentricities(new AbsoluteDate("2999-12-06T17:30:00.000", TimeScalesFactory.getUTC()))), 1.0e-15); Assertions.assertEquals(0.0, station7237.getEccentricitiesTimeSpanMap().getFirstTransition().getDate().durationFrom(new AbsoluteDate("1988-01-01T00:00:00.000", TimeScalesFactory.getUTC())), 1.0e-15); - Assertions.assertTrue(station7237.getEccentricitiesTimeSpanMap().getLastTransition().getDate() == AbsoluteDate.FUTURE_INFINITY); + Assertions.assertSame(station7237.getEccentricitiesTimeSpanMap().getLastTransition().getDate(), AbsoluteDate.FUTURE_INFINITY); // Verify station 7090 final Station station7090 = loader.getStation("7090"); @@ -205,7 +208,7 @@ public void testIssue867() { Assertions.assertEquals(0.0, refStation7090.distance(station7090.getEccentricities(new AbsoluteDate("2999-07-05T07:50:00.000", TimeScalesFactory.getUTC()))), 1.0e-15); Assertions.assertEquals(0.0, station7090.getEccentricitiesTimeSpanMap().getFirstTransition().getDate().durationFrom(new AbsoluteDate("1979-07-01T00:00:00.000", TimeScalesFactory.getUTC())), 1.0e-15); - Assertions.assertTrue(station7090.getEccentricitiesTimeSpanMap().getLastTransition().getDate() == AbsoluteDate.FUTURE_INFINITY); + Assertions.assertSame(station7090.getEccentricitiesTimeSpanMap().getLastTransition().getDate(), AbsoluteDate.FUTURE_INFINITY); // Verify station 7092 final Station station7092 = loader.getStation("7092"); @@ -305,6 +308,65 @@ public void testCorruptedFile() { } } + @Test + public void testPostSeismicDeformation() { + SinexLoader loader = new SinexLoader("ITRF2020-psd-gnss.snx"); + + // 2010-02-27 06:34:16 https://earthquake.usgs.gov/earthquakes/eventpage/official20100227063411530_30/executive + final AbsoluteDate date2010 = new AbsoluteDate(2010, 2, 27, 6, 34, 16.0, TimeScalesFactory.getUTC()); + final TimeSpanMap> psdAntuco = loader.getStation("ANTC").getPsdTimeSpanMap(); + Assertions.assertEquals(2, psdAntuco.getSpansNumber()); + final List corr2010 = psdAntuco.getFirstNonNullSpan().getData(); + Assertions.assertEquals(5, corr2010.size()); + Assertions.assertEquals(0, corr2010.get(0).getEarthquakeDate().durationFrom(date2010)); + Assertions.assertEquals(PsdCorrection.TimeEvolution.LOG, corr2010.get(0).getEvolution()); + Assertions.assertEquals(PsdCorrection.Axis.EAST, corr2010.get(0).getAxis()); + Assertions.assertEquals(-1.28699198121674e-01, corr2010.get(0).getAmplitude(), 1.0e-14); + Assertions.assertEquals(8.08455225832410e-01, corr2010.get(0).getRelaxationTime() / Constants.JULIAN_YEAR, 1.0e-14); + Assertions.assertEquals(0, corr2010.get(1).getEarthquakeDate().durationFrom(date2010)); + Assertions.assertEquals(PsdCorrection.TimeEvolution.LOG, corr2010.get(1).getEvolution()); + Assertions.assertEquals(PsdCorrection.Axis.EAST, corr2010.get(1).getAxis()); + Assertions.assertEquals(-3.56937459818481e-02, corr2010.get(1).getAmplitude(), 1.0e-14); + Assertions.assertEquals(3.53677247694474e-03, corr2010.get(1).getRelaxationTime() / Constants.JULIAN_YEAR, 1.0e-14); + Assertions.assertEquals(0, corr2010.get(2).getEarthquakeDate().durationFrom(date2010)); + Assertions.assertEquals(PsdCorrection.TimeEvolution.LOG, corr2010.get(2).getEvolution()); + Assertions.assertEquals(PsdCorrection.Axis.NORTH, corr2010.get(2).getAxis()); + Assertions.assertEquals(8.76938710916818e-02, corr2010.get(2).getAmplitude(), 1.0e-14); + Assertions.assertEquals(6.74719810025941e+00, corr2010.get(2).getRelaxationTime() / Constants.JULIAN_YEAR, 1.0e-14); + Assertions.assertEquals(0, corr2010.get(3).getEarthquakeDate().durationFrom(date2010)); + Assertions.assertEquals(PsdCorrection.TimeEvolution.LOG, corr2010.get(3).getEvolution()); + Assertions.assertEquals(PsdCorrection.Axis.NORTH, corr2010.get(3).getAxis()); + Assertions.assertEquals(1.23511869822841e-02, corr2010.get(3).getAmplitude(), 1.0e-14); + Assertions.assertEquals(1.72868196121241e-02, corr2010.get(3).getRelaxationTime() / Constants.JULIAN_YEAR, 1.0e-14); + Assertions.assertEquals(0, corr2010.get(4).getEarthquakeDate().durationFrom(date2010)); + Assertions.assertEquals(PsdCorrection.TimeEvolution.LOG, corr2010.get(4).getEvolution()); + Assertions.assertEquals(PsdCorrection.Axis.UP, corr2010.get(4).getAxis()); + Assertions.assertEquals(5.50435552310340e-02, corr2010.get(4).getAmplitude(), 1.0e-14); + Assertions.assertEquals(4.70992312239571e-01, corr2010.get(4).getRelaxationTime() / Constants.JULIAN_YEAR, 1.0e-14); + + // 2013-08-30T16:25:03 https://earthquake.usgs.gov/earthquakes/eventpage/usb000jdt7/executive + // 2016-03-12T18:06:44 https://earthquake.usgs.gov/earthquakes/eventpage/at00o3xubc/executive + final AbsoluteDate date2013 = new AbsoluteDate(2013, 8, 30, 16, 25, 3.0, TimeScalesFactory.getUTC()); + final AbsoluteDate date2016 = new AbsoluteDate(2016, 3, 12, 18, 6, 44.0, TimeScalesFactory.getUTC()); + final TimeSpanMap> psdAtkaIsland = loader.getStation("AB01").getPsdTimeSpanMap(); + Assertions.assertEquals(3, psdAtkaIsland.getSpansNumber()); + final List corr2013 = psdAtkaIsland.getFirstNonNullSpan().getData(); + Assertions.assertEquals(1, corr2013.size()); + Assertions.assertEquals(0, corr2013.get(0).getEarthquakeDate().durationFrom(date2013)); + Assertions.assertEquals(PsdCorrection.TimeEvolution.EXP, corr2013.get(0).getEvolution()); + Assertions.assertEquals(PsdCorrection.Axis.NORTH, corr2013.get(0).getAxis()); + Assertions.assertEquals(-1.16779196624443e-02, corr2013.get(0).getAmplitude(), 1.0e-14); + Assertions.assertEquals(5.02510982822891e-01, corr2013.get(0).getRelaxationTime() / Constants.JULIAN_YEAR, 1.0e-14); + final List corr2016 = psdAtkaIsland.getFirstNonNullSpan().next().getData(); + Assertions.assertEquals(1, corr2016.size()); + Assertions.assertEquals(0, corr2016.get(0).getEarthquakeDate().durationFrom(date2016)); + Assertions.assertEquals(PsdCorrection.TimeEvolution.EXP, corr2016.get(0).getEvolution()); + Assertions.assertEquals(PsdCorrection.Axis.NORTH, corr2016.get(0).getAxis()); + Assertions.assertEquals(-1.31981162574364e-02, corr2016.get(0).getAmplitude(), 1.0e-14); + Assertions.assertEquals(1.02131561331021e+00, corr2016.get(0).getRelaxationTime() / Constants.JULIAN_YEAR, 1.0e-14); + + } + private void checkStation(final Station station, final int startYear, final int startDay, final double secInStartDay, final int endYear, final int endDay, final double secInEndDay, final int epochYear, final int epochDay, final double secInEpoch, diff --git a/src/test/java/org/orekit/files/sp3/NsgfV00FilterTest.java b/src/test/java/org/orekit/files/sp3/NsgfV00FilterTest.java new file mode 100644 index 0000000000..fb87cb168a --- /dev/null +++ b/src/test/java/org/orekit/files/sp3/NsgfV00FilterTest.java @@ -0,0 +1,67 @@ +/* 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.files.sp3; + +import java.io.IOException; + +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.data.DataSource; +import org.orekit.data.FiltersManager; +import org.orekit.data.GzipFilter; +import org.orekit.frames.Frame; +import org.orekit.frames.FramesFactory; +import org.orekit.utils.Constants; +import org.orekit.utils.IERSConventions; + +public class NsgfV00FilterTest { + + @Test + public void testFiltered() throws IOException { + doTestFilter("/sp3/nsgf.orb.stella.v00.sp3.gz", "L56", 100); + } + + @Test + public void testNotFiltered() throws IOException { + doTestFilter("/sp3/example-c-1.sp3", "G04", 1, 1); + } + + private void doTestFilter(final String name, final String id, final int... nbCoords) throws IOException { + final DataSource original = new DataSource(name, () -> getClass().getResourceAsStream(name)); + final Frame frame = FramesFactory.getITRF(IERSConventions.IERS_2003, true); + final SP3Parser parser = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 2, s -> frame); + + final FiltersManager manager = new FiltersManager(); + manager.addFilter(new NsgfV00Filter()); + manager.addFilter(new GzipFilter()); + + final SP3 file = parser.parse(manager.applyRelevantFilters(original)); + for (int i = 0; i < nbCoords.length; ++i) { + Assertions.assertEquals(nbCoords[i], + file.getEphemeris(id).getSegments().get(i).getCoordinates().size()); + } + + } + + @BeforeEach + public void setUp() { + Utils.setDataRoot("regular-data"); + } + +} diff --git a/src/test/java/org/orekit/files/sp3/SP3ParserTest.java b/src/test/java/org/orekit/files/sp3/SP3ParserTest.java index 16c24cf650..f663624b05 100644 --- a/src/test/java/org/orekit/files/sp3/SP3ParserTest.java +++ b/src/test/java/org/orekit/files/sp3/SP3ParserTest.java @@ -16,8 +16,6 @@ */ package org.orekit.files.sp3; -import java.io.IOException; -import java.net.URISyntaxException; import java.util.Arrays; import java.util.List; @@ -32,24 +30,31 @@ import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitIllegalArgumentException; import org.orekit.errors.OrekitMessages; -import org.orekit.frames.FactoryManagedFrame; import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; -import org.orekit.frames.Predefined; +import org.orekit.frames.ITRFVersion; +import org.orekit.frames.VersionedITRF; +import org.orekit.gnss.IGSUtils; import org.orekit.gnss.TimeSystem; import org.orekit.propagation.BoundedPropagator; +import org.orekit.propagation.SpacecraftState; import org.orekit.time.AbsoluteDate; +import org.orekit.time.AggregatedClockModel; +import org.orekit.time.ClockModel; +import org.orekit.time.ClockOffset; +import org.orekit.time.SampledClockModel; import org.orekit.time.TimeScale; import org.orekit.time.TimeScalesFactory; import org.orekit.utils.CartesianDerivativesFilter; import org.orekit.utils.Constants; import org.orekit.utils.IERSConventions; import org.orekit.utils.PVCoordinates; +import org.orekit.utils.TimeSpanMap; public class SP3ParserTest { @Test - public void testParseSP3a1() throws IOException, URISyntaxException { + public void testParseSP3a1() { // simple test for version sp3-a, only contains position entries final String ex = "/sp3/example-a-1.sp3"; final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex)); @@ -58,8 +63,8 @@ public void testParseSP3a1() throws IOException, URISyntaxException { Assertions.assertEquals('a', file.getHeader().getVersion()); Assertions.assertEquals(SP3OrbitType.FIT, file.getHeader().getOrbitType()); Assertions.assertEquals(TimeSystem.GPS, file.getHeader().getTimeSystem()); - Assertions.assertSame(Predefined.ITRF_CIO_CONV_2010_ACCURATE_EOP, - ((FactoryManagedFrame) file.getSatellites().get("1").getFrame()).getFactoryKey()); + Assertions.assertEquals(ITRFVersion.ITRF_1992, + ((VersionedITRF) file.getSatellites().get("1").getFrame()).getITRFVersion()); Assertions.assertEquals(25, file.getSatelliteCount()); @@ -94,7 +99,51 @@ public void testParseSP3a1() throws IOException, URISyntaxException { } @Test - public void testParseSP3a2() throws IOException { + public void testClockModel() { + // simple test for version sp3-a, only contains position entries + final String ex = "/sp3/gbm18432.sp3.Z"; + final DataSource compressed = new DataSource(ex, () -> getClass().getResourceAsStream(ex)); + final DataSource source = new UnixCompressFilter().filter(compressed); + final SP3Parser parser = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 6, IGSUtils::guessFrame); + final SP3 sp3 = parser.parse(source); + final TimeScale ts = sp3.getHeader().getTimeSystem().getTimeScale(TimeScalesFactory.getTimeScales()); + final AggregatedClockModel clockModel = sp3.getEphemeris("C02").extractClockModel(); + + Assertions.assertEquals(3, clockModel.getModels().getSpansNumber()); + Assertions.assertNull(clockModel.getModels().getFirstSpan().getData()); + Assertions.assertNull(clockModel.getModels().getLastSpan().getData()); + final ClockModel middle = clockModel.getModels().getFirstSpan().next().getData(); + Assertions.assertEquals(0.0, + new AbsoluteDate(2015, 5, 5, 0, 0, 0.0, ts).durationFrom(middle.getValidityStart()), + 1.0e-15); + Assertions.assertEquals(0.0, + new AbsoluteDate(2015, 5, 5, 23, 55, 0.0, ts).durationFrom(middle.getValidityEnd()), + 1.0e-15); + Assertions.assertEquals(0.0, + new AbsoluteDate(2015, 5, 5, 0, 0, 0.0, ts).durationFrom(clockModel.getValidityStart()), + 1.0e-15); + Assertions.assertEquals(0.0, + new AbsoluteDate(2015, 5, 5, 23, 55, 0.0, ts).durationFrom(clockModel.getValidityEnd()), + 1.0e-15); + + // points exactly on files entries + Assertions.assertEquals(-9.16573060e-4, + clockModel.getOffset(new AbsoluteDate(2015, 5, 5, 0, 10, 0.0, ts)).getOffset(), + 1.0e-16); + Assertions.assertEquals(-9.16566535e-4, + clockModel.getOffset(new AbsoluteDate(2015, 5, 5, 0, 15, 0.0, ts)).getOffset(), + 1.0e-16); + + // intermediate point + final ClockOffset co = clockModel.getOffset(new AbsoluteDate(2015, 5, 5, 0, 12, 5.25, ts)); + Assertions.assertEquals(-9.16570332e-04, co.getOffset(), 1.0e-12); + Assertions.assertEquals( 2.17288913e-11, co.getRate(), 1.0e-19); + Assertions.assertEquals(-4.31319472e-16, co.getAcceleration(), 1.0e-24); + + } + + @Test + public void testParseSP3a2() { // simple test for version sp3-a, contains p/v entries final String ex = "/sp3/example-a-2.sp3"; final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex)); @@ -126,7 +175,7 @@ public void testParseSP3a2() throws IOException { } @Test - public void testParseSP3c1() throws IOException { + public void testParseSP3c1() { // simple test for version sp3-c, contains p entries final String ex = "/sp3/example-c-1.sp3"; final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex)); @@ -156,7 +205,7 @@ public void testParseSP3c1() throws IOException { } @Test - public void testParseSP3c2() throws IOException { + public void testParseSP3c2() { // simple test for version sp3-c, contains p/v entries and correlations final String ex = "/sp3/example-c-2.sp3"; final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex)); @@ -176,7 +225,7 @@ public void testParseSP3c2() throws IOException { // 2001 8 8 0 0 0.00000000 Assertions.assertEquals(new AbsoluteDate(2001, 8, 8, 0, 0, 0, - TimeScalesFactory.getGPS()), coord.getDate()); + TimeScalesFactory.getGPS()), coord.getDate()); // PG01 -11044.805800 -10475.672350 21929.418200 189.163300 18 18 18 219 // VG01 20298.880364 -18462.044804 1381.387685 -4.534317 14 14 14 191 @@ -188,7 +237,18 @@ public void testParseSP3c2() throws IOException { } @Test - public void testParseSP3d1() throws IOException { + public void testParseSP3cWithoutAgency() { + // simple test for version sp3-c, contains p/v entries and correlations + final String ex = "/sp3/missing-agency.sp3"; + final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex)); + final SP3 file = new SP3Parser().parse(source); + + Assertions.assertEquals('c', file.getHeader().getVersion()); + Assertions.assertEquals("", file.getHeader().getAgency()); + } + + @Test + public void testParseSP3d1() { // simple test for version sp3-d, contains p entries final String ex = "/sp3/example-d-1.sp3"; final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex)); @@ -224,12 +284,21 @@ public void testParseSP3d1() throws IOException { Assertions.assertEquals(0.00000029942, coord.getClockCorrection(), 1.0e-15); } + @Deprecated + @Test + public void testDeprecated() { + for (String name : Arrays.asList("IGS14", "ITR20", "SLR08", "UNDEF", "WGS84")) { + Assertions.assertSame(SP3Parser.guessFrame(name), IGSUtils.guessFrame(name)); + } + } + @Test - public void testParseSP3d2() throws IOException { + public void testParseSP3d2() { // simple test for version sp3-c, contains p/v entries and correlations - final String ex = "/sp3/example-d-2.sp3"; + final String ex = "/sp3/example-d-2.sp3"; final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex)); - final SP3 file = new SP3Parser().parse(source); + final SP3 file = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 1, IGSUtils::guessFrame). + parse(source); Assertions.assertEquals('d', file.getHeader().getVersion()); Assertions.assertEquals(SP3OrbitType.HLM, file.getHeader().getOrbitType()); @@ -273,10 +342,23 @@ public void testParseSP3d2() throws IOException { Assertions.assertFalse(coords2.get(0).hasOrbitManeuverEvent()); Assertions.assertTrue(coords2.get(0).hasOrbitPrediction()); + final BoundedPropagator propagator = file.getEphemeris("G01").getPropagator(); + final SpacecraftState s = propagator.propagate(coord.getDate()); + final Frame frame = file.getSatellites().get("G01").getFrame(); + Assertions.assertEquals(0.0, + Vector3D.distance(coord.getPosition(), s.getPVCoordinates(frame).getPosition()), + 4.2e-8); + Assertions.assertEquals(0.0, + Vector3D.distance(coord.getVelocity(), s.getPVCoordinates(frame).getVelocity()), + 3.9e-12); + Assertions.assertEquals(coord.getClockCorrection(), + s.getAdditionalState(SP3Utils.CLOCK_ADDITIONAL_STATE)[0], + 1.0e-18); + } @Test - public void testSP3GFZ() throws IOException { + public void testSP3GFZ() { // simple test for version sp3-c, contains more than 85 satellites final String ex = "/sp3/gbm19500_truncated.sp3"; final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex)); @@ -304,13 +386,12 @@ public void testSP3GFZ() throws IOException { } @Test - public void testSP3Propagator() throws Exception { + public void testSP3Propagator() { // setup final String ex = "/sp3/gbm18432.sp3.Z"; final DataSource compressed = new DataSource(ex, () -> getClass().getResourceAsStream(ex)); final DataSource source = new UnixCompressFilter().filter(compressed); - final Frame frame = FramesFactory.getITRF(IERSConventions.IERS_2003, true); - final SP3Parser parser = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 2, s -> frame); + final SP3Parser parser = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 2, IGSUtils::guessFrame); // action final SP3 file = parser.parse(source); @@ -319,24 +400,43 @@ public void testSP3Propagator() throws Exception { TimeScale gps = TimeScalesFactory.getGPS(); Assertions.assertNull(file.getSatellites().get("XYZ")); SP3Ephemeris ephemeris = file.getSatellites().get("C03"); + Frame frame = ephemeris.getFrame(); BoundedPropagator propagator = ephemeris.getPropagator(); Assertions.assertEquals(propagator.getMinDate(), new AbsoluteDate(2015, 5, 5, gps)); Assertions.assertEquals(propagator.getMaxDate(), new AbsoluteDate(2015, 5, 5, 23, 55, 0, gps)); SP3Coordinate expected = ephemeris.getSegments().get(0).getCoordinates().get(0); + SpacecraftState s = propagator.propagate(propagator.getMinDate()); Assertions.assertEquals(0.0, - Vector3D.distance(propagator.getPVCoordinates(propagator.getMinDate(), frame).getPosition(), + Vector3D.distance(s.getPVCoordinates(frame).getPosition(), expected.getPosition()), 3.0e-8); + Assertions.assertEquals(expected.getClockCorrection(), + s.getAdditionalState(SP3Utils.CLOCK_ADDITIONAL_STATE)[0], + 1.0e-15); expected = ephemeris.getSegments().get(0).getCoordinates().get(1); + s = propagator.propagate(expected.getDate()); Assertions.assertEquals(0.0, - Vector3D.distance(propagator.getPVCoordinates(expected.getDate(), frame).getPosition(), + Vector3D.distance(s.getPVCoordinates(frame).getPosition(), expected.getPosition()), 3.0e-8); + Assertions.assertEquals(expected.getClockCorrection(), + s.getAdditionalState(SP3Utils.CLOCK_ADDITIONAL_STATE)[0], + 1.0e-15); expected = ephemeris.getSegments().get(0).getCoordinates().get(ephemeris.getSegments().get(0).getCoordinates().size() - 1); + s = propagator.propagate(propagator.getMaxDate()); Assertions.assertEquals(0.0, - Vector3D.distance(propagator.getPVCoordinates(propagator.getMaxDate(), frame).getPosition(), + Vector3D.distance(s.getPVCoordinates(frame).getPosition(), expected.getPosition()), 3.0e-8); + Assertions.assertEquals(expected.getClockCorrection(), + s.getAdditionalState(SP3Utils.CLOCK_ADDITIONAL_STATE)[0], + 1.0e-15); + SP3Coordinate previous = ephemeris.getSegments().get(0).getCoordinates().get(ephemeris.getSegments().get(0).getCoordinates().size() - 2); + final double deltaClock = expected.getClockCorrection() - previous.getClockCorrection(); + final double deltaT = expected.getDate().durationFrom(previous.getDate()); + Assertions.assertEquals(deltaClock / deltaT, + s.getAdditionalStateDerivative(SP3Utils.CLOCK_ADDITIONAL_STATE)[0], + 1.0e-26); ephemeris = file.getSatellites().get("E19"); propagator = ephemeris.getPropagator(new FrameAlignedProvider(ephemeris.getFrame())); @@ -356,11 +456,11 @@ public void testSP3Propagator() throws Exception { Assertions.assertEquals(0.0, Vector3D.distance(propagator.propagate(propagator.getMaxDate()).getPVCoordinates(frame).getPosition(), expected.getPosition()), - 3.0e-8); + 3.1e-8); } @Test - public void testSP3Compressed() throws IOException { + public void testSP3Compressed() { final String ex = "/sp3/gbm18432.sp3.Z"; final SP3Parser parser = new SP3Parser(); @@ -411,12 +511,11 @@ private void checkPVEntry(final PVCoordinates expected, final PVCoordinates actu } @Test - public void testTruncatedLine() throws IOException { + public void testTruncatedLine() { try { final String ex = "/sp3/truncated-line.sp3"; final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex)); - final Frame frame = FramesFactory.getITRF(IERSConventions.IERS_2003, true); - final SP3Parser parser = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 3, s -> frame); + final SP3Parser parser = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 3, IGSUtils::guessFrame); parser.parse(source); Assertions.fail("an exception should have been thrown"); } catch (OrekitException oe) { @@ -428,12 +527,11 @@ public void testTruncatedLine() throws IOException { } @Test - public void testMissingEOF() throws IOException { + public void testMissingEOF() { final String ex = "/sp3/missing-eof.sp3"; try { final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex)); - final Frame frame = FramesFactory.getITRF(IERSConventions.IERS_2003, true); - final SP3Parser parser = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 3, s -> frame); + final SP3Parser parser = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 3, IGSUtils::guessFrame); parser.parse(source); Assertions.fail("an exception should have been thrown"); } catch (OrekitException oe) { @@ -446,11 +544,10 @@ public void testMissingEOF() throws IOException { } @Test - public void testMissingStandardDeviation() throws IOException { + public void testMissingStandardDeviation() { final String ex = "/sp3/missing-standard-deviation.sp3"; final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex)); - final Frame frame = FramesFactory.getITRF(IERSConventions.IERS_2003, true); - final SP3Parser parser = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 3, s -> frame); + final SP3Parser parser = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 3, IGSUtils::guessFrame); final SP3 sp3 = parser.parse(source); Assertions.assertEquals(32, sp3.getSatelliteCount()); List coordinates06 = sp3.getEphemeris("G06").getSegments().get(0).getCoordinates(); @@ -469,12 +566,11 @@ public void testMissingStandardDeviation() throws IOException { } @Test - public void testWrongLineIdentifier() throws IOException { + public void testWrongLineIdentifier() { try { final String ex = "/sp3/wrong-line-identifier.sp3"; final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex)); - final Frame frame = FramesFactory.getITRF(IERSConventions.IERS_2003, true); - final SP3Parser parser = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 3, s -> frame); + final SP3Parser parser = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 3, IGSUtils::guessFrame); parser.parse(source); Assertions.fail("an exception should have been thrown"); } catch (OrekitException oe) { @@ -486,9 +582,8 @@ public void testWrongLineIdentifier() throws IOException { } @Test - public void testBHN() throws IOException { - final Frame frame = FramesFactory.getITRF(IERSConventions.IERS_2003, true); - final SP3Parser parser = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 3, s -> frame); + public void testBHN() { + final SP3Parser parser = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 3, IGSUtils::guessFrame); final String ex = "/sp3/esaBHN.sp3.Z"; final DataSource compressed = new DataSource(ex, () -> getClass().getResourceAsStream(ex)); final DataSource uncompressed = new UnixCompressFilter().filter(compressed); @@ -498,9 +593,8 @@ public void testBHN() throws IOException { } @Test - public void testPRO() throws IOException { - final Frame frame = FramesFactory.getITRF(IERSConventions.IERS_2003, true); - final SP3Parser parser = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 3, s -> frame); + public void testPRO() { + final SP3Parser parser = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 3, IGSUtils::guessFrame); final String ex = "/sp3/esaPRO.sp3.Z"; final DataSource compressed = new DataSource(ex, () -> getClass().getResourceAsStream(ex)); final DataSource uncompressed = new UnixCompressFilter().filter(compressed); @@ -510,9 +604,8 @@ public void testPRO() throws IOException { } @Test - public void testUnknownType() throws IOException { - final Frame frame = FramesFactory.getITRF(IERSConventions.IERS_2003, true); - final SP3Parser parser = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 3, s -> frame); + public void testUnknownType() { + final SP3Parser parser = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 3, IGSUtils::guessFrame); final String ex = "/sp3/unknownType.sp3.Z"; final DataSource compressed = new DataSource(ex, () -> getClass().getResourceAsStream(ex)); final DataSource uncompressed = new UnixCompressFilter().filter(compressed); @@ -522,12 +615,11 @@ public void testUnknownType() throws IOException { } @Test - public void testUnsupportedVersion() throws IOException { + public void testUnsupportedVersion() { try { final String ex = "/sp3/unsupported-version.sp3"; final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex)); - final Frame frame = FramesFactory.getITRF(IERSConventions.IERS_2003, true); - final SP3Parser parser = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 3, s -> frame); + final SP3Parser parser = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 3, IGSUtils::guessFrame); parser.parse(source); Assertions.fail("an exception should have been thrown"); } catch (OrekitException oe) { @@ -539,12 +631,11 @@ public void testUnsupportedVersion() throws IOException { } @Test - public void testWrongNumberOfEpochs() throws IOException { + public void testWrongNumberOfEpochs() { try { final String ex = "/sp3/wrong-number-of-epochs.sp3"; final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex)); - final Frame frame = FramesFactory.getITRF(IERSConventions.IERS_2003, true); - final SP3Parser parser = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 3, s -> frame); + final SP3Parser parser = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 3, IGSUtils::guessFrame); parser.parse(source); Assertions.fail("an exception should have been thrown"); } catch (OrekitException oe) { @@ -557,12 +648,11 @@ public void testWrongNumberOfEpochs() throws IOException { } @Test - public void testInconsistentSamplingDates() throws IOException { + public void testInconsistentSamplingDates() { try { final String ex = "/sp3/inconsistent-sampling-dates.sp3"; final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex)); - final Frame frame = FramesFactory.getITRF(IERSConventions.IERS_2003, true); - final SP3Parser parser = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 3, s -> frame); + final SP3Parser parser = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 3, IGSUtils::guessFrame); parser.parse(source); Assertions.fail("an exception should have been thrown"); } catch (OrekitException oe) { @@ -1109,12 +1199,62 @@ public void testSpliceWrongMu() { public void testSpliceNewSegment() { SP3 sp3 = splice("/sp3/gbm19500_truncated.sp3", "/sp3/gbm19500_large_gap.sp3"); Assertions.assertEquals(2, sp3.getEphemeris("C01").getSegments().size()); + final TimeScale ts = sp3.getHeader().getTimeSystem().getTimeScale(TimeScalesFactory.getTimeScales()); + + final AggregatedClockModel clockModel = sp3.getEphemeris("C01").extractClockModel(); + Assertions.assertEquals(0.0, + new AbsoluteDate(2017, 5, 21, 0, 0, 0.0, ts).durationFrom(clockModel.getValidityStart()), + 1.0e-15); + Assertions.assertEquals(0.0, + new AbsoluteDate(2017, 5, 21, 0, 25, 0.0, ts).durationFrom(clockModel.getValidityEnd()), + 1.0e-15); + + // there are 5 spans after splicing: + // one null from -∞ to 2017-05-21T00:00:00 + // one regular from 2017-05-21T00:00:00 to 2017-05-21T00:05:00 + // one null from 2017-05-21T00:05:00 to 2017-05-21T00:20:00 + // one regular from 2017-05-21T00:20:00 to 2017-05-21T00:25:00 + // one null from 2017-05-21T00:25:00 to +∞ + Assertions.assertEquals(5, clockModel.getModels().getSpansNumber()); + TimeSpanMap.Span span = clockModel.getModels().getFirstSpan(); + Assertions.assertNull(span.getData()); + span = span.next(); + Assertions.assertInstanceOf(SampledClockModel.class, span.getData()); + Assertions.assertEquals(0.0, + new AbsoluteDate(2017, 5, 21, 0, 0, 0.0, ts).durationFrom(span.getData().getValidityStart()), + 1.0e-15); + Assertions.assertEquals(0.0, + new AbsoluteDate(2017, 5, 21, 0, 5, 0.0, ts).durationFrom(span.getData().getValidityEnd()), + 1.0e-15); + span = span.next(); + Assertions.assertNull(span.getData()); + span = span.next(); + Assertions.assertInstanceOf(SampledClockModel.class, span.getData()); + Assertions.assertEquals(0.0, + new AbsoluteDate(2017, 5, 21, 0, 20, 0.0, ts).durationFrom(span.getData().getValidityStart()), + 1.0e-15); + Assertions.assertEquals(0.0, + new AbsoluteDate(2017, 5, 21, 0, 25, 0.0, ts).durationFrom(span.getData().getValidityEnd()), + 1.0e-15); + span = span.next(); + Assertions.assertNull(span.getData()); + + Assertions.assertEquals(2.4314257e-5, clockModel.getOffset(new AbsoluteDate(2017, 5, 21, 0, 4, 59.5, ts)).getOffset(), 1.0e-12); + Assertions.assertEquals(2.4301550e-5, clockModel.getOffset(new AbsoluteDate(2017, 5, 21, 0, 20, 0.5, ts)).getOffset(), 1.0e-12); + try { + clockModel.getOffset(new AbsoluteDate(2017, 5, 21, 0, 10, 0.0, ts)); + Assertions.fail("an exceptions should have been thrown"); + } catch (OrekitException oe) { + Assertions.assertEquals(OrekitMessages.NO_DATA_GENERATED, oe.getSpecifier()); + } + } @Test public void testSpliceDrop() { final SP3 spliced = splice("/sp3/gbm19500_truncated.sp3", "/sp3/gbm19500_after_drop.sp3"); + final TimeScale ts = spliced.getHeader().getTimeSystem().getTimeScale(TimeScalesFactory.getTimeScales()); Assertions.assertEquals(SP3OrbitType.FIT, spliced.getHeader().getOrbitType()); Assertions.assertEquals(TimeSystem.GPS, spliced.getHeader().getTimeSystem()); @@ -1141,6 +1281,28 @@ public void testSpliceDrop() { Assertions.assertEquals(1.25, spliced.getHeader().getPosVelBase(), 1.0e-15); Assertions.assertEquals(1.025, spliced.getHeader().getClockBase(), 1.0e-15); + // since the gap between the two files is equal to the epoch interval + // the segments are kept merged + final AggregatedClockModel clockModel = spliced.getEphemeris("R23").extractClockModel(); + Assertions.assertEquals(3, clockModel.getModels().getSpansNumber()); + Assertions.assertEquals(0.0, + new AbsoluteDate(2017, 5, 21, 0, 0, 0.0, ts).durationFrom(clockModel.getValidityStart()), + 1.0e-15); + Assertions.assertEquals(0.0, + new AbsoluteDate(2017, 5, 21, 0, 10, 0.0, ts).durationFrom(clockModel.getValidityEnd()), + 1.0e-15); + try { + clockModel.getOffset(clockModel.getValidityStart().shiftedBy(-1)); + Assertions.fail("an exceptions should have been thrown"); + } catch (OrekitException oe) { + Assertions.assertEquals(OrekitMessages.NO_DATA_GENERATED, oe.getSpecifier()); + } + try { + clockModel.getOffset(clockModel.getValidityEnd().shiftedBy(1)); + Assertions.fail("an exceptions should have been thrown"); + } catch (OrekitException oe) { + Assertions.assertEquals(OrekitMessages.NO_DATA_GENERATED, oe.getSpecifier()); + } } @Test @@ -1190,9 +1352,9 @@ public void testSpliceNoDrop() { private SP3 splice(final String name1, final String name2) { final DataSource source1 = new DataSource(name1, () -> getClass().getResourceAsStream(name1)); - final SP3 file1 = new SP3Parser().parse(source1); + final SP3 file1 = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 2, IGSUtils::guessFrame).parse(source1); final DataSource source2 = new DataSource(name2, () -> getClass().getResourceAsStream(name2)); - final SP3 file2 = new SP3Parser().parse(source2); + final SP3 file2 = new SP3Parser(Constants.EIGEN5C_EARTH_MU, 2, IGSUtils::guessFrame).parse(source2); return SP3.splice(Arrays.asList(file1, file2)); } diff --git a/src/test/java/org/orekit/files/sp3/SP3WriterTest.java b/src/test/java/org/orekit/files/sp3/SP3WriterTest.java index 02d2558782..fe126894f4 100644 --- a/src/test/java/org/orekit/files/sp3/SP3WriterTest.java +++ b/src/test/java/org/orekit/files/sp3/SP3WriterTest.java @@ -20,14 +20,22 @@ import java.io.CharArrayWriter; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.util.List; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +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.data.DataSource; import org.orekit.data.UnixCompressFilter; +import org.orekit.frames.Frame; +import org.orekit.frames.FramesFactory; +import org.orekit.frames.ITRFVersion; import org.orekit.time.TimeScalesFactory; +import org.orekit.utils.IERSConventions; +import org.orekit.utils.PVCoordinates; public class SP3WriterTest { @@ -141,6 +149,74 @@ public void testRoundtripIssue1327FullLine() { doTestRoundtrip("/sp3/issue1327-136-sats.sp3"); } + @Test + public void testChangeFrameItrf96PositionOnly() { + doTestChangeFrame("/sp3/gbm18432.sp3.Z", + FramesFactory.getITRF(ITRFVersion.ITRF_1996, + IERSConventions.IERS_1996, + false)); + } + + @Test + public void testChangeFrameItrf05PositionOnly() { + doTestChangeFrame("/sp3/gbm18432.sp3.Z", + FramesFactory.getITRF(ITRFVersion.ITRF_2005, + IERSConventions.IERS_2003, + false)); + } + + @Test + public void testChangeFrameItrf20PositionOnly() { + doTestChangeFrame("/sp3/gbm18432.sp3.Z", + FramesFactory.getITRF(ITRFVersion.ITRF_2020, + IERSConventions.IERS_2010, + false)); + } + + @Test + public void testChangeFrameGcrfPositionOnly() { + doTestChangeFrame("/sp3/gbm18432.sp3.Z", FramesFactory.getGCRF()); + } + + @Test + public void testChangeFrameEme2000PositionOnly() { + doTestChangeFrame("/sp3/gbm18432.sp3.Z", FramesFactory.getEME2000()); + } + + @Test + public void testChangeFrameItrf96PositionVelocity() { + doTestChangeFrame("/sp3/example-a-2.sp3", + FramesFactory.getITRF(ITRFVersion.ITRF_1996, + IERSConventions.IERS_1996, + false)); + } + + @Test + public void testChangeFrameItrf05PositionVelocity() { + doTestChangeFrame("/sp3/example-a-2.sp3", + FramesFactory.getITRF(ITRFVersion.ITRF_2005, + IERSConventions.IERS_2003, + false)); + } + + @Test + public void testChangeFrameItrf20PositionVelocity() { + doTestChangeFrame("/sp3/example-a-2.sp3", + FramesFactory.getITRF(ITRFVersion.ITRF_2020, + IERSConventions.IERS_2010, + false)); + } + + @Test + public void testChangeFrameGcrfPositionVelocity() { + doTestChangeFrame("/sp3/example-a-2.sp3", FramesFactory.getGCRF()); + } + + @Test + public void testChangeFrameEme2000PositionVelocity() { + doTestChangeFrame("/sp3/example-a-2.sp3", FramesFactory.getEME2000()); + } + private void doTestRoundtrip(final String name) { try { DataSource source1 = new DataSource(name, () -> getClass().getResourceAsStream(name)); @@ -166,6 +242,66 @@ private void doTestRoundtrip(final String name) { } } + private void doTestChangeFrame(final String name, final Frame newFrame) { + try { + DataSource source1 = new DataSource(name, () -> getClass().getResourceAsStream(name)); + if (name.endsWith(".Z")) { + source1 = new UnixCompressFilter().filter(source1); + + } + final SP3 original = new SP3Parser().parse(source1); + final Frame originalFrame = original.getEphemeris(0).getFrame(); + + // change frame + final SP3 changed = SP3.changeFrame(original, newFrame); + + // write the parsed file back to a characters array + final CharArrayWriter caw = new CharArrayWriter(); + new SP3Writer(caw, name + "-changed", TimeScalesFactory.getTimeScales()).write(changed); + + // reparse the written file + final byte[] bytes = caw.toString().getBytes(StandardCharsets.UTF_8); + final DataSource source2 = new DataSource(name, () -> new ByteArrayInputStream(bytes)); + final SP3 rebuilt = new SP3Parser().parse(source2); + + Assertions.assertEquals(newFrame.getName(), rebuilt.getEphemeris(0).getFrame().getName()); + Assertions.assertEquals(original.getSatelliteCount(), rebuilt.getSatelliteCount()); + double maxErrorP = 0; + double maxErrorV = 0; + for (int i = 0; i < original.getSatelliteCount(); ++i) { + final List originalSegments = original.getEphemeris(i).getSegments(); + final List rebuiltSegments = rebuilt.getEphemeris(i).getSegments(); + Assertions.assertEquals(originalSegments.size(), rebuiltSegments.size()); + for (int j = 0; j < originalSegments.size(); ++j) { + final List originalCoordinates = originalSegments.get(j).getCoordinates(); + final List rebuiltCoordinates = rebuiltSegments.get(j).getCoordinates(); + Assertions.assertEquals(originalCoordinates.size(), rebuiltCoordinates.size()); + for (int k = 0; k < originalCoordinates.size(); ++k) { + final SP3Coordinate ok = originalCoordinates.get(k); + final SP3Coordinate rk = rebuiltCoordinates.get(k); + final PVCoordinates pv = newFrame. + getTransformTo(originalFrame, ok.getDate()). + transformPVCoordinates(rk); + maxErrorP = FastMath.max(maxErrorP, Vector3D.distance(ok.getPosition(), pv.getPosition())); + maxErrorV = FastMath.max(maxErrorV, + Vector3D.distance(ok.getVelocity(), + ok.getVelocity().getNorm() < 1.0e-15 ? + rk.getVelocity() : + pv.getVelocity())); + } + } + } + + // tolerances are limited to SP3 file format accuracy + // as we have written and parsed again a file + Assertions.assertEquals(0, maxErrorP, 1.0e-3); + Assertions.assertEquals(0, maxErrorV, 1.0e-6); + + } catch (IOException ioe) { + Assertions.fail(ioe.getLocalizedMessage()); + } + } + @BeforeEach public void setUp() { Utils.setDataRoot("regular-data"); diff --git a/src/test/java/org/orekit/files/stk/STKEphemerisFileParserTest.java b/src/test/java/org/orekit/files/stk/STKEphemerisFileParserTest.java index b9e6711284..bb6fc08502 100644 --- a/src/test/java/org/orekit/files/stk/STKEphemerisFileParserTest.java +++ b/src/test/java/org/orekit/files/stk/STKEphemerisFileParserTest.java @@ -1,3 +1,20 @@ +/* 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.files.stk; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/org/orekit/forces/drag/AbstractDragForceModelTest.java b/src/test/java/org/orekit/forces/drag/AbstractDragForceModelTest.java new file mode 100644 index 0000000000..e1d269a3ca --- /dev/null +++ b/src/test/java/org/orekit/forces/drag/AbstractDragForceModelTest.java @@ -0,0 +1,131 @@ +/* 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.drag; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.analysis.differentiation.DSFactory; +import org.hipparchus.analysis.differentiation.DerivativeStructure; +import org.hipparchus.analysis.differentiation.Gradient; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.util.FastMath; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.orekit.frames.Frame; +import org.orekit.frames.FramesFactory; +import org.orekit.models.earth.atmosphere.Atmosphere; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.SpacecraftState; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.ParameterDriver; + +import java.util.Collections; +import java.util.List; + +class AbstractDragForceModelTest { + + @Test + void testGetGradientDensityWrtState() { + // GIVEN + final TestDragForceModel testDragForceModel = new TestDragForceModel(new TestAtmosphereModel()); + final Frame frame = FramesFactory.getGCRF(); + final Vector3D position = new Vector3D(100., 1, 0); + final int freeParameters = 3; + final FieldVector3D fieldPosition = new FieldVector3D<>( + Gradient.variable(freeParameters, 0, position.getX()), + Gradient.variable(freeParameters, 1, position.getY()), + Gradient.variable(freeParameters, 2, position.getZ())); + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + // WHEN + final Gradient actualGradient = testDragForceModel.getGradientDensityWrtState(date, frame, fieldPosition); + // THEN + final Gradient expectedGradient = testDragForceModel.getGradientDensityWrtStateUsingFiniteDifferences(date, + frame, fieldPosition); + Assertions.assertEquals(expectedGradient.getValue(), actualGradient.getValue()); + for (int i = 0; i < freeParameters; i++) { + Assertions.assertEquals(expectedGradient.getPartialDerivative(i), actualGradient.getPartialDerivative(i), 1e-10); + } + } + + @Test + void testGetDSDensityWrtState() { + // GIVEN + final TestDragForceModel testDragForceModel = new TestDragForceModel(new TestAtmosphereModel()); + final Frame frame = FramesFactory.getGCRF(); + final Vector3D position = new Vector3D(100., 1, 0); + final int freeParameters = 3; + final DSFactory factory = new DSFactory(freeParameters, 1); + final FieldVector3D fieldPosition = new FieldVector3D<>( + factory.variable(0, position.getX()), + factory.variable(1, position.getY()), + factory.variable(2, position.getZ())); + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + // WHEN + final DerivativeStructure actualDS = testDragForceModel.getDSDensityWrtState(date, frame, fieldPosition); + // THEN + final DerivativeStructure expectedDS = testDragForceModel.getDSDensityWrtStateUsingFiniteDifferences(date, + frame, fieldPosition); + Assertions.assertEquals(expectedDS.getValue(), actualDS.getValue()); + final double[] actualDerivatives = actualDS.getAllDerivatives(); + final double[] expectedDerivatives = expectedDS.getAllDerivatives(); + for (int i = 1; i < expectedDerivatives.length; i++) { + Assertions.assertEquals(expectedDerivatives[i], actualDerivatives[i], 1e-10); + } + } + + private static class TestAtmosphereModel implements Atmosphere { + + @Override + public Frame getFrame() { + return FramesFactory.getEME2000(); + } + + @Override + public double getDensity(AbsoluteDate date, Vector3D position, Frame frame) { + return FastMath.exp(-position.getNormSq()); + } + + @Override + public > T getDensity(FieldAbsoluteDate date, FieldVector3D position, Frame frame) { + return FastMath.exp(position.getNormSq().negate()); + } + } + + private static class TestDragForceModel extends AbstractDragForceModel { + + protected TestDragForceModel(Atmosphere atmosphere) { + super(atmosphere); + } + + @Override + public Vector3D acceleration(SpacecraftState s, double[] parameters) { + return null; + } + + @Override + public > FieldVector3D acceleration(FieldSpacecraftState s, T[] parameters) { + return null; + } + + @Override + public List getParametersDrivers() { + return Collections.emptyList(); + } + } + +} diff --git a/src/test/java/org/orekit/forces/drag/DragForceTest.java b/src/test/java/org/orekit/forces/drag/DragForceTest.java index ff2a7843c8..dc6001c6fd 100644 --- a/src/test/java/org/orekit/forces/drag/DragForceTest.java +++ b/src/test/java/org/orekit/forces/drag/DragForceTest.java @@ -37,6 +37,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.attitudes.LofOffset; import org.orekit.bodies.BodyShape; @@ -78,7 +79,7 @@ import org.orekit.utils.TimeStampedPVCoordinates; import org.orekit.utils.TimeSpanMap.Span; -public class DragForceTest extends AbstractLegacyForceModelTest { +class DragForceTest extends AbstractLegacyForceModelTest { @Override protected FieldVector3D accelerationDerivatives(final ForceModel forceModel, @@ -88,7 +89,7 @@ protected FieldVector3D accelerationDerivatives(final Force final AbsoluteDate date = state.getDate().toAbsoluteDate(); final FieldVector3D position = state.getPVCoordinates().getPosition(); final FieldVector3D velocity = state.getPVCoordinates().getVelocity(); - java.lang.reflect.Field atmosphereField = DragForce.class.getDeclaredField("atmosphere"); + java.lang.reflect.Field atmosphereField = AbstractDragForceModel.class.getDeclaredField("atmosphere"); atmosphereField.setAccessible(true); Atmosphere atmosphere = (Atmosphere) atmosphereField.get(forceModel); java.lang.reflect.Field spacecraftField = DragForce.class.getDeclaredField("spacecraft"); @@ -163,7 +164,7 @@ protected FieldVector3D accelerationDerivativesGradient(final ForceMod final AbsoluteDate date = state.getDate().toAbsoluteDate(); final FieldVector3D position = state.getPVCoordinates().getPosition(); final FieldVector3D velocity = state.getPVCoordinates().getVelocity(); - java.lang.reflect.Field atmosphereField = DragForce.class.getDeclaredField("atmosphere"); + java.lang.reflect.Field atmosphereField = AbstractDragForceModel.class.getDeclaredField("atmosphere"); atmosphereField.setAccessible(true); Atmosphere atmosphere = (Atmosphere) atmosphereField.get(forceModel); java.lang.reflect.Field spacecraftField = DragForce.class.getDeclaredField("spacecraft"); @@ -227,7 +228,7 @@ protected FieldVector3D accelerationDerivativesGradient(final ForceMod } @Test - public void testParameterDerivativeSphere() { + void testParameterDerivativeSphere() { final Vector3D pos = new Vector3D(6.46885878304673824e+06, -1.88050918456274318e+06, -1.32931592294715829e+04); final Vector3D vel = new Vector3D(2.14718074509906819e+03, 7.38239351251748485e+03, -1.14097953925384523e+01); @@ -238,10 +239,7 @@ public void testParameterDerivativeSphere() { Constants.EIGEN5C_EARTH_MU)); final DragForce forceModel = - new DragForce(new HarrisPriester(CelestialBodyFactory.getSun(), - new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, - Constants.WGS84_EARTH_FLATTENING, - FramesFactory.getITRF(IERSConventions.IERS_2010, true))), + new DragForce(getAtmosphere(), new IsotropicDrag(2.5, 1.2)); Assertions.assertFalse(forceModel.dependsOnPositionOnly()); @@ -251,7 +249,7 @@ public void testParameterDerivativeSphere() { } @Test - public void testParameterDerivativeSphereGradient() { + void testParameterDerivativeSphereGradient() { final Vector3D pos = new Vector3D(6.46885878304673824e+06, -1.88050918456274318e+06, -1.32931592294715829e+04); final Vector3D vel = new Vector3D(2.14718074509906819e+03, 7.38239351251748485e+03, -1.14097953925384523e+01); @@ -262,11 +260,8 @@ public void testParameterDerivativeSphereGradient() { Constants.EIGEN5C_EARTH_MU)); final DragForce forceModel = - new DragForce(new HarrisPriester(CelestialBodyFactory.getSun(), - new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, - Constants.WGS84_EARTH_FLATTENING, - FramesFactory.getITRF(IERSConventions.IERS_2010, true))), - new IsotropicDrag(2.5, 1.2)); + new DragForce(getAtmosphere(), + new IsotropicDrag(2.5, 1.2), true); Assertions.assertFalse(forceModel.dependsOnPositionOnly()); @@ -275,7 +270,7 @@ public void testParameterDerivativeSphereGradient() { } @Test - public void testStateJacobianSphere() { + void testStateJacobianSphere() { // initialization AbsoluteDate date = new AbsoluteDate(new DateComponents(2003, 03, 01), @@ -295,11 +290,8 @@ public void testStateJacobianSphere() { tolerances[0], tolerances[1])); propagator.setOrbitType(integrationType); final DragForce forceModel = - new DragForce(new HarrisPriester(CelestialBodyFactory.getSun(), - new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, - Constants.WGS84_EARTH_FLATTENING, - FramesFactory.getITRF(IERSConventions.IERS_2010, true))), - new IsotropicDrag(2.5, 1.2)); + new DragForce(getAtmosphere(), + new IsotropicDrag(2.5, 1.2), false); propagator.addForceModel(forceModel); SpacecraftState state0 = new SpacecraftState(orbit); @@ -309,7 +301,7 @@ public void testStateJacobianSphere() { } @Test - public void testParametersDerivativesBox() { + void testParametersDerivativesBox() { final Vector3D pos = new Vector3D(6.46885878304673824e+06, -1.88050918456274318e+06, -1.32931592294715829e+04); final Vector3D vel = new Vector3D(2.14718074509906819e+03, 7.38239351251748485e+03, -1.14097953925384523e+01); @@ -320,10 +312,7 @@ public void testParametersDerivativesBox() { Constants.EIGEN5C_EARTH_MU)); final DragForce forceModel = - new DragForce(new HarrisPriester(CelestialBodyFactory.getSun(), - new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, - Constants.WGS84_EARTH_FLATTENING, - FramesFactory.getITRF(IERSConventions.IERS_2010, true))), + new DragForce(getAtmosphere(), new BoxAndSolarArraySpacecraft(1.5, 2.0, 1.8, CelestialBodyFactory.getSun(), 20.0, Vector3D.PLUS_J, 1.2, 0.1, 0.7, 0.2)); @@ -333,7 +322,7 @@ public void testParametersDerivativesBox() { } @Test - public void testParametersDerivativesBoxGradient() { + void testParametersDerivativesBoxGradient() { final Vector3D pos = new Vector3D(6.46885878304673824e+06, -1.88050918456274318e+06, -1.32931592294715829e+04); final Vector3D vel = new Vector3D(2.14718074509906819e+03, 7.38239351251748485e+03, -1.14097953925384523e+01); @@ -344,10 +333,7 @@ public void testParametersDerivativesBoxGradient() { Constants.EIGEN5C_EARTH_MU)); final DragForce forceModel = - new DragForce(new HarrisPriester(CelestialBodyFactory.getSun(), - new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, - Constants.WGS84_EARTH_FLATTENING, - FramesFactory.getITRF(IERSConventions.IERS_2010, true))), + new DragForce(getAtmosphere(), new BoxAndSolarArraySpacecraft(1.5, 2.0, 1.8, CelestialBodyFactory.getSun(), 20.0, Vector3D.PLUS_J, 1.2, 0.1, 0.7, 0.2)); @@ -357,7 +343,7 @@ public void testParametersDerivativesBoxGradient() { } @Test - public void testJacobianBoxVs80Implementation() { + void testJacobianBoxVs80Implementation() { // initialization AbsoluteDate date = new AbsoluteDate(new DateComponents(2003, 03, 01), @@ -370,10 +356,7 @@ public void testJacobianBoxVs80Implementation() { 0, PositionAngleType.MEAN, FramesFactory.getEME2000(), date, Constants.EIGEN5C_EARTH_MU); final DragForce forceModel = - new DragForce(new HarrisPriester(CelestialBodyFactory.getSun(), - new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, - Constants.WGS84_EARTH_FLATTENING, - FramesFactory.getITRF(IERSConventions.IERS_2010, true))), + new DragForce(getAtmosphere(), new BoxAndSolarArraySpacecraft(1.5, 2.0, 1.8, CelestialBodyFactory.getSun(), 20.0, Vector3D.PLUS_J, 1.2, 0.0, 0.7, 0.2)); SpacecraftState state = new SpacecraftState(orbit, @@ -385,7 +368,7 @@ public void testJacobianBoxVs80Implementation() { } @Test - public void testJacobianBoxVs80ImplementationGradient() { + void testJacobianBoxVs80ImplementationGradient() { // initialization AbsoluteDate date = new AbsoluteDate(new DateComponents(2003, 03, 01), @@ -398,10 +381,7 @@ public void testJacobianBoxVs80ImplementationGradient() { 0, PositionAngleType.MEAN, FramesFactory.getEME2000(), date, Constants.EIGEN5C_EARTH_MU); final DragForce forceModel = - new DragForce(new HarrisPriester(CelestialBodyFactory.getSun(), - new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, - Constants.WGS84_EARTH_FLATTENING, - FramesFactory.getITRF(IERSConventions.IERS_2010, true))), + new DragForce(getAtmosphere(), new BoxAndSolarArraySpacecraft(1.5, 2.0, 1.8, CelestialBodyFactory.getSun(), 20.0, Vector3D.PLUS_J, 1.2, 0.0, 0.7, 0.2)); SpacecraftState state = new SpacecraftState(orbit, @@ -413,7 +393,7 @@ public void testJacobianBoxVs80ImplementationGradient() { } @Test - public void testJacobianBoxVsFiniteDifferences() { + void testJacobianBoxVsFiniteDifferences() { // initialization AbsoluteDate date = new AbsoluteDate(new DateComponents(2003, 03, 01), @@ -426,10 +406,7 @@ public void testJacobianBoxVsFiniteDifferences() { 0, PositionAngleType.MEAN, FramesFactory.getEME2000(), date, Constants.EIGEN5C_EARTH_MU); final DragForce forceModel = - new DragForce(new HarrisPriester(CelestialBodyFactory.getSun(), - new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, - Constants.WGS84_EARTH_FLATTENING, - FramesFactory.getITRF(IERSConventions.IERS_2010, true))), + new DragForce(getAtmosphere(), new BoxAndSolarArraySpacecraft(1.5, 2.0, 1.8, CelestialBodyFactory.getSun(), 20.0, Vector3D.PLUS_J, 1.2, 0.0, 0.7, 0.2)); SpacecraftState state = new SpacecraftState(orbit, @@ -439,7 +416,7 @@ public void testJacobianBoxVsFiniteDifferences() { } @Test - public void testJacobianBoxVsFiniteDifferencesGradient() { + void testJacobianBoxVsFiniteDifferencesGradient() { // initialization AbsoluteDate date = new AbsoluteDate(new DateComponents(2003, 03, 01), @@ -452,10 +429,7 @@ public void testJacobianBoxVsFiniteDifferencesGradient() { 0, PositionAngleType.MEAN, FramesFactory.getEME2000(), date, Constants.EIGEN5C_EARTH_MU); final DragForce forceModel = - new DragForce(new HarrisPriester(CelestialBodyFactory.getSun(), - new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, - Constants.WGS84_EARTH_FLATTENING, - FramesFactory.getITRF(IERSConventions.IERS_2010, true))), + new DragForce(getAtmosphere(), new BoxAndSolarArraySpacecraft(1.5, 2.0, 1.8, CelestialBodyFactory.getSun(), 20.0, Vector3D.PLUS_J, 1.2, 0.0, 0.7, 0.2)); SpacecraftState state = new SpacecraftState(orbit, @@ -465,7 +439,7 @@ public void testJacobianBoxVsFiniteDifferencesGradient() { } @Test - public void testGlobalStateJacobianBox() { + void testGlobalStateJacobianBox() { // initialization AbsoluteDate date = new AbsoluteDate(new DateComponents(2003, 03, 01), @@ -485,10 +459,7 @@ public void testGlobalStateJacobianBox() { tolerances[0], tolerances[1])); propagator.setOrbitType(integrationType); final DragForce forceModel = - new DragForce(new HarrisPriester(CelestialBodyFactory.getSun(), - new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, - Constants.WGS84_EARTH_FLATTENING, - FramesFactory.getITRF(IERSConventions.IERS_2010, true))), + new DragForce(getAtmosphere(), new BoxAndSolarArraySpacecraft(1.5, 2.0, 1.8, CelestialBodyFactory.getSun(), 20.0, Vector3D.PLUS_J, 1.2, 0.0, 0.7, 0.2)); propagator.addForceModel(forceModel); @@ -500,7 +471,7 @@ public void testGlobalStateJacobianBox() { } @Test - public void testIssue229() { + void testIssue229() { AbsoluteDate initialDate = new AbsoluteDate(2004, 1, 1, 0, 0, 0., TimeScalesFactory.getUTC()); Frame frame = FramesFactory.getEME2000(); double rpe = 160.e3 + Constants.WGS84_EARTH_EQUATORIAL_RADIUS; @@ -767,12 +738,36 @@ public void RealFieldExpectErrorTest() { Assertions.assertFalse(FastMath.abs(finPVC_DS.toPVCoordinates().getPosition().getZ() - finPVC_R.getPosition().getZ()) < FastMath.abs(finPVC_R.getPosition().getZ()) * 1e-11); } + @Test + void testDependsOnAttitudeRateTrue() { + // GIVEN + final DragSensitive mockedSensitive = Mockito.mock(DragSensitive.class); + Mockito.when(mockedSensitive.dependsOnAttitudeRate()).thenReturn(true); + final DragForce dragForce = new DragForce(Mockito.mock(Atmosphere.class), mockedSensitive); + // WHEN + final boolean value = dragForce.dependsOnAttitudeRate(); + // THEN + Assertions.assertTrue(value); + } + + @Test + void testDependsOnAttitudeRateFalse() { + // GIVEN + final DragSensitive mockedSensitive = Mockito.mock(DragSensitive.class); + Mockito.when(mockedSensitive.dependsOnAttitudeRate()).thenReturn(false); + final DragForce dragForce = new DragForce(Mockito.mock(Atmosphere.class), mockedSensitive); + // WHEN + final boolean value = dragForce.dependsOnAttitudeRate(); + // THEN + Assertions.assertFalse(value); + } + /** Test that the getParameterDrivers method is working as expected * on an IsotropicDrag-based (ie. spherical) DragForce model with * several estimated values. */ @Test - public void testGetParameterDriversSphereForParameterWithSeveralValues() { + void testGetParameterDriversSphereForParameterWithSeveralValues() { // Atmosphere final Atmosphere atmosphere = new HarrisPriester(CelestialBodyFactory.getSun(), @@ -852,7 +847,7 @@ public void testGetParameterDriversSphereForParameterWithSeveralValues() { * to test that the different parameters' derivatives are computed correctly. */ @Test - public void testParameterDerivativeSphereForParameterWithSeveralValues() { + void testParameterDerivativeSphereForParameterWithSeveralValues() { // Low Earth orbit definition (about 360km altitude) final Vector3D pos = new Vector3D(6.46885878304673824e+06, -1.88050918456274318e+06, -1.32931592294715829e+04); @@ -908,6 +903,13 @@ public void testParameterDerivativeSphereForParameterWithSeveralValues() { checkParameterDerivative(state.shiftedBy(dt3 * 1.1), forceModel, "Cd", 1.0e-4, 2.0e-12); } + private Atmosphere getAtmosphere() { + return new HarrisPriester(CelestialBodyFactory.getSun(), + new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, + Constants.WGS84_EARTH_FLATTENING, + FramesFactory.getITRF(IERSConventions.IERS_2010, true))); + } + @BeforeEach public void setUp() { Utils.setDataRoot("regular-data"); diff --git a/src/test/java/org/orekit/forces/drag/TimeSpanDragForceTest.java b/src/test/java/org/orekit/forces/drag/TimeSpanDragForceTest.java index c83b900d1c..97ee1df89f 100644 --- a/src/test/java/org/orekit/forces/drag/TimeSpanDragForceTest.java +++ b/src/test/java/org/orekit/forces/drag/TimeSpanDragForceTest.java @@ -71,7 +71,7 @@ import java.util.List; -public class TimeSpanDragForceTest extends AbstractLegacyForceModelTest { +class TimeSpanDragForceTest extends AbstractLegacyForceModelTest { /** UTC time scale. */ private TimeScale utc; @@ -87,7 +87,7 @@ protected FieldVector3D accelerationDerivatives(final Force final AbsoluteDate date = state.getDate().toAbsoluteDate(); final FieldVector3D position = state.getPVCoordinates().getPosition(); final FieldVector3D velocity = state.getPVCoordinates().getVelocity(); - java.lang.reflect.Field atmosphereField = TimeSpanDragForce.class.getDeclaredField("atmosphere"); + java.lang.reflect.Field atmosphereField = AbstractDragForceModel.class.getDeclaredField("atmosphere"); atmosphereField.setAccessible(true); Atmosphere atmosphere = (Atmosphere) atmosphereField.get(forceModel); java.lang.reflect.Field spacecraftField = DragForce.class.getDeclaredField("spacecraft"); @@ -151,7 +151,7 @@ protected FieldVector3D accelerationDerivativesGradient(final ForceMod final AbsoluteDate date = state.getDate().toAbsoluteDate(); final FieldVector3D position = state.getPVCoordinates().getPosition(); final FieldVector3D velocity = state.getPVCoordinates().getVelocity(); - java.lang.reflect.Field atmosphereField = TimeSpanDragForce.class.getDeclaredField("atmosphere"); + java.lang.reflect.Field atmosphereField = AbstractDragForceModel.class.getDeclaredField("atmosphere"); atmosphereField.setAccessible(true); Atmosphere atmosphere = (Atmosphere) atmosphereField.get(forceModel); java.lang.reflect.Field spacecraftField = DragForce.class.getDeclaredField("spacecraft"); @@ -203,13 +203,10 @@ protected FieldVector3D accelerationDerivativesGradient(final ForceMod * on an IsotropicDrag-based (ie. spherical) TimeSpanDragForce model. */ @Test - public void testGetParameterDriversSphere() { + void testGetParameterDriversSphere() { // Atmosphere - final Atmosphere atmosphere = new HarrisPriester(CelestialBodyFactory.getSun(), - new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, - Constants.WGS84_EARTH_FLATTENING, - FramesFactory.getITRF(IERSConventions.IERS_2010, true))); + final Atmosphere atmosphere = getAtmosphere(); // A date AbsoluteDate date = new AbsoluteDate("2000-01-01T00:00:00.000", TimeScalesFactory.getUTC()); @@ -318,7 +315,7 @@ public void testGetParameterDriversSphere() { * to test that the different parameters' derivatives are computed correctly. */ @Test - public void testParameterDerivativeSphere() { + void testParameterDerivativeSphere() { // Low Earth orbit definition (about 360km altitude) final Vector3D pos = new Vector3D(6.46885878304673824e+06, -1.88050918456274318e+06, -1.32931592294715829e+04); @@ -331,10 +328,7 @@ public void testParameterDerivativeSphere() { Constants.EIGEN5C_EARTH_MU)); // Atmosphere - final Atmosphere atmosphere = new HarrisPriester(CelestialBodyFactory.getSun(), - new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, - Constants.WGS84_EARTH_FLATTENING, - FramesFactory.getITRF(IERSConventions.IERS_2010, true))); + final Atmosphere atmosphere = getAtmosphere(); // Constant area for the different tests final double dragArea = 2.5; @@ -387,7 +381,7 @@ public void testParameterDerivativeSphere() { * to test that the different parameters' derivatives are computed correctly. */ @Test - public void testParameterDerivativeSphereGradient() { + void testParameterDerivativeSphereGradient() { // Low Earth orbit definition (about 360km altitude) final Vector3D pos = new Vector3D(6.46885878304673824e+06, -1.88050918456274318e+06, -1.32931592294715829e+04); @@ -400,10 +394,7 @@ public void testParameterDerivativeSphereGradient() { Constants.EIGEN5C_EARTH_MU)); // Atmosphere - final Atmosphere atmosphere = new HarrisPriester(CelestialBodyFactory.getSun(), - new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, - Constants.WGS84_EARTH_FLATTENING, - FramesFactory.getITRF(IERSConventions.IERS_2010, true))); + final Atmosphere atmosphere = getAtmosphere(); // Constant area for the different tests final double dragArea = 2.5; @@ -452,7 +443,7 @@ public void testParameterDerivativeSphereGradient() { /** Test state Jacobian computation. */ @Test - public void testStateJacobianSphere() + void testStateJacobianSphere() { // Initialization @@ -471,10 +462,7 @@ public void testStateJacobianSphere() // Atmosphere - final Atmosphere atmosphere = new HarrisPriester(CelestialBodyFactory.getSun(), - new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, - Constants.WGS84_EARTH_FLATTENING, - FramesFactory.getITRF(IERSConventions.IERS_2010, true))); + final Atmosphere atmosphere = getAtmosphere(); // Time span drag force model init double dragArea = 2.; @@ -528,14 +516,11 @@ public void testStateJacobianSphere() * Here only the drag coefficient is modeled. */ @Test - public void testGetParameterDriversBoxOnlyDrag() { + void testGetParameterDriversBoxOnlyDrag() { // Atmosphere final CelestialBody sun = CelestialBodyFactory.getSun(); - final Atmosphere atmosphere = new HarrisPriester(sun, - new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, - Constants.WGS84_EARTH_FLATTENING, - FramesFactory.getITRF(IERSConventions.IERS_2010, true))); + final Atmosphere atmosphere = getAtmosphere(); // A date AbsoluteDate date = new AbsoluteDate("2000-01-01T00:00:00.000", TimeScalesFactory.getUTC()); @@ -627,14 +612,11 @@ public void testGetParameterDriversBoxOnlyDrag() { * Here both drag and lift coefficients are modeled. */ @Test - public void testGetParameterDriversBox() { + void testGetParameterDriversBox() { // Atmosphere final CelestialBody sun = CelestialBodyFactory.getSun(); - final Atmosphere atmosphere = new HarrisPriester(sun, - new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, - Constants.WGS84_EARTH_FLATTENING, - FramesFactory.getITRF(IERSConventions.IERS_2010, true))); + final Atmosphere atmosphere = getAtmosphere(); // A date AbsoluteDate date = new AbsoluteDate("2000-01-01T00:00:00.000", TimeScalesFactory.getUTC()); @@ -732,7 +714,7 @@ public void testGetParameterDriversBox() { * to test that the different parameters' derivatives are computed correctly. */ @Test - public void testParametersDerivativesBox() { + void testParametersDerivativesBox() { // Low Earth orbit definition (about 360km altitude) final Vector3D pos = new Vector3D(6.46885878304673824e+06, -1.88050918456274318e+06, -1.32931592294715829e+04); @@ -746,10 +728,7 @@ public void testParametersDerivativesBox() { // Atmosphere final CelestialBody sun = CelestialBodyFactory.getSun(); - final Atmosphere atmosphere = new HarrisPriester(sun, - new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, - Constants.WGS84_EARTH_FLATTENING, - FramesFactory.getITRF(IERSConventions.IERS_2010, true))); + final Atmosphere atmosphere = getAtmosphere(); // Initialize force model (first coef is valid at all epochs) final double dragCd = 1.; @@ -807,7 +786,7 @@ public void testParametersDerivativesBox() { * to test that the different parameters' derivatives are computed correctly. */ @Test - public void testParametersDerivativesBoxGradient() { + void testParametersDerivativesBoxGradient() { // Low Earth orbit definition (about 360km altitude) final Vector3D pos = new Vector3D(6.46885878304673824e+06, -1.88050918456274318e+06, -1.32931592294715829e+04); @@ -821,10 +800,7 @@ public void testParametersDerivativesBoxGradient() { // Atmosphere final CelestialBody sun = CelestialBodyFactory.getSun(); - final Atmosphere atmosphere = new HarrisPriester(sun, - new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, - Constants.WGS84_EARTH_FLATTENING, - FramesFactory.getITRF(IERSConventions.IERS_2010, true))); + final Atmosphere atmosphere = getAtmosphere(); // Initialize force model (first coef is valid at all epochs) final double dragCd = 1.; @@ -880,7 +856,7 @@ public void testParametersDerivativesBoxGradient() { * and method {@link #accelerationDerivatives} */ @Test - public void testJacobianBoxVs80Implementation() + void testJacobianBoxVs80Implementation() { // initialization @@ -896,11 +872,7 @@ public void testJacobianBoxVs80Implementation() CelestialBody sun = CelestialBodyFactory.getSun(); // Atmosphere - final Atmosphere atmosphere = - new HarrisPriester(sun, - new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, - Constants.WGS84_EARTH_FLATTENING, - FramesFactory.getITRF(IERSConventions.IERS_2010, true))); + final Atmosphere atmosphere = getAtmosphere(); // Time span drag force model initialization double dragCd0 = 1.; double dragCl0 = 0.1; @@ -951,7 +923,7 @@ public void testJacobianBoxVs80Implementation() * and method {@link #accelerationDerivatives} */ @Test - public void testJacobianBoxVs80ImplementationGradient() + void testJacobianBoxVs80ImplementationGradient() { // initialization @@ -968,11 +940,7 @@ public void testJacobianBoxVs80ImplementationGradient() AttitudeProvider defaultLaw = Utils.defaultLaw(); // Atmosphere - final Atmosphere atmosphere = - new HarrisPriester(sun, - new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, - Constants.WGS84_EARTH_FLATTENING, - FramesFactory.getITRF(IERSConventions.IERS_2010, true))); + final Atmosphere atmosphere = getAtmosphere(); // Time span drag force model initialization double dragCd0 = 1.; double dragCl0 = 0.1; @@ -1023,7 +991,7 @@ public void testJacobianBoxVs80ImplementationGradient() * This time the differentiation is made using built-in numerical differentiation method. */ @Test - public void testJacobianBoxVsFiniteDifferences() + void testJacobianBoxVsFiniteDifferences() { // initialization @@ -1040,11 +1008,7 @@ public void testJacobianBoxVsFiniteDifferences() AttitudeProvider defaultLaw = Utils.defaultLaw(); // Atmosphere - final Atmosphere atmosphere = - new HarrisPriester(sun, - new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, - Constants.WGS84_EARTH_FLATTENING, - FramesFactory.getITRF(IERSConventions.IERS_2010, true))); + final Atmosphere atmosphere = getAtmosphere(); // Time span drag force model initialization double dragCd0 = 1.; @@ -1089,7 +1053,7 @@ public void testJacobianBoxVsFiniteDifferences() * This time the differentiation is made using built-in numerical differentiation method. */ @Test - public void testJacobianBoxVsFiniteDifferencesGradient() + void testJacobianBoxVsFiniteDifferencesGradient() { // initialization @@ -1106,11 +1070,7 @@ public void testJacobianBoxVsFiniteDifferencesGradient() AttitudeProvider defaultLaw = Utils.defaultLaw(); // Atmosphere - final Atmosphere atmosphere = - new HarrisPriester(sun, - new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, - Constants.WGS84_EARTH_FLATTENING, - FramesFactory.getITRF(IERSConventions.IERS_2010, true))); + final Atmosphere atmosphere = getAtmosphere(); // Time span drag force model initialization double dragCd0 = 1.; @@ -1134,26 +1094,27 @@ public void testJacobianBoxVsFiniteDifferencesGradient() // Check state derivatives inside first box model Orbit orbit = refOrbit.shiftedBy(0.); + final double dP = 1.; SpacecraftState state = new SpacecraftState(orbit, defaultLaw.getAttitude(orbit, orbit.getDate(), orbit.getFrame())); - checkStateJacobianVsFiniteDifferencesGradient(state, forceModel, defaultLaw, 1.0, 5.0e-6, false); + checkStateJacobianVsFiniteDifferencesGradient(state, forceModel, defaultLaw, dP, 5.0e-6, false); // Check state derivatives inside 2nd box model orbit = refOrbit.shiftedBy(1.1 * dt); state = new SpacecraftState(orbit, defaultLaw.getAttitude(orbit, orbit.getDate(), orbit.getFrame())); - checkStateJacobianVsFiniteDifferencesGradient(state, forceModel, defaultLaw, 1.0, 5.0e-6, false); + checkStateJacobianVsFiniteDifferencesGradient(state, forceModel, defaultLaw, dP, 5.0e-6, false); // Check state derivatives inside 3rd box model orbit = refOrbit.shiftedBy(-1.1 * dt); state = new SpacecraftState(orbit, defaultLaw.getAttitude(orbit, orbit.getDate(), orbit.getFrame())); - checkStateJacobianVsFiniteDifferencesGradient(state, forceModel, defaultLaw, 1.0, 6.0e-6, false); + checkStateJacobianVsFiniteDifferencesGradient(state, forceModel, defaultLaw, dP, 6.0e-6, false); } /** Test state Jacobian computation. */ @Test - public void testGlobalStateJacobianBox() + void testGlobalStateJacobianBox() { // Initialization @@ -1171,10 +1132,7 @@ public void testGlobalStateJacobianBox() CelestialBody sun = CelestialBodyFactory.getSun(); // Atmosphere - final Atmosphere atmosphere = new HarrisPriester(sun, - new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, - Constants.WGS84_EARTH_FLATTENING, - FramesFactory.getITRF(IERSConventions.IERS_2010, true))); + final Atmosphere atmosphere = getAtmosphere(); // Time span drag force model init double dragCd0 = 1.; double dragCl0 = 0.1; @@ -1285,10 +1243,7 @@ public void RealFieldTest() { CelestialBody sun = CelestialBodyFactory.getSun(); // Atmosphere - final Atmosphere atmosphere = new HarrisPriester(sun, - new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, - Constants.WGS84_EARTH_FLATTENING, - FramesFactory.getITRF(IERSConventions.IERS_2010, true))); + final Atmosphere atmosphere = getAtmosphere(); // Time span drag force model init double dragCd0 = 1.; double dragCl0 = 0.1; @@ -1381,9 +1336,11 @@ public void RealFieldGradientTest() { FieldNumericalPropagator FNP = new FieldNumericalPropagator<>(field, integrator); FNP.setOrbitType(type); FNP.setInitialState(initialState); + FNP.setPositionAngleType(PositionAngleType.TRUE); NumericalPropagator NP = new NumericalPropagator(RIntegrator); - NP.setOrbitType(type); + NP.setOrbitType(FNP.getOrbitType()); + NP.setPositionAngleType(FNP.getPositionAngleType()); NP.setInitialState(iSR); @@ -1391,10 +1348,7 @@ public void RealFieldGradientTest() { CelestialBody sun = CelestialBodyFactory.getSun(); // Atmosphere - final Atmosphere atmosphere = new HarrisPriester(sun, - new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, - Constants.WGS84_EARTH_FLATTENING, - FramesFactory.getITRF(IERSConventions.IERS_2010, true))); + final Atmosphere atmosphere = getAtmosphere(); // Time span drag force model init double dragCd0 = 1.; double dragCl0 = 0.1; @@ -1497,10 +1451,7 @@ public void RealFieldExpectErrorTest() { CelestialBody sun = CelestialBodyFactory.getSun(); // Atmosphere - final Atmosphere atmosphere = new HarrisPriester(sun, - new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, - Constants.WGS84_EARTH_FLATTENING, - FramesFactory.getITRF(IERSConventions.IERS_2010, true))); + final Atmosphere atmosphere = getAtmosphere(); // Time span drag force model init double dragCd0 = 1.; double dragCl0 = 0.1; @@ -1534,6 +1485,13 @@ public void RealFieldExpectErrorTest() { Assertions.assertFalse(FastMath.abs(finPVC_DS.toPVCoordinates().getPosition().getZ() - finPVC_R.getPosition().getZ()) < FastMath.abs(finPVC_R.getPosition().getZ()) * 1e-11); } + private Atmosphere getAtmosphere() { + return new HarrisPriester(CelestialBodyFactory.getSun(), + new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, + Constants.WGS84_EARTH_FLATTENING, + FramesFactory.getITRF(IERSConventions.IERS_2010, true))); + } + @BeforeEach public void setUp() { Utils.setDataRoot("regular-data"); diff --git a/src/test/java/org/orekit/forces/empirical/HarmonicAccelerationModelTest.java b/src/test/java/org/orekit/forces/empirical/HarmonicAccelerationModelTest.java index b0d2a43a2e..b3d759cf40 100644 --- a/src/test/java/org/orekit/forces/empirical/HarmonicAccelerationModelTest.java +++ b/src/test/java/org/orekit/forces/empirical/HarmonicAccelerationModelTest.java @@ -32,7 +32,11 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.orekit.Utils; -import org.orekit.attitudes.*; +import org.orekit.attitudes.Attitude; +import org.orekit.attitudes.AttitudeProvider; +import org.orekit.attitudes.CelestialBodyPointed; +import org.orekit.attitudes.FrameAlignedProvider; +import org.orekit.attitudes.LofOffset; import org.orekit.bodies.CelestialBodyFactory; import org.orekit.errors.OrekitException; import org.orekit.estimation.leastsquares.BatchLSEstimator; @@ -44,11 +48,7 @@ import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; import org.orekit.frames.LOFType; -import org.orekit.orbits.CartesianOrbit; -import org.orekit.orbits.CircularOrbit; -import org.orekit.orbits.KeplerianOrbit; -import org.orekit.orbits.Orbit; -import org.orekit.orbits.PositionAngleType; +import org.orekit.orbits.*; import org.orekit.propagation.FieldBoundedPropagator; import org.orekit.propagation.FieldEphemerisGenerator; import org.orekit.propagation.FieldSpacecraftState; @@ -144,8 +144,7 @@ private void doTestEquivalentManeuver(final double mass, final ConstantThrustManeuver maneuver, final AttitudeProvider accelerationLaw, final ParametricAcceleration parametricAcceleration, - final double positionTolerance) - { + final double positionTolerance) { SpacecraftState initialState = new SpacecraftState(initialOrbit, maneuverLaw.getAttitude(initialOrbit, @@ -161,6 +160,8 @@ private void doTestEquivalentManeuver(final double mass, new DormandPrince853Integrator(0.001, 100, tolerance[0], tolerance[1]); integrator0.setInitialStepSize(60); final NumericalPropagator propagator0 = new NumericalPropagator(integrator0); + propagator0.setOrbitType(OrbitType.EQUINOCTIAL); + propagator0.setPositionAngleType(PositionAngleType.TRUE); propagator0.setInitialState(initialState); propagator0.setAttitudeProvider(maneuverLaw); propagator0.addForceModel(maneuver); @@ -170,6 +171,8 @@ private void doTestEquivalentManeuver(final double mass, new DormandPrince853Integrator(0.001, 100, tolerance[0], tolerance[1]); integrator1.setInitialStepSize(60); final NumericalPropagator propagator1 = new NumericalPropagator(integrator1); + propagator1.setOrbitType(propagator0.getOrbitType()); + propagator1.setPositionAngleType(propagator0.getPositionAngleType()); propagator1.setInitialState(initialState); propagator1.setAttitudeProvider(accelerationLaw); propagator1.addForceModel(parametricAcceleration); @@ -276,6 +279,8 @@ private > void doTestEquivalentManeuver(final new DormandPrince853FieldIntegrator<>(field, 0.001, 100, tolerance[0], tolerance[1]); integrator0.setInitialStepSize(60); final FieldNumericalPropagator propagator0 = new FieldNumericalPropagator<>(field, integrator0); + propagator0.setOrbitType(OrbitType.EQUINOCTIAL); + propagator0.setPositionAngleType(PositionAngleType.TRUE); propagator0.setInitialState(initialState); propagator0.setAttitudeProvider(maneuverLaw); propagator0.addForceModel(maneuver); @@ -288,6 +293,8 @@ private > void doTestEquivalentManeuver(final new DormandPrince853FieldIntegrator<>(field, 0.001, 100, tolerance[0], tolerance[1]); integrator1.setInitialStepSize(60); final FieldNumericalPropagator propagator1 = new FieldNumericalPropagator<>(field, integrator1); + propagator1.setOrbitType(propagator0.getOrbitType()); + propagator1.setPositionAngleType(propagator0.getPositionAngleType()); propagator1.setInitialState(initialState); propagator1.setAttitudeProvider(accelerationLaw); propagator1.addForceModel(parametricAcceleration); @@ -374,6 +381,8 @@ public void testCoefficientsDetermination() { new DormandPrince853Integrator(minStep, maxStep, tolerance[0], tolerance[1]); integrator0.setInitialStepSize(60); final NumericalPropagator propagator0 = new NumericalPropagator(integrator0); + propagator0.setOrbitType(OrbitType.EQUINOCTIAL); + propagator0.setPositionAngleType(PositionAngleType.TRUE); propagator0.setInitialState(initialState); propagator0.setAttitudeProvider(maneuverLaw); final ParametricAcceleration hpaRefX1 = new ParametricAcceleration(Vector3D.PLUS_I, true, @@ -480,7 +489,7 @@ public void setUp() { final double OMEGA = FastMath.toRadians(261); final double lv = 0; - final AbsoluteDate initDate = new AbsoluteDate(new DateComponents(2004, 01, 01), + final AbsoluteDate initDate = new AbsoluteDate(new DateComponents(2004, 1, 1), new TimeComponents(23, 30, 00.000), TimeScalesFactory.getUTC()); initialOrbit = diff --git a/src/test/java/org/orekit/forces/empirical/PolynomialAccelerationModelTest.java b/src/test/java/org/orekit/forces/empirical/PolynomialAccelerationModelTest.java index 91dad542a5..0bd617571c 100644 --- a/src/test/java/org/orekit/forces/empirical/PolynomialAccelerationModelTest.java +++ b/src/test/java/org/orekit/forces/empirical/PolynomialAccelerationModelTest.java @@ -41,10 +41,7 @@ import org.orekit.forces.maneuvers.ConstantThrustManeuver; import org.orekit.frames.FramesFactory; import org.orekit.frames.LOFType; -import org.orekit.orbits.CartesianOrbit; -import org.orekit.orbits.KeplerianOrbit; -import org.orekit.orbits.Orbit; -import org.orekit.orbits.PositionAngleType; +import org.orekit.orbits.*; import org.orekit.propagation.FieldBoundedPropagator; import org.orekit.propagation.FieldEphemerisGenerator; import org.orekit.propagation.FieldSpacecraftState; @@ -146,6 +143,8 @@ private void doTestEquivalentManeuver(final double mass, new DormandPrince853Integrator(0.001, 100, tolerance[0], tolerance[1]); integrator0.setInitialStepSize(60); final NumericalPropagator propagator0 = new NumericalPropagator(integrator0); + propagator0.setOrbitType(OrbitType.EQUINOCTIAL); + propagator0.setPositionAngleType(PositionAngleType.TRUE); propagator0.setInitialState(initialState); propagator0.setAttitudeProvider(maneuverLaw); propagator0.addForceModel(maneuver); @@ -155,6 +154,8 @@ private void doTestEquivalentManeuver(final double mass, new DormandPrince853Integrator(0.001, 100, tolerance[0], tolerance[1]); integrator1.setInitialStepSize(60); final NumericalPropagator propagator1 = new NumericalPropagator(integrator1); + propagator1.setOrbitType(propagator0.getOrbitType()); + propagator1.setPositionAngleType(propagator0.getPositionAngleType()); propagator1.setInitialState(initialState); propagator1.setAttitudeProvider(accelerationLaw); propagator1.addForceModel(parametricAcceleration); @@ -255,6 +256,8 @@ private > void doTestEquivalentManeuver(final new DormandPrince853FieldIntegrator<>(field, 0.001, 100, tolerance[0], tolerance[1]); integrator0.setInitialStepSize(60); final FieldNumericalPropagator propagator0 = new FieldNumericalPropagator<>(field, integrator0); + propagator0.setOrbitType(OrbitType.EQUINOCTIAL); + propagator0.setPositionAngleType(PositionAngleType.TRUE); propagator0.setInitialState(initialState); propagator0.setAttitudeProvider(maneuverLaw); propagator0.addForceModel(maneuver); @@ -267,6 +270,8 @@ private > void doTestEquivalentManeuver(final new DormandPrince853FieldIntegrator<>(field, 0.001, 100, tolerance[0], tolerance[1]); integrator1.setInitialStepSize(60); final FieldNumericalPropagator propagator1 = new FieldNumericalPropagator<>(field, integrator1); + propagator1.setOrbitType(propagator0.getOrbitType()); + propagator1.setPositionAngleType(propagator0.getPositionAngleType()); propagator1.setInitialState(initialState); propagator1.setAttitudeProvider(accelerationLaw); propagator1.addForceModel(parametricAcceleration); diff --git a/src/test/java/org/orekit/forces/empirical/TimeSpanParametricAccelerationTest.java b/src/test/java/org/orekit/forces/empirical/TimeSpanParametricAccelerationTest.java index 17ad260de2..04e0a97ce6 100644 --- a/src/test/java/org/orekit/forces/empirical/TimeSpanParametricAccelerationTest.java +++ b/src/test/java/org/orekit/forces/empirical/TimeSpanParametricAccelerationTest.java @@ -284,7 +284,7 @@ public void testStateJacobianAttitudeOverride() { // Set target date to 0.5*dt to be inside 1st AccelerationModel // The further away we are from the initial date, the greater the checkTolerance parameter must be set checkStateJacobian(propagator, state0, date.shiftedBy(0.5 * dt), - 1e3, tolerances[0], 1.7e-9); + 1e3, tolerances[0], 1.8e-9); // Check state derivatives inside 2nd AccelerationModel propagator = new NumericalPropagator(new DormandPrince853Integrator(1.0e-3, 120, diff --git a/src/test/java/org/orekit/forces/gravity/AbstractBodyAttractionTest.java b/src/test/java/org/orekit/forces/gravity/AbstractBodyAttractionTest.java new file mode 100644 index 0000000000..7500fd76b5 --- /dev/null +++ b/src/test/java/org/orekit/forces/gravity/AbstractBodyAttractionTest.java @@ -0,0 +1,72 @@ +/* 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.gravity; + +import org.hipparchus.CalculusFieldElement; +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.bodies.CelestialBody; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.SpacecraftState; + +class AbstractBodyAttractionTest { + + @Test + void testDependsOnPositionOnly() { + // GIVEN + final AbstractBodyAttraction mockedAttraction = Mockito.mock(AbstractBodyAttraction.class); + Mockito.when(mockedAttraction.dependsOnPositionOnly()).thenCallRealMethod(); + // WHEN + final boolean actualDependsOnPositionOnly = mockedAttraction.dependsOnPositionOnly(); + // THEN + Assertions.assertTrue(actualDependsOnPositionOnly); + } + + @Test + void TestGetBodyName() { + // GIVEN + final String expectedName = "Moon"; + final CelestialBody mockedBody = Mockito.mock(CelestialBody.class); + Mockito.when(mockedBody.getName()).thenReturn(expectedName); + final TestBodyAttraction testBodyAttraction = new TestBodyAttraction(mockedBody); + // WHEN + final String actualName = testBodyAttraction.getBodyName(); + // THEN + Assertions.assertEquals(expectedName, actualName); + } + + private static class TestBodyAttraction extends AbstractBodyAttraction { + + protected TestBodyAttraction(CelestialBody body) { + super(body); + } + + @Override + public Vector3D acceleration(SpacecraftState s, double[] parameters) { + return null; + } + + @Override + public > FieldVector3D acceleration(FieldSpacecraftState s, T[] parameters) { + return null; + } + } + +} diff --git a/src/test/java/org/orekit/forces/gravity/HolmesFeatherstoneAttractionModelTest.java b/src/test/java/org/orekit/forces/gravity/HolmesFeatherstoneAttractionModelTest.java index a25164e1bf..a45d7758f0 100644 --- a/src/test/java/org/orekit/forces/gravity/HolmesFeatherstoneAttractionModelTest.java +++ b/src/test/java/org/orekit/forces/gravity/HolmesFeatherstoneAttractionModelTest.java @@ -38,7 +38,11 @@ import org.hipparchus.ode.nonstiff.DormandPrince853FieldIntegrator; import org.hipparchus.ode.nonstiff.DormandPrince853Integrator; import org.hipparchus.util.FastMath; -import org.junit.jupiter.api.*; +import org.junit.jupiter.api.AfterEach; +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.attitudes.Attitude; @@ -255,8 +259,8 @@ private GradientHessian gradientHessian(final HolmesFeatherstoneAttractionModel for (int m = degree; m >= 0; --m) { // compute tesseral terms - index = ((Integer) computeTesseralMethod.invoke(hfModel, m, degree, index, t, u, tOu, - pnm0Plus2, pnm0Plus1, pnm1Plus1, pnm0, pnm1, pnm2)).intValue(); + index = (Integer) computeTesseralMethod.invoke(hfModel, m, degree, index, t, u, tOu, + pnm0Plus2, pnm0Plus1, pnm1Plus1, pnm0, pnm1, pnm2); if (m <= order) { // compute contribution of current order to field (equation 5 of the paper) @@ -503,10 +507,12 @@ public void RealFieldTest() { FieldNumericalPropagator FNP = new FieldNumericalPropagator<>(field, integrator); FNP.setOrbitType(type); + FNP.setPositionAngleType(PositionAngleType.TRUE); FNP.setInitialState(initialState); NumericalPropagator NP = new NumericalPropagator(RIntegrator); - NP.setOrbitType(type); + NP.setOrbitType(FNP.getOrbitType()); + NP.setPositionAngleType(FNP.getPositionAngleType()); NP.setInitialState(iSR); double[][] c = new double[3][1]; @@ -799,7 +805,7 @@ public void testHelioSynchronous() { // initialization - AbsoluteDate date = new AbsoluteDate(new DateComponents(1970, 07, 01), + AbsoluteDate date = new AbsoluteDate(new DateComponents(1970, 7, 1), new TimeComponents(13, 59, 27.816), TimeScalesFactory.getUTC()); Transform itrfToEME2000 = itrf.getTransformTo(FramesFactory.getEME2000(), date); @@ -823,7 +829,7 @@ public void testHelioSynchronous() c, s))); // let the step handler perform the test - propagator.setStepHandler(Constants.JULIAN_DAY, new SpotStepHandler(date, mu)); + propagator.setStepHandler(Constants.JULIAN_DAY, new SpotStepHandler()); propagator.setInitialState(new SpacecraftState(orbit)); propagator.propagate(date.shiftedBy(7 * Constants.JULIAN_DAY)); Assertions.assertTrue(propagator.getCalls() < 9200); @@ -832,13 +838,14 @@ public void testHelioSynchronous() private static class SpotStepHandler implements OrekitFixedStepHandler { - public SpotStepHandler(AbsoluteDate date, double mu) { + private final PVCoordinatesProvider sun; + private double previous; + + public SpotStepHandler() { sun = CelestialBodyFactory.getSun(); previous = Double.NaN; } - private PVCoordinatesProvider sun; - private double previous; public void handleStep(SpacecraftState currentState) { @@ -886,6 +893,8 @@ public void testEcksteinHechlerReference() { // let the step handler perform the test propagator.setInitialState(new SpacecraftState(initialOrbit)); + propagator.setOrbitType(OrbitType.EQUINOCTIAL); + propagator.setPositionAngleType(PositionAngleType.TRUE); propagator.setStepHandler(20, new EckStepHandler(initialOrbit, ae, unnormalizedC20, unnormalizedC30, unnormalizedC40, unnormalizedC50, unnormalizedC60)); @@ -899,15 +908,15 @@ private static class EckStepHandler implements OrekitFixedStepHandler { /** Body mu */ private static final double mu = 3.986004415e+14; + private final EcksteinHechlerPropagator referencePropagator; + private EckStepHandler(Orbit initialOrbit, double ae, - double c20, double c30, double c40, double c50, double c60) - { + double c20, double c30, double c40, double c50, double c60) { referencePropagator = new EcksteinHechlerPropagator(initialOrbit, ae, mu, c20, c30, c40, c50, c60); } - private EcksteinHechlerPropagator referencePropagator; public void handleStep(SpacecraftState currentState) { SpacecraftState EHPOrbit = referencePropagator.propagate(currentState.getDate()); @@ -1049,7 +1058,7 @@ public void testStateJacobian() GravityFieldFactory.addPotentialCoefficientsReader(new GRGSFormatReader("grim4s4_gr", true)); // initialization - AbsoluteDate date = new AbsoluteDate(new DateComponents(2000, 07, 01), + AbsoluteDate date = new AbsoluteDate(new DateComponents(2000, 7, 1), new TimeComponents(13, 59, 27.816), TimeScalesFactory.getUTC()); double i = FastMath.toRadians(98.7); @@ -1082,7 +1091,7 @@ public void testStateJacobianVs80Implementation() GravityFieldFactory.addPotentialCoefficientsReader(new GRGSFormatReader("grim4s4_gr", true)); // initialization - AbsoluteDate date = new AbsoluteDate(new DateComponents(2000, 07, 01), + AbsoluteDate date = new AbsoluteDate(new DateComponents(2000, 7, 1), new TimeComponents(13, 59, 27.816), TimeScalesFactory.getUTC()); double i = FastMath.toRadians(98.7); @@ -1110,7 +1119,7 @@ public void testStateJacobianVs80ImplementationGradient() GravityFieldFactory.addPotentialCoefficientsReader(new GRGSFormatReader("grim4s4_gr", true)); // initialization - AbsoluteDate date = new AbsoluteDate(new DateComponents(2000, 07, 01), + AbsoluteDate date = new AbsoluteDate(new DateComponents(2000, 7, 1), new TimeComponents(13, 59, 27.816), TimeScalesFactory.getUTC()); double i = FastMath.toRadians(98.7); @@ -1138,7 +1147,7 @@ public void testStateJacobianVsFiniteDifferences() GravityFieldFactory.addPotentialCoefficientsReader(new GRGSFormatReader("grim4s4_gr", true)); // initialization - AbsoluteDate date = new AbsoluteDate(new DateComponents(2000, 07, 01), + AbsoluteDate date = new AbsoluteDate(new DateComponents(2000, 7, 1), new TimeComponents(13, 59, 27.816), TimeScalesFactory.getUTC()); double i = FastMath.toRadians(98.7); @@ -1165,7 +1174,7 @@ public void testStateJacobianVsFiniteDifferencesGradient() GravityFieldFactory.addPotentialCoefficientsReader(new GRGSFormatReader("grim4s4_gr", true)); // initialization - AbsoluteDate date = new AbsoluteDate(new DateComponents(2000, 07, 01), + AbsoluteDate date = new AbsoluteDate(new DateComponents(2000, 7, 1), new TimeComponents(13, 59, 27.816), TimeScalesFactory.getUTC()); double i = FastMath.toRadians(98.7); @@ -1191,7 +1200,7 @@ public void testIssue996() { GravityFieldFactory.addPotentialCoefficientsReader(new GRGSFormatReader("grim4s4_gr", true)); // initialization - AbsoluteDate date = new AbsoluteDate(new DateComponents(2000, 07, 01), + AbsoluteDate date = new AbsoluteDate(new DateComponents(2000, 7, 1), new TimeComponents(13, 59, 27.816), TimeScalesFactory.getUTC()); double i = FastMath.toRadians(98.7); diff --git a/src/test/java/org/orekit/forces/gravity/J2OnlyPerturbationTest.java b/src/test/java/org/orekit/forces/gravity/J2OnlyPerturbationTest.java new file mode 100644 index 0000000000..87f0e33a14 --- /dev/null +++ b/src/test/java/org/orekit/forces/gravity/J2OnlyPerturbationTest.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.forces.gravity; + +import org.hipparchus.CalculusFieldElement; +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.BeforeEach; +import org.junit.jupiter.api.Test; +import org.orekit.Utils; +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.CartesianOrbit; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.SpacecraftState; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.TimeScalarFunction; +import org.orekit.utils.Constants; +import org.orekit.utils.PVCoordinates; + +class J2OnlyPerturbationTest { + + @BeforeEach + public void setUp() { + Utils.setDataRoot("regular-data:potential"); + } + + @Test + void testAcceleration() { + // GIVEN + final UnnormalizedSphericalHarmonicsProvider unnormalizedProvider = GravityFieldFactory.getUnnormalizedProvider(2 ,0); + final Frame frame = FramesFactory.getGTOD(false); + final J2OnlyPerturbation j2OnlyPerturbation = new J2OnlyPerturbation(unnormalizedProvider, frame); + final SpacecraftState spacecraftState = buildState(); + // WHEN + final Vector3D actualAcceleration = j2OnlyPerturbation.acceleration(spacecraftState, new double[0]); + // THEN + final HolmesFeatherstoneAttractionModel holmesFeatherstoneAttractionModel = new HolmesFeatherstoneAttractionModel( + frame, GravityFieldFactory.getNormalizedProvider(unnormalizedProvider)); + final Vector3D expectedAcceleration = holmesFeatherstoneAttractionModel.acceleration(spacecraftState, + new double[] { unnormalizedProvider.getMu() }); + final double tolerance = 1e-12; + Assertions.assertEquals(expectedAcceleration.getX(), actualAcceleration.getX(), tolerance); + Assertions.assertEquals(expectedAcceleration.getY(), actualAcceleration.getY(), tolerance); + Assertions.assertEquals(expectedAcceleration.getZ(), actualAcceleration.getZ(), tolerance); + } + + @Test + void testAccelerationField() { + // GIVEN + final Frame frame = FramesFactory.getGTOD(false); + final J2OnlyPerturbation j2OnlyPerturbation = new J2OnlyPerturbation(Constants.EGM96_EARTH_MU, + Constants.EGM96_EARTH_EQUATORIAL_RADIUS, -Constants.EGM96_EARTH_C20, frame); + final SpacecraftState spacecraftState = buildState(); + final ComplexField field = ComplexField.getInstance(); + final FieldSpacecraftState fieldState = new FieldSpacecraftState<>(field, spacecraftState); + // WHEN + final FieldVector3D fieldAcceleration = j2OnlyPerturbation.acceleration(fieldState, null); + final Vector3D actualAcceleration = fieldAcceleration.toVector3D(); + // THEN + final Vector3D expectedAcceleration = j2OnlyPerturbation.acceleration(spacecraftState, new double[0]); + final double tolerance = 1e-12; + Assertions.assertEquals(expectedAcceleration.getX(), actualAcceleration.getX(), tolerance); + Assertions.assertEquals(expectedAcceleration.getY(), actualAcceleration.getY(), tolerance); + Assertions.assertEquals(expectedAcceleration.getZ(), actualAcceleration.getZ(), tolerance); + } + + @Test + void testAccelerationFieldAgainstGeopotential() { + // GIVEN + final UnnormalizedSphericalHarmonicsProvider unnormalizedProvider = GravityFieldFactory.getUnnormalizedProvider(2 ,0); + final Frame frame = FramesFactory.getTOD(false); + final J2OnlyPerturbation j2OnlyPerturbation = new J2OnlyPerturbation(unnormalizedProvider, frame); + final SpacecraftState spacecraftState = buildState(); + final ComplexField field = ComplexField.getInstance(); + final FieldSpacecraftState fieldState = new FieldSpacecraftState<>(field, spacecraftState); + // WHEN + final FieldVector3D actualAcceleration = j2OnlyPerturbation.acceleration(fieldState, null); + // THEN + final Frame rotatingFrame = FramesFactory.getGTOD(false); + final HolmesFeatherstoneAttractionModel holmesFeatherstoneAttractionModel = new HolmesFeatherstoneAttractionModel( + rotatingFrame, GravityFieldFactory.getNormalizedProvider(unnormalizedProvider)); + final Complex[] mu = MathArrays.buildArray(field, 1); + mu[0] = field.getOne().newInstance(unnormalizedProvider.getMu()); + final FieldVector3D expectedAcceleration = holmesFeatherstoneAttractionModel.acceleration(fieldState, + mu); + final double tolerance = 1e-12; + Assertions.assertEquals(expectedAcceleration.getX().getReal(), actualAcceleration.getX().getReal(), tolerance); + Assertions.assertEquals(expectedAcceleration.getY().getReal(), actualAcceleration.getY().getReal(), tolerance); + Assertions.assertEquals(expectedAcceleration.getZ().getReal(), actualAcceleration.getZ().getReal(), tolerance); + Assertions.assertEquals(expectedAcceleration.getX().getImaginary(), actualAcceleration.getX().getImaginary(), tolerance); + Assertions.assertEquals(expectedAcceleration.getY().getImaginary(), actualAcceleration.getY().getImaginary(), tolerance); + Assertions.assertEquals(expectedAcceleration.getZ().getImaginary(), actualAcceleration.getZ().getImaginary(), tolerance); + } + + @Test + void testGetters() { + // GIVEN + final Frame expectedFrame = FramesFactory.getGTOD(false); + final double expectedMu = Constants.EGM96_EARTH_MU; + final double expectedrEq = Constants.EGM96_EARTH_EQUATORIAL_RADIUS; + final double expectedJ2 = -Constants.EGM96_EARTH_C20; + // WHEN + final J2OnlyPerturbation j2OnlyPerturbation = new J2OnlyPerturbation(expectedMu, expectedrEq, + getJ2OverTime(expectedJ2), expectedFrame); + // THEN + Assertions.assertEquals(expectedFrame, j2OnlyPerturbation.getFrame()); + Assertions.assertEquals(expectedMu, j2OnlyPerturbation.getMu()); + Assertions.assertEquals(expectedrEq, j2OnlyPerturbation.getrEq()); + Assertions.assertTrue(j2OnlyPerturbation.getParametersDrivers().isEmpty()); + Assertions.assertTrue(j2OnlyPerturbation.dependsOnPositionOnly()); + } + + @Test + void testGetJ2() { + // GIVEN + final Frame expectedFrame = FramesFactory.getGTOD(false); + final double expectedMu = Constants.EGM96_EARTH_MU; + final double expectedrEq = Constants.EGM96_EARTH_EQUATORIAL_RADIUS; + final double expectedJ2 = -Constants.EGM96_EARTH_C20; + final AbsoluteDate absoluteDate = AbsoluteDate.ARBITRARY_EPOCH; + // WHEN + final J2OnlyPerturbation j2OnlyPerturbation = new J2OnlyPerturbation(expectedMu, expectedrEq, + getJ2OverTime(expectedJ2), expectedFrame); + // THEN + final FieldAbsoluteDate fieldAbsoluteDate = new FieldAbsoluteDate<>(ComplexField.getInstance(), + absoluteDate); + Assertions.assertEquals(j2OnlyPerturbation.getJ2(absoluteDate), + j2OnlyPerturbation.getJ2(fieldAbsoluteDate).getReal()); + } + + private TimeScalarFunction getJ2OverTime(final double constantJ2) { + return new TimeScalarFunction() { + @Override + public double value(AbsoluteDate date) { + return constantJ2; + } + + @Override + public > T value(FieldAbsoluteDate date) { + return date.getField().getZero().newInstance(constantJ2); + } + }; + } + + private SpacecraftState buildState() { + final Vector3D position = new Vector3D(1e7, 1e3, 1e2); + final Vector3D velocity = new Vector3D(0., 4e3, 1e1); + final PVCoordinates pvCoordinates = new PVCoordinates(position, velocity); + final CartesianOrbit orbit = new CartesianOrbit(pvCoordinates, FramesFactory.getGCRF(), + AbsoluteDate.ARBITRARY_EPOCH, Constants.EGM96_EARTH_MU); + return new SpacecraftState(orbit); + } + +} diff --git a/src/test/java/org/orekit/forces/gravity/ReferenceFieldModel.java b/src/test/java/org/orekit/forces/gravity/ReferenceFieldModel.java index b806024a2c..5cf53b84b5 100644 --- a/src/test/java/org/orekit/forces/gravity/ReferenceFieldModel.java +++ b/src/test/java/org/orekit/forces/gravity/ReferenceFieldModel.java @@ -106,9 +106,9 @@ public Dfp nonCentralPart(final AbsoluteDate date, final Vector3D position) Dfp y = dfpField.newDfp(position.getY()); Dfp z = dfpField.newDfp(position.getZ()); - Dfp rho2 = x.multiply(x).add(y.multiply(y)); + Dfp rho2 = x.square().add(y.square()); Dfp rho = rho2.sqrt(); - Dfp r2 = rho2.add(z.multiply(z)); + Dfp r2 = rho2.add(z.square()); Dfp r = r2.sqrt(); Dfp aOr = dfpField.newDfp(provider.getAe()).divide(r); Dfp lambda = position.getX() > 0 ? diff --git a/src/test/java/org/orekit/forces/gravity/SingleBodyAbsoluteAttractionTest.java b/src/test/java/org/orekit/forces/gravity/SingleBodyAbsoluteAttractionTest.java index 79d43b8737..5f5aa24659 100644 --- a/src/test/java/org/orekit/forces/gravity/SingleBodyAbsoluteAttractionTest.java +++ b/src/test/java/org/orekit/forces/gravity/SingleBodyAbsoluteAttractionTest.java @@ -75,7 +75,7 @@ protected FieldVector3D accelerationDerivatives(final Force try { final AbsoluteDate date = state.getDate().toAbsoluteDate(); final FieldVector3D position = state.getPVCoordinates().getPosition(); - java.lang.reflect.Field bodyField = SingleBodyAbsoluteAttraction.class.getDeclaredField("body"); + java.lang.reflect.Field bodyField = AbstractBodyAttraction.class.getDeclaredField("body"); bodyField.setAccessible(true); CelestialBody body = (CelestialBody) bodyField.get(forceModel); double gm = forceModel. @@ -105,7 +105,7 @@ protected FieldVector3D accelerationDerivativesGradient(final ForceMod try { final AbsoluteDate date = state.getDate().toAbsoluteDate(); final FieldVector3D position = state.getPVCoordinates().getPosition(); - java.lang.reflect.Field bodyField = SingleBodyAbsoluteAttraction.class.getDeclaredField("body"); + java.lang.reflect.Field bodyField = AbstractBodyAttraction.class.getDeclaredField("body"); bodyField.setAccessible(true); CelestialBody body = (CelestialBody) bodyField.get(forceModel); double gm = forceModel. diff --git a/src/test/java/org/orekit/forces/gravity/SingleBodyRelativeAttractionTest.java b/src/test/java/org/orekit/forces/gravity/SingleBodyRelativeAttractionTest.java index 273395ee88..03d8b705e9 100644 --- a/src/test/java/org/orekit/forces/gravity/SingleBodyRelativeAttractionTest.java +++ b/src/test/java/org/orekit/forces/gravity/SingleBodyRelativeAttractionTest.java @@ -71,7 +71,7 @@ protected FieldVector3D accelerationDerivatives(final Force try { final AbsoluteDate date = state.getDate().toAbsoluteDate(); final FieldVector3D position = state.getPVCoordinates().getPosition(); - java.lang.reflect.Field bodyField = SingleBodyRelativeAttraction.class.getDeclaredField("body"); + java.lang.reflect.Field bodyField = AbstractBodyAttraction.class.getDeclaredField("body"); bodyField.setAccessible(true); CelestialBody body = (CelestialBody) bodyField.get(forceModel); double gm = forceModel. @@ -101,7 +101,7 @@ protected FieldVector3D accelerationDerivativesGradient(final ForceMod try { final AbsoluteDate date = state.getDate().toAbsoluteDate(); final FieldVector3D position = state.getPVCoordinates().getPosition(); - java.lang.reflect.Field bodyField = SingleBodyRelativeAttraction.class.getDeclaredField("body"); + java.lang.reflect.Field bodyField = AbstractBodyAttraction.class.getDeclaredField("body"); bodyField.setAccessible(true); CelestialBody body = (CelestialBody) bodyField.get(forceModel); double gm = forceModel. diff --git a/src/test/java/org/orekit/forces/gravity/ThirdBodyAttractionTest.java b/src/test/java/org/orekit/forces/gravity/ThirdBodyAttractionTest.java index dfe341a88d..36b883460d 100644 --- a/src/test/java/org/orekit/forces/gravity/ThirdBodyAttractionTest.java +++ b/src/test/java/org/orekit/forces/gravity/ThirdBodyAttractionTest.java @@ -76,7 +76,7 @@ protected FieldVector3D accelerationDerivatives(final Force try { final AbsoluteDate date = state.getDate().toAbsoluteDate(); final FieldVector3D position = state.getPVCoordinates().getPosition(); - java.lang.reflect.Field bodyField = ThirdBodyAttraction.class.getDeclaredField("body"); + java.lang.reflect.Field bodyField = AbstractBodyAttraction.class.getDeclaredField("body"); bodyField.setAccessible(true); CelestialBody body = (CelestialBody) bodyField.get(forceModel); double gm = forceModel. @@ -108,7 +108,7 @@ protected FieldVector3D accelerationDerivativesGradient(final ForceMod try { final AbsoluteDate date = state.getDate().toAbsoluteDate(); final FieldVector3D position = state.getPVCoordinates().getPosition(); - java.lang.reflect.Field bodyField = ThirdBodyAttraction.class.getDeclaredField("body"); + java.lang.reflect.Field bodyField = AbstractBodyAttraction.class.getDeclaredField("body"); bodyField.setAccessible(true); CelestialBody body = (CelestialBody) bodyField.get(forceModel); double gm = forceModel. diff --git a/src/test/java/org/orekit/forces/gravity/potential/NormalizedSphericalHarmonicsProviderTest.java b/src/test/java/org/orekit/forces/gravity/potential/NormalizedSphericalHarmonicsProviderTest.java new file mode 100644 index 0000000000..03efd759d0 --- /dev/null +++ b/src/test/java/org/orekit/forces/gravity/potential/NormalizedSphericalHarmonicsProviderTest.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.forces.gravity.potential; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.orekit.time.AbsoluteDate; + +class NormalizedSphericalHarmonicsProviderTest { + + @Test + void testGetNormalizedC20() { + // GIVEN + final double expectedC20 = 1.; + final TestNormalizedProvider testNormalizedProvider = new TestNormalizedProvider(expectedC20); + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + // WHEN + final double actualC20 = testNormalizedProvider.getNormalizedC20(date); + // THEN + Assertions.assertEquals(expectedC20, actualC20, 0.); + } + + private static class TestNormalizedProvider implements NormalizedSphericalHarmonicsProvider { + + private final double c20; + + TestNormalizedProvider(final double c20) { + this.c20 = c20; + } + + @Override + public int getMaxDegree() { + return 2; + } + + @Override + public int getMaxOrder() { + return 0; + } + + @Override + public double getMu() { + return 0; + } + + @Override + public double getAe() { + return 0; + } + + @Override + public AbsoluteDate getReferenceDate() { + return null; + } + + @Override + public TideSystem getTideSystem() { + return null; + } + + @Override + public NormalizedSphericalHarmonics onDate(AbsoluteDate date) { + return new NormalizedSphericalHarmonics() { + @Override + public double getNormalizedCnm(int n, int m) { + if ((n == 2) && (m == 0)) { + return c20; + } + return 0; + } + + @Override + public double getNormalizedSnm(int n, int m) { + return 0; + } + + @Override + public AbsoluteDate getDate() { + return null; + } + }; + } + } + +} diff --git a/src/test/java/org/orekit/forces/gravity/potential/UnnormalizedSphericalHarmonicsProviderTest.java b/src/test/java/org/orekit/forces/gravity/potential/UnnormalizedSphericalHarmonicsProviderTest.java new file mode 100644 index 0000000000..9ddd37d2cd --- /dev/null +++ b/src/test/java/org/orekit/forces/gravity/potential/UnnormalizedSphericalHarmonicsProviderTest.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.forces.gravity.potential; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.orekit.time.AbsoluteDate; + +class UnnormalizedSphericalHarmonicsProviderTest { + + @Test + void testGetUnnormalizedC20() { + // GIVEN + final double expectedC20 = 1.; + final TestUnnormalizedProvider testUnnormalizedProvider = new TestUnnormalizedProvider(expectedC20); + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + // WHEN + final double actualC20 = testUnnormalizedProvider.getUnnormalizedC20(date); + // THEN + Assertions.assertEquals(expectedC20, actualC20, 0.); + } + + private static class TestUnnormalizedProvider implements UnnormalizedSphericalHarmonicsProvider { + + private final double c20; + + TestUnnormalizedProvider(final double c20) { + this.c20 = c20; + } + + @Override + public int getMaxDegree() { + return 2; + } + + @Override + public int getMaxOrder() { + return 0; + } + + @Override + public double getMu() { + return 0; + } + + @Override + public double getAe() { + return 0; + } + + @Override + public AbsoluteDate getReferenceDate() { + return null; + } + + @Override + public TideSystem getTideSystem() { + return null; + } + + @Override + public UnnormalizedSphericalHarmonics onDate(AbsoluteDate date) { + return new UnnormalizedSphericalHarmonics() { + @Override + public double getUnnormalizedCnm(int n, int m) { + if ((n == 2) && (m == 0)) { + return c20; + } + return 0; + } + + @Override + public double getUnnormalizedSnm(int n, int m) { + return 0; + } + + @Override + public AbsoluteDate getDate() { + return null; + } + }; + } + } + +} diff --git a/src/test/java/org/orekit/forces/maneuvers/ConfigurableLowThrustManeuverTest.java b/src/test/java/org/orekit/forces/maneuvers/ConfigurableLowThrustManeuverTest.java index bced0c8e9b..b402f52756 100644 --- a/src/test/java/org/orekit/forces/maneuvers/ConfigurableLowThrustManeuverTest.java +++ b/src/test/java/org/orekit/forces/maneuvers/ConfigurableLowThrustManeuverTest.java @@ -170,7 +170,7 @@ protected PerigeeCenteredIntervalDetector(final double halfArcLength, final Posi public PerigeeCenteredIntervalDetector(final double halfArcLength, final PositionAngleType type, final EventHandler handler) { - super(halfArcLength, type, s -> maxCheck, maxThreshold, DEFAULT_MAX_ITER, handler); + super(halfArcLength, type, AdaptableInterval.of(maxCheck), maxThreshold, DEFAULT_MAX_ITER, handler); } @Override @@ -203,7 +203,7 @@ protected ApogeeCenteredIntervalDetector(final double halfArcLength, final Posit } public ApogeeCenteredIntervalDetector(final double halfArcLength, final PositionAngleType type, final EventHandler handler) { - super(halfArcLength, type, s -> maxCheck, maxThreshold, DEFAULT_MAX_ITER, handler); + super(halfArcLength, type, AdaptableInterval.of(maxCheck), maxThreshold, DEFAULT_MAX_ITER, handler); } @Override @@ -232,7 +232,7 @@ private static class DateIntervalDetector extends AbstractDetector DateDetector.DEFAULT_MAX_CHECK, DateDetector.DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, + this(startDate, endDate, AdaptableInterval.of(DateDetector.DEFAULT_MAX_CHECK), DateDetector.DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, new StopOnEvent()); } @@ -281,7 +281,7 @@ private static class DateIntervalFieldDetector public DateIntervalFieldDetector(final FieldAbsoluteDate startDate, final FieldAbsoluteDate endDate) { this(startDate, endDate, - s -> DateDetector.DEFAULT_MAX_CHECK, + FieldAdaptableInterval.of(DateDetector.DEFAULT_MAX_CHECK), startDate.getField().getZero().newInstance(DateDetector.DEFAULT_THRESHOLD), DEFAULT_MAX_ITER, new FieldStopOnEvent<>()); } diff --git a/src/test/java/org/orekit/forces/maneuvers/ConstantThrustManeuverTest.java b/src/test/java/org/orekit/forces/maneuvers/ConstantThrustManeuverTest.java index b9e1968eb2..9f5cd760ea 100644 --- a/src/test/java/org/orekit/forces/maneuvers/ConstantThrustManeuverTest.java +++ b/src/test/java/org/orekit/forces/maneuvers/ConstantThrustManeuverTest.java @@ -69,7 +69,7 @@ import org.orekit.utils.ParameterDriver; import org.orekit.utils.TimeStampedPVCoordinates; -public class ConstantThrustManeuverTest extends AbstractLegacyForceModelTest { +class ConstantThrustManeuverTest extends AbstractLegacyForceModelTest { // Body mu private double mu; @@ -126,7 +126,7 @@ private CircularOrbit dummyOrbit(AbsoluteDate date) { } @Test - public void testJacobianVs80Implementation() { + void testJacobianVs80Implementation() { final double isp = 318; final double mass = 2500; final double a = 24396159; @@ -168,7 +168,7 @@ public void testJacobianVs80Implementation() { } @Test - public void testJacobianVs80ImplementationGradient() { + void testJacobianVs80ImplementationGradient() { final double isp = 318; final double mass = 2500; final double a = 24396159; @@ -210,7 +210,7 @@ public void testJacobianVs80ImplementationGradient() { } @Test - public void testPositiveDuration() { + void testPositiveDuration() { AbsoluteDate date = new AbsoluteDate(new DateComponents(2004, 01, 01), new TimeComponents(23, 30, 00.000), TimeScalesFactory.getUTC()); @@ -240,7 +240,7 @@ public void testPositiveDuration() { } @Test - public void testNegativeDuration() { + void testNegativeDuration() { AbsoluteDate date = new AbsoluteDate(new DateComponents(2004, 01, 01), new TimeComponents(23, 30, 00.000), TimeScalesFactory.getUTC()); @@ -264,7 +264,7 @@ public void testNegativeDuration() { } @Test - public void testRoughBehaviour() { + void testRoughBehaviour() { final double isp = 318; final double mass = 2500; final double a = 24396159; @@ -327,7 +327,7 @@ public void testRoughBehaviour() { * propagation X with the FieldPropagation and then applying the taylor * expansion of dX to the result.*/ @Test - public void testRealField() { + void testRealField() { DSFactory factory = new DSFactory(6, 5); DerivativeStructure a_0 = factory.variable(0, 7e7); DerivativeStructure e_0 = factory.variable(1, 0.4); @@ -389,7 +389,7 @@ public void testRealField() { * propagation X with the FieldPropagation and then applying the taylor * expansion of dX to the result.*/ @Test - public void testRealFieldGradient() { + void testRealFieldGradient() { int freeParameters = 6; Gradient a_0 = Gradient.variable(freeParameters, 0, 7e7); Gradient e_0 = Gradient.variable(freeParameters, 1, 0.4); @@ -511,7 +511,7 @@ public void RealFieldExpectErrorTest() { } @Test - public void testForwardAndBackward() { + void testForwardAndBackward() { final double isp = 318; final double mass = 2500; final double a = 24396159; @@ -552,6 +552,7 @@ public void testForwardAndBackward() { integrator1.setInitialStepSize(60); final NumericalPropagator propagator1 = new NumericalPropagator(integrator1); propagator1.setOrbitType(orbitType); + propagator1.setPositionAngleType(PositionAngleType.TRUE); propagator1.setInitialState(initialState); propagator1.setAttitudeProvider(law); propagator1.addForceModel(maneuver); @@ -567,6 +568,7 @@ public void testForwardAndBackward() { integrator2.setInitialStepSize(60); final NumericalPropagator propagator2 = new NumericalPropagator(integrator2); propagator2.setOrbitType(orbitType); + propagator2.setPositionAngleType(propagator1.getPositionAngleType()); propagator2.setInitialState(finalState); propagator2.setAttitudeProvider(law); propagator2.addForceModel(maneuver); @@ -584,7 +586,7 @@ public void testForwardAndBackward() { } @Test - public void testParameterDerivative() { + void testParameterDerivative() { // pos-vel (from a ZOOM ephemeris reference) final Vector3D pos = new Vector3D(6.46885878304673824e+06, -1.88050918456274318e+06, -1.32931592294715829e+04); @@ -606,7 +608,7 @@ public void testParameterDerivative() { } @Test - public void testParameterDerivativeGradient() { + void testParameterDerivativeGradient() { // pos-vel (from a ZOOM ephemeris reference) final Vector3D pos = new Vector3D(6.46885878304673824e+06, -1.88050918456274318e+06, -1.32931592294715829e+04); @@ -628,7 +630,7 @@ public void testParameterDerivativeGradient() { } @Test - public void testInertialManeuver() { + void testInertialManeuver() { final double isp = 318; final double mass = 2500; final double a = 24396159; @@ -722,7 +724,7 @@ public void testInertialManeuver() { } @Test - public void testStopInMiddle() { + void testStopInMiddle() { final double isp = 318; final double mass = 2500; final double a = 24396159; @@ -795,7 +797,7 @@ public void testStopInMiddle() { } @Test - public void testNullDuration() { + void testNullDuration() { // Defining initial state final Frame eme2000 = FramesFactory.getEME2000(); diff --git a/src/test/java/org/orekit/forces/maneuvers/Control3DVectorCostTypeTest.java b/src/test/java/org/orekit/forces/maneuvers/Control3DVectorCostTypeTest.java index c510c93c3d..b90a3ef596 100644 --- a/src/test/java/org/orekit/forces/maneuvers/Control3DVectorCostTypeTest.java +++ b/src/test/java/org/orekit/forces/maneuvers/Control3DVectorCostTypeTest.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2024 Romain Serra +/* 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. diff --git a/src/test/java/org/orekit/forces/maneuvers/FieldImpulseManeuverTest.java b/src/test/java/org/orekit/forces/maneuvers/FieldImpulseManeuverTest.java index f37464e2ba..581a8f9186 100644 --- a/src/test/java/org/orekit/forces/maneuvers/FieldImpulseManeuverTest.java +++ b/src/test/java/org/orekit/forces/maneuvers/FieldImpulseManeuverTest.java @@ -18,7 +18,13 @@ import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; -import org.hipparchus.analysis.differentiation.*; +import org.hipparchus.analysis.differentiation.DSFactory; +import org.hipparchus.analysis.differentiation.Gradient; +import org.hipparchus.analysis.differentiation.GradientField; +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.complex.Complex; import org.hipparchus.complex.ComplexField; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; @@ -43,10 +49,31 @@ import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; import org.orekit.frames.LOFType; -import org.orekit.orbits.*; -import org.orekit.propagation.*; -import org.orekit.propagation.events.*; -import org.orekit.propagation.events.handlers.*; +import org.orekit.orbits.CartesianOrbit; +import org.orekit.orbits.FieldCartesianOrbit; +import org.orekit.orbits.FieldOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; +import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.AbstractPropagator; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.MatricesHarvester; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.events.AbstractDetector; +import org.orekit.propagation.events.DateDetector; +import org.orekit.propagation.events.EclipseDetector; +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.FieldEclipseDetector; +import org.orekit.propagation.events.FieldEventDetector; +import org.orekit.propagation.events.FieldLatitudeCrossingDetector; +import org.orekit.propagation.events.LatitudeCrossingDetector; +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.handlers.StopOnEvent; import org.orekit.propagation.integration.FieldAdditionalDerivativesProvider; import org.orekit.propagation.integration.FieldCombinedDerivatives; import org.orekit.propagation.numerical.FieldNumericalPropagator; @@ -57,7 +84,7 @@ import org.orekit.utils.FieldPVCoordinates; import org.orekit.utils.PVCoordinates; -public class FieldImpulseManeuverTest { +class FieldImpulseManeuverTest { private final double mu = Constants.WGS84_EARTH_MU; private final Frame inertialFrame = FramesFactory.getGCRF(); @@ -70,6 +97,7 @@ public class FieldImpulseManeuverTest { private final Vector3D deltaV = new Vector3D(0.1, -0.5, 0.2); private final double timeOfFlight = 10000.; private final OrbitType orbitType = OrbitType.EQUINOCTIAL; + private final PositionAngleType positionAngleType = PositionAngleType.ECCENTRIC; private final LofOffset attitudeOverride = new LofOffset(inertialFrame, LOFType.QSW); private final GradientField gradientField = GradientField.getField(2); private final UnivariateDerivative1Field univariateDerivative1Field = new UnivariateDerivative1(0., 0.).getField(); @@ -113,14 +141,13 @@ public void setUp() { } @Test - public void testComplexConstructors() { + void testComplexConstructors() { // Given final Complex zero = complexField.getZero(); final Complex isp = zero.add(200.); final FieldVector3D deltaV = new FieldVector3D<>(complexField, Vector3D.PLUS_I); final FieldAbsoluteDate fieldAbsoluteDate = new FieldAbsoluteDate<>(complexField, AbsoluteDate.ARBITRARY_EPOCH); - @SuppressWarnings("unchecked") final FieldDateDetector dateDetector = new FieldDateDetector<>(complexField, fieldAbsoluteDate).withThreshold(zero.add(100.)); // When @@ -138,13 +165,12 @@ public void testComplexConstructors() { } @Test - public void testDeltaVNorm() { + void testDeltaVNorm() { // Given final Complex isp = complexField.getOne().add(200.); final FieldVector3D deltaV = new FieldVector3D<>(complexField, Vector3D.PLUS_I); final FieldAbsoluteDate fieldAbsoluteDate = new FieldAbsoluteDate<>(complexField, AbsoluteDate.ARBITRARY_EPOCH); - @SuppressWarnings("unchecked") final FieldDateDetector dateDetector = new FieldDateDetector<>(complexField, fieldAbsoluteDate); // When @@ -162,43 +188,43 @@ public void testDeltaVNorm() { } @Test - public void testEclipseDetectorDerivativeStructure() { + void testEclipseDetectorDerivativeStructure() { templateDetector(new DSFactory(1, 1).getDerivativeField(), DetectorType.ECLIPSE_DETECTOR, Control3DVectorCostType.TWO_NORM); } @Test - public void testEclipseDetectorGradient() { + void testEclipseDetectorGradient() { templateDetector(gradientField, DetectorType.ECLIPSE_DETECTOR, Control3DVectorCostType.TWO_NORM); } @Test - public void testEclipseDetectorGradientNormInf() { + void testEclipseDetectorGradientNormInf() { templateDetector(gradientField, DetectorType.ECLIPSE_DETECTOR, Control3DVectorCostType.INF_NORM); } @Test - public void testDateDetectorComplex() { + void testDateDetectorComplex() { templateDetector(complexField, DetectorType.DATE_DETECTOR, Control3DVectorCostType.TWO_NORM); } @Test - public void testDateDetectorUnivariateDerivative2() { + void testDateDetectorUnivariateDerivative2() { templateDetector(univariateDerivative2Field, DetectorType.DATE_DETECTOR, Control3DVectorCostType.TWO_NORM); } @Test - public void testDateDetectorGradientNorm1() { + void testDateDetectorGradientNorm1() { templateDetector(gradientField, DetectorType.DATE_DETECTOR, Control3DVectorCostType.ONE_NORM); } @Test - public void testLatitudeCrossingDetectorUnivariateDerivative1() { + void testLatitudeCrossingDetectorUnivariateDerivative1() { templateDetector(univariateDerivative1Field, DetectorType.LATITUDE_CROSSING_DETECTOR, Control3DVectorCostType.TWO_NORM); } @Test - public void testLatitudeCrossingDetectorDerivativeStructure() { + void testLatitudeCrossingDetectorDerivativeStructure() { templateDetector(new DSFactory(1, 1).getDerivativeField(), DetectorType.LATITUDE_CROSSING_DETECTOR, Control3DVectorCostType.TWO_NORM); } @@ -209,21 +235,21 @@ private > FieldImpulseManeuver fieldDeltaVSat = new FieldVector3D<>(field, impulseManeuver.getDeltaVSat()); final EventDetector detector = impulseManeuver.getTrigger(); final int maxIter = detector.getMaxIterationCount(); - final FieldAdaptableInterval fieldMaxCheck = s -> detector.getMaxCheckInterval().currentInterval(s.toSpacecraftState()); + final FieldAdaptableInterval + fieldMaxCheck = s -> detector.getMaxCheckInterval().currentInterval(s.toSpacecraftState()); final T fieldThreshold = field.getZero().add(detector.getThreshold()); FieldAbstractDetector fieldDetector; if (detector instanceof DateDetector) { - @SuppressWarnings("unchecked") FieldDateDetector dateDetector = new FieldDateDetector<>(field, new FieldAbsoluteDate<>(field, ((DateDetector) detector).getDate())); fieldDetector = dateDetector; } else if (detector instanceof LatitudeCrossingDetector) { fieldDetector = new FieldLatitudeCrossingDetector<>(field, - ((LatitudeCrossingDetector) detector).getBody(), - ((LatitudeCrossingDetector) detector).getLatitude()); + ((LatitudeCrossingDetector) detector).getBody(), + ((LatitudeCrossingDetector) detector).getLatitude()); } else if (detector instanceof EclipseDetector) { fieldDetector = new FieldEclipseDetector<>(field, - ((EclipseDetector) detector).getOccultationEngine()); + ((EclipseDetector) detector).getOccultationEngine()); } else { throw new OrekitInternalError(null); } @@ -251,7 +277,8 @@ private > void templateDetector(final Field propagator.addEventDetector(impulseManeuver); fieldPropagator.addEventDetector(convertManeuver(field, impulseManeuver, new FieldStopOnEvent<>())); // When - final SpacecraftState terminalState = propagator.propagate(endOfPropagationDate); + final SpacecraftState + terminalState = propagator.propagate(endOfPropagationDate); final FieldSpacecraftState fieldTerminalState = fieldPropagator.propagate(new FieldAbsoluteDate<>(field, endOfPropagationDate)); // Then compareStateToConstantOfFieldState(terminalState, fieldTerminalState); @@ -292,6 +319,7 @@ private NumericalPropagator createUnperturbedPropagator(final Orbit initialOrbit final NumericalPropagator propagator = new NumericalPropagator(integrator); propagator.setInitialState(new SpacecraftState(initialOrbit, initialMass)); propagator.setOrbitType(orbitType); + propagator.setPositionAngleType(positionAngleType); return propagator; } @@ -306,6 +334,7 @@ private > FieldNumericalPropagator createUn final T fieldInitialMass = field.getZero().add(initialMass); fieldPropagator.setInitialState(new FieldSpacecraftState<>(fieldInitialOrbit, fieldInitialMass)); fieldPropagator.setOrbitType(orbitType); + fieldPropagator.setPositionAngleType(positionAngleType); return fieldPropagator; } @@ -342,30 +371,29 @@ private > FieldCartesianOrbit createConstan final FieldPVCoordinates fieldPVCoordinates = new FieldPVCoordinates<>(fieldPosition, fieldVelocity); return new FieldCartesianOrbit<>(fieldPVCoordinates, inertialFrame, - new FieldAbsoluteDate<>(field, orbit.getDate()), field.getZero().add(mu)); + new FieldAbsoluteDate<>(field, orbit.getDate()), field.getZero().add(mu)); } @Test - public void testAdditionalStatePropagation() { + void testAdditionalStatePropagation() { // Given final UnivariateDerivative1 zero = univariateDerivative1Field.getZero(); final FieldNumericalPropagator fieldPropagator = createFieldPropagatorForAdditionalStatesAndDerivatives( univariateDerivative1Field); FieldSpacecraftState initialState = fieldPropagator.getInitialState(); final String name = "dummy"; - final UnivariateDerivative1 expectedValue = zero; - initialState = initialState.addAdditionalState(name, expectedValue); + initialState = initialState.addAdditionalState(name, zero); fieldPropagator.resetInitialState(initialState); // When final FieldAbsoluteDate targetDate = initialState.getDate().shiftedBy(zero.add(10000.)); final FieldSpacecraftState terminalState = fieldPropagator.propagate(targetDate); // Then final UnivariateDerivative1 actualValue = terminalState.getAdditionalState(name)[0]; - Assertions.assertEquals(expectedValue, actualValue); + Assertions.assertEquals(zero, actualValue); } @Test - public void testAdditionalStateDerivativesPropagation() { + void testAdditionalStateDerivativesPropagation() { // Given final Gradient zero = gradientField.getZero(); final FieldNumericalPropagator fieldPropagator = createFieldPropagatorForAdditionalStatesAndDerivatives( @@ -398,7 +426,7 @@ private > FieldNumericalPropagator createFi } @Test - public void testBackAndForthPropagation() { + void testBackAndForthPropagation() { // Given final Orbit initialOrbit = createOrbit(); final NumericalPropagator propagator = createUnperturbedPropagator(initialOrbit, initialMass); @@ -407,7 +435,6 @@ public void testBackAndForthPropagation() { final UnivariateDerivative1 zero = univariateDerivative1Field.getZero(); final FieldNumericalPropagator fieldPropagator = createUnperturbedFieldPropagator(univariateDerivative1Field, initialOrbit, initialMass); - fieldPropagator.setOrbitType(propagator.getOrbitType()); fieldPropagator.setAttitudeProvider(propagator.getAttitudeProvider()); fieldPropagator.setResetAtEnd(true); fieldPropagator.addEventDetector(convertManeuver(univariateDerivative1Field, impulseManeuver, new FieldContinueOnEvent<>())); @@ -422,7 +449,7 @@ public void testBackAndForthPropagation() { } @Test - public void testVersusCartesianStateTransitionMatrix() { + void testVersusCartesianStateTransitionMatrix() { // Given final int freeParameters = 3; final GradientField field = GradientField.getField(freeParameters); @@ -437,10 +464,8 @@ public void testVersusCartesianStateTransitionMatrix() { propagator.addEventDetector(dateDetector); propagator.setOrbitType(OrbitType.CARTESIAN); final Gradient zero = field.getZero(); - @SuppressWarnings("unchecked") final FieldDateDetector fieldDateDetector = - new FieldDateDetector<>(field, - new FieldAbsoluteDate<>(field, dateDetector.getDate())); + new FieldDateDetector<>(field, new FieldAbsoluteDate<>(field, dateDetector.getDate())); final FieldVector3D fieldDeltaV = new FieldVector3D<>( Gradient.variable(freeParameters, 0, 0.), Gradient.variable(freeParameters, 1, 0.), diff --git a/src/test/java/org/orekit/forces/maneuvers/ManeuverTest.java b/src/test/java/org/orekit/forces/maneuvers/ManeuverTest.java index d091bb029b..00dca7fd84 100644 --- a/src/test/java/org/orekit/forces/maneuvers/ManeuverTest.java +++ b/src/test/java/org/orekit/forces/maneuvers/ManeuverTest.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2024 Romain Serra +/* 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. diff --git a/src/test/java/org/orekit/forces/maneuvers/SmallManeuverAnalyticalModelTest.java b/src/test/java/org/orekit/forces/maneuvers/SmallManeuverAnalyticalModelTest.java index d8209f3b89..7fb43538d1 100644 --- a/src/test/java/org/orekit/forces/maneuvers/SmallManeuverAnalyticalModelTest.java +++ b/src/test/java/org/orekit/forces/maneuvers/SmallManeuverAnalyticalModelTest.java @@ -45,10 +45,10 @@ import org.orekit.utils.Constants; import org.orekit.utils.PVCoordinates; -public class SmallManeuverAnalyticalModelTest { +class SmallManeuverAnalyticalModelTest { @Test - public void testLowEarthOrbit1() { + void testLowEarthOrbit1() { Orbit leo = new CircularOrbit(7200000.0, -1.0e-5, 2.0e-4, FastMath.toRadians(98.0), @@ -67,7 +67,7 @@ public void testLowEarthOrbit1() { BoundedPropagator withoutManeuver = getEphemeris(leo, mass, t0, Vector3D.ZERO, f, isp); BoundedPropagator withManeuver = getEphemeris(leo, mass, t0, dV, f, isp); SmallManeuverAnalyticalModel model = - new SmallManeuverAnalyticalModel(withoutManeuver.propagate(t0), dV, isp); + new SmallManeuverAnalyticalModel(withoutManeuver.propagate(t0), OrbitType.CIRCULAR, dV, isp); Assertions.assertEquals(t0, model.getDate()); for (AbsoluteDate t = withoutManeuver.getMinDate(); @@ -95,7 +95,7 @@ public void testLowEarthOrbit1() { } @Test - public void testLowEarthOrbit2() { + void testLowEarthOrbit2() { Orbit leo = new CircularOrbit(7200000.0, -1.0e-5, 2.0e-4, FastMath.toRadians(98.0), @@ -113,8 +113,10 @@ public void testLowEarthOrbit2() { double isp = 315.0; BoundedPropagator withoutManeuver = getEphemeris(leo, mass, t0, Vector3D.ZERO, f, isp); BoundedPropagator withManeuver = getEphemeris(leo, mass, t0, dV, f, isp); + SpacecraftState stateWithoutManeuver = withoutManeuver.propagate(t0); + Vector3D rotatedDV = stateWithoutManeuver.getAttitude().getRotation().applyInverseTo(dV); SmallManeuverAnalyticalModel model = - new SmallManeuverAnalyticalModel(withoutManeuver.propagate(t0), dV, isp); + new SmallManeuverAnalyticalModel(stateWithoutManeuver, OrbitType.EQUINOCTIAL, leo.getFrame(), rotatedDV, isp); Assertions.assertEquals(t0, model.getDate()); for (AbsoluteDate t = withoutManeuver.getMinDate(); @@ -142,7 +144,7 @@ public void testLowEarthOrbit2() { } @Test - public void testEccentricOrbit() { + void testEccentricOrbit() { Orbit heo = new KeplerianOrbit(90000000.0, 0.92, FastMath.toRadians(98.0), FastMath.toRadians(12.3456), @@ -193,7 +195,7 @@ public void testEccentricOrbit() { } @Test - public void testJacobian() { + void testJacobian() { Frame eme2000 = FramesFactory.getEME2000(); Orbit leo = new CircularOrbit(7200000.0, -1.0e-2, 2.0e-3, @@ -295,6 +297,7 @@ private BoundedPropagator getEphemeris(final Orbit orbit, final double mass, integrator.setInitialStepSize(orbit.getKeplerianPeriod() / 100.0); final NumericalPropagator propagator = new NumericalPropagator(integrator); propagator.setOrbitType(orbit.getType()); + propagator.setPositionAngleType(PositionAngleType.TRUE); propagator.setInitialState(initialState); propagator.setAttitudeProvider(law); diff --git a/src/test/java/org/orekit/forces/maneuvers/propulsion/ProfileThrustPropulsionModelTest.java b/src/test/java/org/orekit/forces/maneuvers/propulsion/ProfileThrustPropulsionModelTest.java index ad00d66557..a1f2017ccb 100644 --- a/src/test/java/org/orekit/forces/maneuvers/propulsion/ProfileThrustPropulsionModelTest.java +++ b/src/test/java/org/orekit/forces/maneuvers/propulsion/ProfileThrustPropulsionModelTest.java @@ -54,17 +54,17 @@ import org.orekit.utils.Constants; import org.orekit.utils.TimeSpanMap; -public class ProfileThrustPropulsionModelTest { +class ProfileThrustPropulsionModelTest { @Test - public void testRoughBehaviour() { + void testRoughBehaviour() { doRoughBehaviour( 1.0, 2008.017, 28968115.974); doRoughBehaviour( 10.0, 2009.229, 28950587.132); doRoughBehaviour(100.0, 2021.350, 28777772.266); } @Test - public void testRoughBehaviourField() { + void testRoughBehaviourField() { doRoughBehaviourField(Binary64Field.getInstance(), 1.0, 2008.017, 28968115.974); doRoughBehaviourField(Binary64Field.getInstance(), 10.0, 2009.229, 28950587.132); doRoughBehaviourField(Binary64Field.getInstance(), 100.0, 2021.350, 28777772.266); @@ -90,8 +90,9 @@ private void doRoughBehaviour(final double rampDuration, final double expectedM, final AbsoluteDate initDate = new AbsoluteDate(new DateComponents(2004, 01, 01), new TimeComponents(23, 30, 00.000), TimeScalesFactory.getUTC()); + final PositionAngleType positionAngleType = PositionAngleType.TRUE; final Orbit initOrbit = - new KeplerianOrbit(a, e, i, omega, OMEGA, lv, PositionAngleType.TRUE, + new KeplerianOrbit(a, e, i, omega, OMEGA, lv, positionAngleType, FramesFactory.getEME2000(), initDate, Constants.EIGEN5C_EARTH_MU); final SpacecraftState initialState = new SpacecraftState(initOrbit, law.getAttitude(initOrbit, initOrbit.getDate(), initOrbit.getFrame()), mass); @@ -178,8 +179,9 @@ private > void doRoughBehaviourField(final Fie new DateComponents(2004, 01, 01), new TimeComponents(23, 30, 00.000), TimeScalesFactory.getUTC()); + final PositionAngleType positionAngleType = PositionAngleType.TRUE; final FieldOrbit initOrbit = - new FieldKeplerianOrbit<>(a, e, i, omega, OMEGA, lv, PositionAngleType.TRUE, + new FieldKeplerianOrbit<>(a, e, i, omega, OMEGA, lv, positionAngleType, FramesFactory.getEME2000(), initDate, zero.newInstance(Constants.EIGEN5C_EARTH_MU)); final FieldSpacecraftState initialState = @@ -236,6 +238,7 @@ private > void doRoughBehaviourField(final Fie integrator.setInitialStepSize(0.1); final FieldNumericalPropagator propagator = new FieldNumericalPropagator<>(field, integrator); propagator.setOrbitType(initOrbit.getType()); + propagator.setPositionAngleType(positionAngleType); propagator.setInitialState(initialState); propagator.setAttitudeProvider(law); propagator.addForceModel(maneuver); diff --git a/src/test/java/org/orekit/forces/radiation/AbstractLightFluxModelTest.java b/src/test/java/org/orekit/forces/radiation/AbstractLightFluxModelTest.java new file mode 100644 index 0000000000..9bd5e6d187 --- /dev/null +++ b/src/test/java/org/orekit/forces/radiation/AbstractLightFluxModelTest.java @@ -0,0 +1,143 @@ +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.FieldSpacecraftState; +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.ExtendedPVCoordinatesProvider; +import org.orekit.utils.ExtendedPositionProvider; + +import java.util.Collections; +import java.util.List; + +class AbstractLightFluxModelTest { + + @Test + void testGetLightFluxVectorWithZeroLightingRatio() { + // GIVEN + final SpacecraftState mockedState = Mockito.mock(SpacecraftState.class); + final AbstractLightFluxModel mockedFluxModel = Mockito.mock(AbstractLightFluxModel.class); + Mockito.when(mockedFluxModel.getLightingRatio(Mockito.any(Vector3D.class), Mockito.any(Vector3D.class))).thenReturn(0.); + Mockito.when(mockedFluxModel.getLightFluxVector(mockedState)).thenCallRealMethod(); + // WHEN + final Vector3D actualFluxVector = mockedFluxModel.getLightFluxVector(mockedState); + // THEN + Assertions.assertEquals(Vector3D.ZERO, actualFluxVector); + } + + @Test + void testGetLightFluxVector() { + // GIVEN + final Vector3D position = new Vector3D(1.0, 2.0, 3.0); + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final Frame mockedFrame = Mockito.mock(Frame.class); + final SpacecraftState mockedState = mockState(position, date, mockedFrame); + final ExtendedPVCoordinatesProvider mockedProvider = Mockito.mock(ExtendedPVCoordinatesProvider.class); + final Vector3D sunPosition = Vector3D.PLUS_K; + Mockito.when(mockedProvider.getPosition(date, mockedFrame)).thenReturn(sunPosition); + final TestLightFluxModel testLightFluxModel = new TestLightFluxModel(mockedProvider); + // WHEN + final Vector3D actualFluxVector = testLightFluxModel.getLightFluxVector(mockedState); + // THEN + final Vector3D expectedFluxVector = position.subtract(sunPosition).normalize(); + Assertions.assertEquals(expectedFluxVector, actualFluxVector); + } + + private SpacecraftState mockState(final Vector3D position, final AbsoluteDate date, final Frame frame) { + final SpacecraftState mockedState = Mockito.mock(SpacecraftState.class); + Mockito.when(mockedState.getFrame()).thenReturn(frame); + Mockito.when(mockedState.getPosition()).thenReturn(position); + Mockito.when(mockedState.getDate()).thenReturn(date); + return mockedState; + } + + @Test + void testGetLightFluxVectorField() { + // 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 mockedState = 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 FieldVector3D actualFluxVector = testLightFluxModel.getLightFluxVector(mockedState); + // THEN + final FieldVector3D expectedFluxVector = position.subtract(sunPosition).normalize(); + Assertions.assertEquals(expectedFluxVector, actualFluxVector); + } + + @SuppressWarnings("unchecked") + private FieldSpacecraftState mockState(final FieldVector3D position, final FieldAbsoluteDate date, + final Frame frame) { + final FieldSpacecraftState mockedState = Mockito.mock(FieldSpacecraftState.class); + Mockito.when(mockedState.getFrame()).thenReturn(frame); + Mockito.when(mockedState.getPosition()).thenReturn(position); + Mockito.when(mockedState.getDate()).thenReturn(date); + return mockedState; + } + + @Test + void testConstructor() { + // GIVEN + final ExtendedPVCoordinatesProvider mockedProvider = Mockito.mock(ExtendedPVCoordinatesProvider.class); + final TestLightFluxModel testLightFluxModel = new TestLightFluxModel(mockedProvider); + // WHEN + final ExtendedPositionProvider actualProvider = testLightFluxModel.getOccultedBody(); + // THEN + Assertions.assertEquals(mockedProvider, actualProvider); + } + + private static class TestLightFluxModel extends AbstractLightFluxModel { + + public TestLightFluxModel(final ExtendedPVCoordinatesProvider occultedBody) { + super(occultedBody); + } + + @Override + protected Vector3D getUnoccultedFluxVector(Vector3D relativePosition) { + return relativePosition.normalize(); + } + + @Override + protected > FieldVector3D getUnoccultedFluxVector(FieldVector3D relativePosition) { + return relativePosition.normalize(); + } + + @Override + protected double getLightingRatio(Vector3D position, Vector3D occultedBodyPosition) { + return 1.; + } + + @Override + protected > T getLightingRatio(FieldVector3D position, FieldVector3D occultedBodyPosition) { + return position.getX().getField().getOne(); + } + + @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/CylindricallyShadowedLightFluxModelTest.java b/src/test/java/org/orekit/forces/radiation/CylindricallyShadowedLightFluxModelTest.java new file mode 100644 index 0000000000..7d115851ad --- /dev/null +++ b/src/test/java/org/orekit/forces/radiation/CylindricallyShadowedLightFluxModelTest.java @@ -0,0 +1,186 @@ +/* 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.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.FastMath; +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.FieldSpacecraftState; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.events.*; +import org.orekit.propagation.events.handlers.EventHandler; +import org.orekit.propagation.events.handlers.FieldStopOnEvent; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.ExtendedPVCoordinatesProvider; + +import java.util.List; + +class CylindricallyShadowedLightFluxModelTest { + + @Test + void testGetLightingRatio() { + // GIVEN + final double occultingBodyRadius = 0.5; + final Vector3D sunPosition = Vector3D.PLUS_I.scalarMultiply(10.); + final ExtendedPVCoordinatesProvider sun = mockProvider(sunPosition); + final CylindricallyShadowedLightFluxModel model = new CylindricallyShadowedLightFluxModel(Double.NaN, sun, occultingBodyRadius); + final CylindricalShadowEclipseDetector detector = new CylindricalShadowEclipseDetector(sun, + model.getOccultingBodyRadius(), Mockito.mock(EventHandler.class)); + // WHEN & THEN + final int size = 100; + for (int i = 0; i < size; i++) { + final double angle = i * FastMath.PI / size; + final Vector3D position = new Vector3D(angle, 0.); + final double ratio = model.getLightingRatio(position, sunPosition); + final SpacecraftState state = mockState(position); + final double g = detector.g(state); + if (g < 0.) { + Assertions.assertEquals(0., ratio); + } else { + Assertions.assertEquals(1., ratio); + } + } + } + + private ExtendedPVCoordinatesProvider mockProvider(final Vector3D sunPosition) { + final ExtendedPVCoordinatesProvider mockedProvider = Mockito.mock(ExtendedPVCoordinatesProvider.class); + Mockito.when(mockedProvider.getPosition(Mockito.any(AbsoluteDate.class), Mockito.any(Frame.class))) + .thenReturn(sunPosition); + return mockedProvider; + } + + private SpacecraftState mockState(final Vector3D position) { + final SpacecraftState mockedState = Mockito.mock(SpacecraftState.class); + Mockito.when(mockedState.getPosition()).thenReturn(position); + Mockito.when(mockedState.getDate()).thenReturn(AbsoluteDate.ARBITRARY_EPOCH); + Mockito.when(mockedState.getFrame()).thenReturn(Mockito.mock(Frame.class)); + return mockedState; + } + + @Test + void testFieldGetLightingRatio() { + // 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 FieldCylindricalShadowEclipseDetector detector = new FieldCylindricalShadowEclipseDetector<>(sun, + new Complex(model.getOccultingBodyRadius()), new FieldStopOnEvent<>()); + // WHEN & THEN + final int size = 100; + for (int i = 0; i < size; i++) { + final double angle = i * FastMath.PI / size; + final FieldVector3D position = new FieldVector3D<>(field, new Vector3D(angle, 0.)); + final double ratio = model.getLightingRatio(position, sunPosition).getReal(); + final FieldSpacecraftState state = mockFieldState(position); + final double g = detector.g(state).getReal(); + if (g < 0.) { + Assertions.assertEquals(0., ratio); + } else { + Assertions.assertEquals(1., ratio); + } + } + } + + @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); + Mockito.when(mockedProvider.getPosition(Mockito.any(FieldAbsoluteDate.class), Mockito.any(Frame.class))) + .thenReturn(sunPosition); + return mockedProvider; + } + + @SuppressWarnings("unchecked") + private FieldSpacecraftState mockFieldState(final FieldVector3D position) { + final FieldSpacecraftState mockedState = Mockito.mock(FieldSpacecraftState.class); + Mockito.when(mockedState.getPosition()).thenReturn(position); + Mockito.when(mockedState.getDate()).thenReturn(Mockito.mock(FieldAbsoluteDate.class)); + Mockito.when(mockedState.getFrame()).thenReturn(Mockito.mock(Frame.class)); + return mockedState; + } + + @Test + void testGetEclipseConditionsDetector() { + // GIVEN + final CylindricallyShadowedLightFluxModel model = Mockito.mock(CylindricallyShadowedLightFluxModel.class); + Mockito.when(model.getOccultingBodyRadius()).thenReturn(1.); + Mockito.when(model.getEclipseConditionsDetector()).thenCallRealMethod(); + // WHEN + final List detectors = model.getEclipseConditionsDetector(); + // THEN + Assertions.assertEquals(1, detectors.size()); + final EventDetector detector = detectors.get(0); + Assertions.assertInstanceOf(CylindricalShadowEclipseDetector.class, detector); + final Action action = detector.getHandler().eventOccurred(Mockito.mock(SpacecraftState.class), detector, false); + Assertions.assertEquals(Action.RESET_DERIVATIVES, action); + } + + @Test + void testGetFieldEclipseConditionsDetector() { + // GIVEN + final ComplexField field = ComplexField.getInstance(); + final CylindricallyShadowedLightFluxModel model = Mockito.mock(CylindricallyShadowedLightFluxModel.class); + Mockito.when(model.getOccultingBodyRadius()).thenReturn(1.); + Mockito.when(model.getFieldEclipseConditionsDetector(field)).thenCallRealMethod(); + Mockito.when(model.getEclipseConditionsDetector()).thenCallRealMethod(); + // WHEN + final List> fieldEventDetectors = model.getFieldEclipseConditionsDetector(field); + // THEN + final List eventDetectors = model.getEclipseConditionsDetector(); + Assertions.assertEquals(eventDetectors.size(), fieldEventDetectors.size()); + Assertions.assertInstanceOf(FieldCylindricalShadowEclipseDetector.class, fieldEventDetectors.get(0)); + final FieldCylindricalShadowEclipseDetector fieldShadowDetector = (FieldCylindricalShadowEclipseDetector) fieldEventDetectors.get(0); + final CylindricalShadowEclipseDetector shadowDetector = (CylindricalShadowEclipseDetector) eventDetectors.get(0); + Assertions.assertEquals(shadowDetector.getThreshold(), fieldShadowDetector.getThreshold().getReal()); + compareActions(shadowDetector, fieldShadowDetector); + } + + @SuppressWarnings("unchecked") + private void compareActions(final EventDetector eventDetector, final FieldEventDetector fieldEventDetector) { + final boolean isIncreasing = false; + final Action expectedAction = eventDetector.getHandler().eventOccurred(Mockito.mock(SpacecraftState.class), eventDetector, isIncreasing); + final Action actualAction = fieldEventDetector.getHandler().eventOccurred(Mockito.mock(FieldSpacecraftState.class), fieldEventDetector, isIncreasing); + Assertions.assertEquals(expectedAction, actualAction); + } + +} diff --git a/src/test/java/org/orekit/forces/radiation/KnockeRediffusedForceModelTest.java b/src/test/java/org/orekit/forces/radiation/KnockeRediffusedForceModelTest.java index bc649f9729..1e1c6333a0 100644 --- a/src/test/java/org/orekit/forces/radiation/KnockeRediffusedForceModelTest.java +++ b/src/test/java/org/orekit/forces/radiation/KnockeRediffusedForceModelTest.java @@ -62,7 +62,7 @@ * However, the complete reproduction of the LAGEOS-1 test case is much too long for it to be implemented in test class. * Then, only */ -public class KnockeRediffusedForceModelTest extends AbstractForceModelTest{ +class KnockeRediffusedForceModelTest extends AbstractForceModelTest{ @BeforeEach public void setUp() { @@ -71,7 +71,7 @@ public void setUp() { @Test - public void testJacobianVsFiniteDifferences() { + void testJacobianVsFiniteDifferences() { // initialization AbsoluteDate date = new AbsoluteDate(new DateComponents(2003, 03, 01), @@ -103,7 +103,7 @@ public void testJacobianVsFiniteDifferences() { } @Test - public void testParameterIsotropicSingle() { + void testParameterIsotropicSingle() { final Vector3D pos = new Vector3D(6.46885878304673824e+06, -1.88050918456274318e+06, -1.32931592294715829e+04); final Vector3D vel = new Vector3D(2.14718074509906819e+03, 7.38239351251748485e+03, -1.14097953925384523e+01); @@ -130,7 +130,7 @@ public void testParameterIsotropicSingle() { } @Test - public void testGlobalStateJacobianIsotropicSingle() + void testGlobalStateJacobianIsotropicSingle() { // initialization @@ -171,7 +171,7 @@ public void testGlobalStateJacobianIsotropicSingle() } @Test - public void testRealField() { + void testRealField() { // Initial field Keplerian orbit // The variables are the six orbital parameters @@ -236,13 +236,13 @@ public void testRealField() { // Do the test checkRealFieldPropagation(FKO, PositionAngleType.MEAN, 300., NP, FNP, - 1.0e-30, 1.3e-8, 6.7e-11, 1.4e-10, + 1.0e-15, 1.3e-8, 6.7e-11, 1.4e-10, 1, false); } @Test - public void testRealFieldGradient() { + void testRealFieldGradient() { // Initial field Keplerian orbit // The variables are the six orbital parameters @@ -306,17 +306,17 @@ public void testRealFieldGradient() { // Do the test checkRealFieldPropagationGradient(FKO, PositionAngleType.MEAN, 300., NP, FNP, - 1.0e-30, 1.3e-2, 9.6e-5, 1.4e-4, + 1.0e-15, 1.3e-2, 9.6e-5, 1.4e-4, 1, false); } - /** Roughtly compare Knocke model accelerations against results from "EARTH RADIATION PRESSURE EFFECTS ON SATELLITES", + /** Roughly compare Knocke model accelerations against results from "EARTH RADIATION PRESSURE EFFECTS ON SATELLITES", * 1988, by P. C. Knocke, J. C. Ries, and B. D. Tapley. * The case is as close as possible from what it might be in the paper. Orbit and date have been artifically set so that the angle between * the orbital plan and Earth-Sun direction is almost equal to zero. */ @Test - public void testRoughtAcceleration() { + void testRoughAcceleration() { // LAGEOS-1 final double mass = 406.9; diff --git a/src/test/java/org/orekit/forces/radiation/RadiationPressureModelTest.java b/src/test/java/org/orekit/forces/radiation/RadiationPressureModelTest.java new file mode 100644 index 0000000000..6bda5dcfdf --- /dev/null +++ b/src/test/java/org/orekit/forces/radiation/RadiationPressureModelTest.java @@ -0,0 +1,167 @@ +package org.orekit.forces.radiation; + +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.AbstractIntegrator; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +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.frames.FramesFactory; +import org.orekit.orbits.EquinoctialOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.conversion.DormandPrince54IntegratorBuilder; +import org.orekit.propagation.events.EventDetector; +import org.orekit.propagation.events.FieldEventDetector; +import org.orekit.propagation.numerical.NumericalPropagator; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.Constants; +import org.orekit.utils.ExtendedPVCoordinatesProvider; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +class RadiationPressureModelTest { + + @Test + void testAcceleration() { + // GIVEN + final SpacecraftState state = createState(); + final ComplexField field = ComplexField.getInstance(); + final FieldSpacecraftState fieldState = new FieldSpacecraftState<>(field, state); + final IsotropicRadiationSingleCoefficient radiationSingleCoefficient = new IsotropicRadiationSingleCoefficient(1., 2.); + final LightFluxModel mockedFluxModel = Mockito.mock(LightFluxModel.class); + final Vector3D fluxVector = new Vector3D(1., 2., 3.); + Mockito.when(mockedFluxModel.getLightFluxVector(state)).thenReturn(fluxVector); + Mockito.when(mockedFluxModel.getLightFluxVector(fieldState)).thenReturn(new FieldVector3D<>(field, fluxVector)); + final RadiationPressureModel forceModel = new RadiationPressureModel(mockedFluxModel, + radiationSingleCoefficient); + // WHEN + final FieldVector3D fieldAcceleration = forceModel.acceleration(fieldState, forceModel.getParameters(field)); + // THEN + final Vector3D expectedAcceleration = forceModel.acceleration(state, forceModel.getParameters()); + Assertions.assertEquals(expectedAcceleration, fieldAcceleration.toVector3D()); + } + + @Test + void testDependsOnPositionOnlyTrue() { + // GIVEN + final IsotropicRadiationSingleCoefficient mockedIsotropicRadiationSingleCoefficient = Mockito.mock(IsotropicRadiationSingleCoefficient.class); + final LightFluxModel mockedFluxModel = Mockito.mock(LightFluxModel.class); + final RadiationPressureModel forceModel = new RadiationPressureModel(mockedFluxModel, + mockedIsotropicRadiationSingleCoefficient); + // WHEN + final boolean dependsOnPositionOnly = forceModel.dependsOnPositionOnly(); + // THEN + Assertions.assertTrue(dependsOnPositionOnly); + } + + @Test + void testDependsOnPositionOnlyFalse() { + // GIVEN + final BoxAndSolarArraySpacecraft mockedBoxAndSolarArraySpacecraft = Mockito.mock(BoxAndSolarArraySpacecraft.class); + final LightFluxModel mockedFluxModel = Mockito.mock(LightFluxModel.class); + final RadiationPressureModel forceModel = new RadiationPressureModel(mockedFluxModel, + mockedBoxAndSolarArraySpacecraft); + // WHEN + final boolean dependsOnPositionOnly = forceModel.dependsOnPositionOnly(); + // THEN + Assertions.assertFalse(dependsOnPositionOnly); + } + + @Test + void testGetEventDetectors() { + // GIVEN + final RadiationSensitive mockedRadiationSensitive = Mockito.mock(RadiationSensitive.class); + final LightFluxModel mockedFluxModel = Mockito.mock(LightFluxModel.class); + final List eclipseDetectors = new ArrayList<>(); + eclipseDetectors.add(Mockito.mock(EventDetector.class)); + Mockito.when(mockedFluxModel.getEclipseConditionsDetector()).thenReturn(eclipseDetectors); + final RadiationPressureModel forceModel = new RadiationPressureModel(mockedFluxModel, + mockedRadiationSensitive); + // WHEN + final Stream detectors = forceModel.getEventDetectors(); + // THEN + Assertions.assertEquals(eclipseDetectors.size(), detectors.toArray().length); + } + + @SuppressWarnings("unchecked") + @Test + void testGetFieldEventDetectors() { + // GIVEN + final RadiationSensitive mockedRadiationSensitive = Mockito.mock(RadiationSensitive.class); + final LightFluxModel mockedFluxModel = Mockito.mock(LightFluxModel.class); + final List> eclipseDetectors = new ArrayList<>(); + eclipseDetectors.add(Mockito.mock(FieldEventDetector.class)); + final ComplexField field = ComplexField.getInstance(); + Mockito.when(mockedFluxModel.getFieldEclipseConditionsDetector(field)).thenReturn(eclipseDetectors); + final RadiationPressureModel forceModel = new RadiationPressureModel(mockedFluxModel, + mockedRadiationSensitive); + // WHEN + final Stream detectors = forceModel.getFieldEventDetectors(field); + // THEN + Assertions.assertEquals(eclipseDetectors.size(), detectors.toArray().length); + } + + @Test + void testPropagation() { + // GIVEN + Utils.setDataRoot("regular-data"); + final IsotropicRadiationSingleCoefficient isotropicRadiationSingleCoefficient = new IsotropicRadiationSingleCoefficient(10., 0.5); + final CylindricallyShadowedLightFluxModel lightFluxModel = new CylindricallyShadowedLightFluxModel(CelestialBodyFactory.getSun(), + Constants.EGM96_EARTH_EQUATORIAL_RADIUS); + final RadiationPressureModel forceModel = new RadiationPressureModel(lightFluxModel, + isotropicRadiationSingleCoefficient); + final NumericalPropagator propagator = createPropagator(); + 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 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 SolarRadiationPressure solarRadiationPressure = new SolarRadiationPressure((ExtendedPVCoordinatesProvider) lightFluxModel.getOccultedBody(), + new OneAxisEllipsoid(lightFluxModel.getOccultingBodyRadius(), 0., FramesFactory.getGTOD(false)), + radiationPressureModel.getRadiationSensitive()); + propagator.addForceModel(solarRadiationPressure); + return propagator.propagate(terminalDate); + } + + private SpacecraftState createState() { + return new SpacecraftState(createOrbit()); + } + + private Orbit createOrbit() { + return new EquinoctialOrbit(42000e3, 0., 0., 0., 0., 0., PositionAngleType.ECCENTRIC, FramesFactory.getGCRF(), + AbsoluteDate.ARBITRARY_EPOCH, Constants.EGM96_EARTH_MU); + } + + private NumericalPropagator createPropagator() { + final SpacecraftState initialState = createState(); + final Orbit initialOrbit = initialState.getOrbit(); + final DormandPrince54IntegratorBuilder 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()); + propagator.setInitialState(initialState); + return propagator; + } + +} diff --git a/src/test/java/org/orekit/forces/radiation/SolarRadiationPressureTest.java b/src/test/java/org/orekit/forces/radiation/SolarRadiationPressureTest.java index dd71691a53..fd6852e5a8 100644 --- a/src/test/java/org/orekit/forces/radiation/SolarRadiationPressureTest.java +++ b/src/test/java/org/orekit/forces/radiation/SolarRadiationPressureTest.java @@ -37,6 +37,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.attitudes.LofOffset; import org.orekit.bodies.CelestialBody; @@ -157,7 +158,7 @@ protected FieldVector3D accelerationDerivativesGradient(final ForceMod } @Test - public void testLightingInterplanetary() throws ParseException { + void testLightingInterplanetary() throws ParseException { // Initialization AbsoluteDate date = new AbsoluteDate(new DateComponents(1970, 3, 21), new TimeComponents(13, 59, 27.816), @@ -168,7 +169,6 @@ public void testLightingInterplanetary() throws ParseException { SolarRadiationPressure srp = new SolarRadiationPressure(sun, new OneAxisEllipsoid(1.0e-10, 0.0, FramesFactory.getICRF()), new IsotropicRadiationClassicalConvention(50.0, 0.5, 0.5)); - Assertions.assertFalse(srp.dependsOnPositionOnly()); // Test lighting ratio method with double Assertions.assertEquals(1.0, @@ -184,7 +184,7 @@ public void testLightingInterplanetary() throws ParseException { } @Test - public void testLighting() throws ParseException { + void testLighting() throws ParseException { // Given AbsoluteDate date = new AbsoluteDate(new DateComponents(1970, 3, 21), @@ -233,7 +233,7 @@ public void testLighting() throws ParseException { } @Test - public void testGlobalStateJacobianIsotropicSingle() { + void testGlobalStateJacobianIsotropicSingle() { // initialization AbsoluteDate date = new AbsoluteDate(new DateComponents(2003, 03, 01), @@ -267,7 +267,7 @@ public void testGlobalStateJacobianIsotropicSingle() { } @Test - public void testLocalJacobianIsotropicClassicalVs80Implementation() { + void testLocalJacobianIsotropicClassicalVs80Implementation() { // initialization AbsoluteDate date = new AbsoluteDate(new DateComponents(2003, 03, 01), @@ -293,7 +293,7 @@ public void testLocalJacobianIsotropicClassicalVs80Implementation() { } @Test - public void testLocalJacobianIsotropicClassicalVs80ImplementationGradient() { + void testLocalJacobianIsotropicClassicalVs80ImplementationGradient() { // initialization AbsoluteDate date = new AbsoluteDate(new DateComponents(2003, 03, 01), @@ -319,39 +319,39 @@ public void testLocalJacobianIsotropicClassicalVs80ImplementationGradient() { } @Test - public void testLocalJacobianIsotropicClassicalVsFiniteDifferencesFullLight() { + void testLocalJacobianIsotropicClassicalVsFiniteDifferencesFullLight() { // here, lighting ratio is exactly 1 for all points used for finite differences doTestLocalJacobianIsotropicClassicalVsFiniteDifferences(250.0, 1000.0, 3.0e-8, false); } @Test - public void testLocalJacobianIsotropicClassicalVsFiniteDifferencesGradientFullLight() { + void testLocalJacobianIsotropicClassicalVsFiniteDifferencesGradientFullLight() { // here, lighting ratio is exactly 1 for all points used for finite differences doTestLocalJacobianIsotropicClassicalVsFiniteDifferencesGradient(250.0, 1000.0, 3.0e-8, false); } @Test - public void testLocalJacobianIsotropicClassicalVsFiniteDifferencesPenumbra() { + void testLocalJacobianIsotropicClassicalVsFiniteDifferencesPenumbra() { // here, lighting ratio is about 0.57, // and remains strictly between 0 and 1 for all points used for finite differences doTestLocalJacobianIsotropicClassicalVsFiniteDifferences(275.5, 100.0, 8.0e-7, false); } @Test - public void testLocalJacobianIsotropicClassicalVsFiniteDifferencesGradientPenumbra() { + void testLocalJacobianIsotropicClassicalVsFiniteDifferencesGradientPenumbra() { // here, lighting ratio is about 0.57, // and remains strictly between 0 and 1 for all points used for finite differences doTestLocalJacobianIsotropicClassicalVsFiniteDifferencesGradient(275.5, 100.0, 8.0e-7, false); } @Test - public void testLocalJacobianIsotropicClassicalVsFiniteDifferencesEclipse() { + void testLocalJacobianIsotropicClassicalVsFiniteDifferencesEclipse() { // here, lighting ratio is exactly 0 for all points used for finite differences doTestLocalJacobianIsotropicClassicalVsFiniteDifferences(300.0, 1000.0, 1.0e-50, false); } @Test - public void testLocalJacobianIsotropicClassicalVsFiniteDifferencesGradientEclipse() { + void testLocalJacobianIsotropicClassicalVsFiniteDifferencesGradientEclipse() { // here, lighting ratio is exactly 0 for all points used for finite differences doTestLocalJacobianIsotropicClassicalVsFiniteDifferencesGradient(300.0, 1000.0, 1.0e-50, false); } @@ -409,7 +409,7 @@ private void doTestLocalJacobianIsotropicClassicalVsFiniteDifferencesGradient(do } @Test - public void testGlobalStateJacobianIsotropicClassical() { + void testGlobalStateJacobianIsotropicClassical() { // initialization AbsoluteDate date = new AbsoluteDate(new DateComponents(2003, 03, 01), @@ -443,7 +443,7 @@ public void testGlobalStateJacobianIsotropicClassical() { } @Test - public void testGlobalStateJacobianIsotropicCnes() { + void testGlobalStateJacobianIsotropicCnes() { // initialization AbsoluteDate date = new AbsoluteDate(new DateComponents(2003, 03, 01), @@ -477,7 +477,7 @@ public void testGlobalStateJacobianIsotropicCnes() { } @Test - public void testParameterDerivativeBox() { + void testParameterDerivativeBox() { final Vector3D pos = new Vector3D(6.46885878304673824e+06, -1.88050918456274318e+06, -1.32931592294715829e+04); final Vector3D vel = new Vector3D(2.14718074509906819e+03, 7.38239351251748485e+03, -1.14097953925384523e+01); @@ -500,7 +500,7 @@ public void testParameterDerivativeBox() { } @Test - public void testParameterDerivativeGradientBox() { + void testParameterDerivativeGradientBox() { final Vector3D pos = new Vector3D(6.46885878304673824e+06, -1.88050918456274318e+06, -1.32931592294715829e+04); final Vector3D vel = new Vector3D(2.14718074509906819e+03, 7.38239351251748485e+03, -1.14097953925384523e+01); @@ -523,7 +523,7 @@ public void testParameterDerivativeGradientBox() { } @Test - public void testGlobalStateJacobianBox() { + void testGlobalStateJacobianBox() { // initialization AbsoluteDate date = new AbsoluteDate(new DateComponents(2003, 03, 01), @@ -558,7 +558,7 @@ public void testGlobalStateJacobianBox() { } @Test - public void testRoughOrbitalModifs() { + void testRoughOrbitalModifs() { // initialization AbsoluteDate date = new AbsoluteDate(new DateComponents(1970, 7, 1), @@ -767,7 +767,7 @@ public void RealFieldExpectErrorTest() { } @Test - public void testFlatteningLEO() { + void testFlatteningLEO() { GravityFieldFactory.addPotentialCoefficientsReader(new ICGEMFormatReader("eigen-6s-truncated", false)); NormalizedSphericalHarmonicsProvider gravityField = GravityFieldFactory.getNormalizedProvider(20, 20); @@ -808,6 +808,7 @@ private NumericalPropagator createLeoPropagator(final CelestialBody sun, final C final NumericalPropagator propagator = new NumericalPropagator(new DormandPrince853Integrator(1.0e-9, 60.0, tol[0], tol[1])); propagator.setOrbitType(OrbitType.CIRCULAR); + propagator.setPositionAngleType(PositionAngleType.TRUE); propagator.addForceModel(new HolmesFeatherstoneAttractionModel(earth.getBodyFrame(), gravityField)); propagator.addForceModel(new ThirdBodyAttraction(sun)); propagator.addForceModel(new ThirdBodyAttraction(moon)); @@ -853,13 +854,28 @@ public void finish(final List finalStates) { } + @Test + void testDependsOnlyOnPosition() { + // GIVEN + final IsotropicRadiationSingleCoefficient mockedSpacecraft = Mockito.mock(IsotropicRadiationSingleCoefficient.class); + final SolarRadiationPressure radiationPressure = new SolarRadiationPressure(null, null, mockedSpacecraft); + // WHEN + final boolean actualValue = radiationPressure.dependsOnPositionOnly(); + // THEN + Assertions.assertEquals(mockedSpacecraft, radiationPressure.getRadiationSensitiveSpacecraft()); + Assertions.assertTrue(actualValue); + final BoxAndSolarArraySpacecraft mockedBoxSpacecraft = Mockito.mock(BoxAndSolarArraySpacecraft.class); + final SolarRadiationPressure boxRadiationPressure = new SolarRadiationPressure(null, null, mockedBoxSpacecraft); + Assertions.assertFalse(boxRadiationPressure.dependsOnPositionOnly()); + } + /** Testing if eclipses due to Moon are considered. * Reference values are presented in "A Study of Solar Radiation Pressure acting on GPS Satellites" * written by Laurent Olivier Froideval in 2009. * Modifications of the step handler and time span able to print lighting ratios other a year and get a reference like graph. */ @Test - public void testMoonPenumbra() { + void testMoonPenumbra() { final AbsoluteDate date = new AbsoluteDate(2007, 1, 19, 5, 0, 0, TimeScalesFactory.getGPS()); final Vector3D p = new Vector3D(12538484.957505366, 15515522.98001655, -17884023.51839292); final Vector3D v = new Vector3D(-3366.9009055533616, 769.5389825219049, -1679.3840677789601); @@ -869,7 +885,7 @@ public void testMoonPenumbra() { } @Test - public void testEarthPenumbraOnly() { + void testEarthPenumbraOnly() { final AbsoluteDate date = new AbsoluteDate(2007, 3, 13, 17, 14, 0, TimeScalesFactory.getGPS()); final Vector3D p = new Vector3D(-26168647.4977583, -1516554.3304749255, -3206794.210706205); final Vector3D v = new Vector3D(-213.65557094060222, -2377.3633988328584, 3079.4740070013495); @@ -879,7 +895,7 @@ public void testEarthPenumbraOnly() { } @Test - public void testEarthPenumbraAndUmbra() { + void testEarthPenumbraAndUmbra() { final AbsoluteDate date = new AbsoluteDate(2007, 3, 14, 5, 8, 0, TimeScalesFactory.getGPS()); final Vector3D p = new Vector3D(-26101379.998276696, -947280.678355501, -3940992.754483608); final Vector3D v = new Vector3D(-348.8911736753223, -2383.738528546711, 3060.9815784341567); @@ -1042,7 +1058,7 @@ else if (isInMoonPenumbra || isInEarthPenumbra) { * Modifications of the step handler and time span able to print lighting ratios other a year and get a reference like graph. */ @Test - public void testFieldMoonPenumbra() { + void testFieldMoonPenumbra() { Field field = Binary64Field.getInstance(); final FieldAbsoluteDate date = new FieldAbsoluteDate<>(field, 2007, 1, 19, 5, 0, 0, TimeScalesFactory.getGPS()); final FieldVector3D p = new FieldVector3D<>(field, new Vector3D(12538484.957505366, 15515522.98001655, -17884023.51839292)); @@ -1055,7 +1071,7 @@ public void testFieldMoonPenumbra() { } @Test - public void testFieldEarthPenumbraOnly() { + void testFieldEarthPenumbraOnly() { Field field = Binary64Field.getInstance(); final FieldAbsoluteDate date = new FieldAbsoluteDate<>(field, 2007, 3, 13, 17, 14, 0, TimeScalesFactory.getGPS()); final FieldVector3D p = new FieldVector3D<>(field, new Vector3D(-26168647.4977583, -1516554.3304749255, -3206794.210706205)); @@ -1068,7 +1084,7 @@ public void testFieldEarthPenumbraOnly() { } @Test - public void testFieldEarthPenumbraAndUmbra() { + void testFieldEarthPenumbraAndUmbra() { Field field = Binary64Field.getInstance(); final FieldAbsoluteDate date = new FieldAbsoluteDate<>(field, 2007, 3, 14, 5, 8, 0, TimeScalesFactory.getGPS()); final FieldVector3D p = new FieldVector3D<>(field, new Vector3D(-26101379.998276696, -947280.678355501, -3940992.754483608)); diff --git a/src/test/java/org/orekit/frames/FieldKinematicTransformTest.java b/src/test/java/org/orekit/frames/FieldKinematicTransformTest.java new file mode 100644 index 0000000000..82e4243433 --- /dev/null +++ b/src/test/java/org/orekit/frames/FieldKinematicTransformTest.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.frames; + +import org.hipparchus.Field; +import org.hipparchus.complex.Complex; +import org.hipparchus.complex.ComplexField; +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.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldPVCoordinates; +import org.orekit.utils.PVCoordinates; +import org.orekit.utils.TimeStampedFieldPVCoordinates; + +class FieldKinematicTransformTest { + + @Test + void testOf() { + // GIVEN + final KinematicTransform kinematicTransform = createArbitraryKineticTransform(); + // WHEn + final FieldKinematicTransform fieldKinematicTransform = FieldKinematicTransform.of(ComplexField.getInstance(), + kinematicTransform); + // THEN + Assertions.assertEquals(fieldKinematicTransform.getDate(), kinematicTransform.getDate()); + Assertions.assertEquals(fieldKinematicTransform.getTranslation().toVector3D(), kinematicTransform.getTranslation()); + Assertions.assertEquals(fieldKinematicTransform.getVelocity().toVector3D(), kinematicTransform.getVelocity()); + Assertions.assertEquals(0., Rotation.distance(fieldKinematicTransform.getRotation().toRotation(), + kinematicTransform.getRotation())); + Assertions.assertEquals(fieldKinematicTransform.getRotationRate().toVector3D(), + kinematicTransform.getRotationRate()); + } + + private KinematicTransform createArbitraryKineticTransform() { + return KinematicTransform.of(AbsoluteDate.ARBITRARY_EPOCH, + new PVCoordinates(Vector3D.MINUS_I, Vector3D.PLUS_J), + new Rotation(Vector3D.MINUS_K, Vector3D.PLUS_I), + Vector3D.MINUS_J); + } + + @Test + void testGetInverse() { + // GIVEN + final FieldKinematicTransform fieldKinematicTransform = createArbitraryFieldKineticTransform(); + // WHEn + final FieldKinematicTransform fieldInverse = fieldKinematicTransform.getInverse(); + // THEN + final KinematicTransform kinematicTransform = createKinematicTransform(fieldKinematicTransform); + final KinematicTransform inverse = kinematicTransform.getInverse(); + Assertions.assertEquals(inverse.getDate(), fieldInverse.getDate()); + Assertions.assertEquals(inverse.getTranslation(), fieldInverse.getTranslation().toVector3D()); + Assertions.assertEquals(inverse.getVelocity(), fieldInverse.getVelocity().toVector3D()); + Assertions.assertEquals(0., Rotation.distance(inverse.getRotation(), + fieldInverse.getRotation().toRotation())); + Assertions.assertEquals(inverse.getRotationRate(), fieldInverse.getRotationRate().toVector3D()); + } + + @Test + void testCompositeVelocity() { + // GIVEN + final FieldKinematicTransform fieldKinematicTransform = createArbitraryFieldKineticTransform(); + final Field field = fieldKinematicTransform.getFieldDate().getField(); + // WHEN + final FieldVector3D actualCompositeVelocity = FieldKinematicTransform + .compositeVelocity(fieldKinematicTransform, FieldKinematicTransform.getIdentity(field)); + // THEN + final KinematicTransform kinematicTransform = createKinematicTransform(fieldKinematicTransform); + final Vector3D expectedCompositeVelocity = kinematicTransform.getVelocity(); + Assertions.assertEquals(expectedCompositeVelocity, actualCompositeVelocity.toVector3D()); + } + + @Test + void testCompositeRotationRate() { + // GIVEN + final FieldKinematicTransform fieldKinematicTransform = createArbitraryFieldKineticTransform(); + final Field field = fieldKinematicTransform.getFieldDate().getField(); + // WHEN + final FieldVector3D actualCompositeRotationRate = FieldKinematicTransform + .compositeRotationRate(fieldKinematicTransform, FieldKinematicTransform.getIdentity(field)); + // THEN + final KinematicTransform kinematicTransform = createKinematicTransform(fieldKinematicTransform); + final Vector3D expectedCompositeRotationRate = kinematicTransform.getRotationRate(); + Assertions.assertEquals(expectedCompositeRotationRate, actualCompositeRotationRate.toVector3D()); + } + + @Test + void testTransformPVOnly(){ + // GIVEN + final FieldKinematicTransform fieldKinematicTransform = createArbitraryFieldKineticTransform(); + final FieldPVCoordinates pvCoordinates = createFieldPV(); + // WHEN + final FieldPVCoordinates convertedPV = fieldKinematicTransform.transformOnlyPV(pvCoordinates); + // THEN + final KinematicTransform kinematicTransform = createKinematicTransform(fieldKinematicTransform); + final PVCoordinates expectedPV = kinematicTransform.transformOnlyPV(pvCoordinates + .toPVCoordinates()); + comparePVCoordinates(expectedPV, convertedPV.toPVCoordinates()); + } + + @Test + void testTransformTimeStampedPVCoordinatesWithoutA() { + // GIVEN + final FieldKinematicTransform fieldKinematicTransform = createArbitraryFieldKineticTransform(); + final TimeStampedFieldPVCoordinates pvCoordinates = new TimeStampedFieldPVCoordinates<>(AbsoluteDate.ARBITRARY_EPOCH, + createFieldPV()); + // WHEN + final TimeStampedFieldPVCoordinates convertedPV = fieldKinematicTransform + .transformOnlyPV(pvCoordinates); + // THEN + final KinematicTransform kinematicTransform = createKinematicTransform(fieldKinematicTransform); + final PVCoordinates expectedPV = kinematicTransform.transformOnlyPV(pvCoordinates + .toPVCoordinates()); + comparePVCoordinates(expectedPV, convertedPV.toPVCoordinates()); + } + + private FieldPVCoordinates createFieldPV() { + final PVCoordinates pvCoordinates = new PVCoordinates(new Vector3D(1, 2, 3), new Vector3D(4, 5, 6)); + return new FieldPVCoordinates<>(ComplexField.getInstance(), pvCoordinates); + } + + private void comparePVCoordinates(final PVCoordinates pvCoordinates, final PVCoordinates reconvertedPV) { + final double tolerance = 1e-10; + final double[] expectedPosition = pvCoordinates.getPosition().toArray(); + final double[] actualPosition = reconvertedPV.getPosition().toArray(); + final double[] expectedVelocity = pvCoordinates.getVelocity().toArray(); + final double[] actualVelocity = reconvertedPV.getVelocity().toArray(); + for (int i = 0; i < 3; i++) { + Assertions.assertEquals(expectedPosition[i], actualPosition[i], tolerance); + Assertions.assertEquals(expectedVelocity[i], actualVelocity[i], tolerance); + } + } + + private FieldKinematicTransform createArbitraryFieldKineticTransform() { + final Field complexField = ComplexField.getInstance(); + final FieldAbsoluteDate fieldAbsoluteDate = new FieldAbsoluteDate<>(complexField, + AbsoluteDate.ARBITRARY_EPOCH); + return FieldKinematicTransform.of(fieldAbsoluteDate, + new FieldPVCoordinates<>(complexField, new PVCoordinates(Vector3D.MINUS_I, Vector3D.PLUS_J)), + new FieldRotation<>(complexField, new Rotation(Vector3D.MINUS_K, Vector3D.PLUS_I)), + new FieldVector3D<>(complexField, Vector3D.MINUS_J)); + } + + private KinematicTransform createKinematicTransform(final FieldKinematicTransform fieldKinematicTransform) { + return KinematicTransform.of(fieldKinematicTransform.getDate(), + new PVCoordinates(fieldKinematicTransform.getTranslation().toVector3D(), + fieldKinematicTransform.getVelocity().toVector3D()), + fieldKinematicTransform.getRotation().toRotation(), + fieldKinematicTransform.getRotationRate().toVector3D()); + } + +} diff --git a/src/test/java/org/orekit/frames/FieldStaticTransformTest.java b/src/test/java/org/orekit/frames/FieldStaticTransformTest.java index d189d69e71..7538c6b4e0 100644 --- a/src/test/java/org/orekit/frames/FieldStaticTransformTest.java +++ b/src/test/java/org/orekit/frames/FieldStaticTransformTest.java @@ -1,18 +1,40 @@ +/* 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.frames; import org.hamcrest.CoreMatchers; import org.hamcrest.MatcherAssert; import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; +import org.hipparchus.complex.Complex; +import org.hipparchus.complex.ComplexField; 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.RotationConvention; +import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.Binary64Field; import org.hipparchus.util.FastMath; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.orekit.OrekitMatchers; +import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; /** @@ -108,4 +130,94 @@ private > void verifyTransform(final Field CoreMatchers.is(line.getTolerance())); } + @Test + void testOf() { + // GIVEN + final AbsoluteDate expectedDate = AbsoluteDate.ARBITRARY_EPOCH; + final Vector3D expectedTranslation = new Vector3D(1., 2., 3.); + final Rotation rotation = new Rotation(Vector3D.MINUS_J, Vector3D.PLUS_I); + final ComplexField field = ComplexField.getInstance(); + final Complex imaginaryComplex = Complex.I; + final FieldAbsoluteDate expectedFieldDate = new FieldAbsoluteDate<>(field, expectedDate) + .shiftedBy(imaginaryComplex); + final FieldVector3D fieldTranslation = new FieldVector3D<>(field, expectedTranslation); + final FieldRotation fieldRotation = new FieldRotation<>(field, rotation); + // WHEN + final FieldStaticTransform staticTransform = FieldStaticTransform.of(expectedFieldDate, + fieldTranslation, fieldRotation); + // WHEN + Assertions.assertEquals(expectedDate, staticTransform.getDate()); + final FieldAbsoluteDate actualFieldDate = staticTransform.getFieldDate(); + Assertions.assertEquals(staticTransform.getDate(), actualFieldDate.toAbsoluteDate()); + Assertions.assertEquals(Complex.ZERO, actualFieldDate.durationFrom(expectedFieldDate)); + Assertions.assertEquals(expectedTranslation, staticTransform.getTranslation().toVector3D()); + Assertions.assertEquals(0., Rotation.distance(fieldRotation.toRotation(), + staticTransform.getRotation().toRotation())); + } + + @Test + void testGetStaticInverse() { + // GIVEN + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final Vector3D translation = new Vector3D(1., 2., 3.); + final Rotation rotation = new Rotation(Vector3D.MINUS_J, Vector3D.PLUS_I); + final ComplexField field = ComplexField.getInstance(); + final FieldAbsoluteDate fieldDate = new FieldAbsoluteDate<>(field, date); + final FieldVector3D fieldTranslation = new FieldVector3D<>(field, translation); + final FieldRotation fieldRotation = new FieldRotation<>(field, rotation); + final FieldStaticTransform staticTransform = FieldStaticTransform.of(fieldDate, fieldTranslation, + fieldRotation); + // WHEN + final FieldStaticTransform actualInverseStaticTransform = staticTransform.getStaticInverse(); + // THEN + final FieldStaticTransform expectedInverseStaticTransform = staticTransform.getInverse(); + Assertions.assertEquals(expectedInverseStaticTransform.getDate(), actualInverseStaticTransform.getDate()); + Assertions.assertEquals(expectedInverseStaticTransform.getFieldDate(), + actualInverseStaticTransform.getFieldDate()); + Assertions.assertEquals(expectedInverseStaticTransform.getTranslation().toVector3D(), + actualInverseStaticTransform.getTranslation().toVector3D()); + Assertions.assertEquals(0., Rotation.distance(expectedInverseStaticTransform.getRotation().toRotation(), + actualInverseStaticTransform.getRotation().toRotation())); + } + + @Test + void testGetFieldDate() { + // GIVEN + final AbsoluteDate arbitraryEpoch = AbsoluteDate.ARBITRARY_EPOCH; + final TestFieldStaticTransform testFieldStaticTransform = new TestFieldStaticTransform(arbitraryEpoch); + // WHEN + final FieldAbsoluteDate actualFieldDate = testFieldStaticTransform.getFieldDate(); + // THEN + Assertions.assertEquals(testFieldStaticTransform.getDate(), actualFieldDate.toAbsoluteDate()); + } + + private static class TestFieldStaticTransform implements FieldStaticTransform { + + private final AbsoluteDate date; + + TestFieldStaticTransform(final AbsoluteDate date) { + this.date = date; + } + + @Override + public FieldVector3D getTranslation() { + return FieldVector3D.getPlusI(ComplexField.getInstance()); + } + + @Override + public FieldRotation getRotation() { + return null; + } + + @Override + public FieldStaticTransform getInverse() { + return null; + } + + @Override + public AbsoluteDate getDate() { + return date; + } + } + } diff --git a/src/test/java/org/orekit/frames/FrameTest.java b/src/test/java/org/orekit/frames/FrameTest.java index f451f78648..a24ac15b74 100644 --- a/src/test/java/org/orekit/frames/FrameTest.java +++ b/src/test/java/org/orekit/frames/FrameTest.java @@ -17,6 +17,8 @@ package org.orekit.frames; import org.hamcrest.MatcherAssert; +import org.hipparchus.complex.Complex; +import org.hipparchus.complex.ComplexField; import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; @@ -28,6 +30,7 @@ import org.orekit.bodies.GeodeticPoint; import org.orekit.bodies.OneAxisEllipsoid; 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; @@ -266,6 +269,56 @@ private void checkNoTransform(Transform transform, Random random) { } } + @Test + void testGetKinematicTransformTo() { + // GIVEN + final Frame oldFrame = FramesFactory.getEME2000(); + final Frame newFrame = FramesFactory.getGCRF(); + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + // WHEN + final KinematicTransform kinematicTransform = oldFrame.getKinematicTransformTo(newFrame, date); + // THEN + final Transform transform = oldFrame.getTransformTo(newFrame, date); + Assertions.assertEquals(date, kinematicTransform.getDate()); + Assertions.assertEquals(transform.getCartesian().getPosition(), kinematicTransform.getTranslation()); + Assertions.assertEquals(transform.getCartesian().getVelocity(), kinematicTransform.getVelocity()); + Assertions.assertEquals(0., Rotation.distance(transform.getRotation(), kinematicTransform.getRotation())); + Assertions.assertEquals(transform.getRotationRate(), kinematicTransform.getRotationRate()); + } + + @Test + void testFieldGetKinematicTransformToWithConstantDate() { + templateTestFieldGetKinematicTransformTo(getComplexDate()); + } + + @Test + void testFieldGetKinematicTransformToWithNonConstantDate() { + templateTestFieldGetKinematicTransformTo(getComplexDate().shiftedBy(Complex.I)); + } + + private void templateTestFieldGetKinematicTransformTo(final FieldAbsoluteDate fieldDate) { + // GIVEN + final Frame oldFrame = FramesFactory.getEME2000(); + final Frame newFrame = FramesFactory.getGCRF(); + // WHEN + final FieldKinematicTransform fieldKinematicTransform = oldFrame.getKinematicTransformTo(newFrame, + fieldDate); + // THEN + final KinematicTransform kinematicTransform = oldFrame.getKinematicTransformTo(newFrame, + fieldDate.toAbsoluteDate()); + Assertions.assertEquals(kinematicTransform.getDate(), fieldKinematicTransform.getDate()); + Assertions.assertEquals(kinematicTransform.getTranslation(), fieldKinematicTransform.getTranslation().toVector3D()); + Assertions.assertEquals(kinematicTransform.getVelocity(), fieldKinematicTransform.getVelocity().toVector3D()); + Assertions.assertEquals(0., Rotation.distance(kinematicTransform.getRotation(), + fieldKinematicTransform.getRotation().toRotation())); + Assertions.assertEquals(kinematicTransform.getRotationRate(), + fieldKinematicTransform.getRotationRate().toVector3D()); + } + + private FieldAbsoluteDate getComplexDate() { + return FieldAbsoluteDate.getArbitraryEpoch(ComplexField.getInstance()); + } + @BeforeEach public void setUp() { Utils.setDataRoot("compressed-data"); diff --git a/src/test/java/org/orekit/frames/GTODProviderTest.java b/src/test/java/org/orekit/frames/GTODProviderTest.java index dbb1ce8837..3b16ce8273 100644 --- a/src/test/java/org/orekit/frames/GTODProviderTest.java +++ b/src/test/java/org/orekit/frames/GTODProviderTest.java @@ -242,6 +242,40 @@ public void testSerialization() throws IOException, ClassNotFoundException { } + @Test + void testGetKinematicTransform() { + // GIVEN + final GTODProvider provider = new GTODProvider(IERSConventions.IERS_2010, + FramesFactory.getEOPHistory(IERSConventions.IERS_2010, true), + DataContext.getDefault().getTimeScales()); + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + // WHEN + final KinematicTransform kinematicTransform = provider.getKinematicTransform(date); + // THEN + final Transform transform = provider.getTransform(date); + Assertions.assertEquals(date, kinematicTransform.getDate()); + Assertions.assertEquals(transform.getCartesian().getPosition(), kinematicTransform.getTranslation()); + Assertions.assertEquals(transform.getCartesian().getVelocity(), kinematicTransform.getVelocity()); + Assertions.assertEquals(0., Rotation.distance(transform.getRotation(), kinematicTransform.getRotation())); + Assertions.assertEquals(transform.getRotationRate(), kinematicTransform.getRotationRate()); + } + + @Test + void testGetStaticTransform() { + // GIVEN + final GTODProvider provider = new GTODProvider(IERSConventions.IERS_2010, + FramesFactory.getEOPHistory(IERSConventions.IERS_2010, true), + DataContext.getDefault().getTimeScales()); + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + // WHEN + final StaticTransform staticTransform = provider.getStaticTransform(date); + // THEN + final Transform transform = provider.getTransform(date); + Assertions.assertEquals(date, staticTransform.getDate()); + Assertions.assertEquals(transform.getCartesian().getPosition(), staticTransform.getTranslation()); + Assertions.assertEquals(0., Rotation.distance(transform.getRotation(), staticTransform.getRotation())); + } + @BeforeEach public void setUp() { Utils.setDataRoot("compressed-data"); diff --git a/src/test/java/org/orekit/frames/ITRFVersionTest.java b/src/test/java/org/orekit/frames/ITRFVersionTest.java index 28b2a73016..17182fae3f 100644 --- a/src/test/java/org/orekit/frames/ITRFVersionTest.java +++ b/src/test/java/org/orekit/frames/ITRFVersionTest.java @@ -17,12 +17,15 @@ package org.orekit.frames; import org.hamcrest.MatcherAssert; +import org.hipparchus.complex.Complex; +import org.hipparchus.complex.ComplexField; import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.util.Binary64; import org.hipparchus.util.Binary64Field; 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.errors.OrekitException; @@ -35,7 +38,7 @@ public class ITRFVersionTest { @Test - public void testYears() { + void testYears() { Assertions.assertEquals(2014, ITRFVersion.ITRF_2014.getYear()); Assertions.assertEquals(2008, ITRFVersion.ITRF_2008.getYear()); Assertions.assertEquals(2005, ITRFVersion.ITRF_2005.getYear()); @@ -52,7 +55,7 @@ public void testYears() { } @Test - public void testNames() { + void testNames() { Assertions.assertEquals("ITRF-2014", ITRFVersion.ITRF_2014.getName()); Assertions.assertEquals("ITRF-2008", ITRFVersion.ITRF_2008.getName()); Assertions.assertEquals("ITRF-2005", ITRFVersion.ITRF_2005.getName()); @@ -69,7 +72,7 @@ public void testNames() { } @Test - public void testBuildFromYear() { + void testBuildFromYear() { Assertions.assertEquals(ITRFVersion.ITRF_2014, ITRFVersion.getITRFVersion(2014)); Assertions.assertEquals(ITRFVersion.ITRF_2008, ITRFVersion.getITRFVersion(2008)); Assertions.assertEquals(ITRFVersion.ITRF_2005, ITRFVersion.getITRFVersion(2005)); @@ -95,7 +98,7 @@ public void testBuildFromYear() { } @Test - public void testInexistantYear() { + void testInexistantYear() { try { ITRFVersion.getITRFVersion(1999); Assertions.fail("an exception should have been thrown"); @@ -106,7 +109,7 @@ public void testInexistantYear() { } @Test - public void testBuildFromName() { + void testBuildFromName() { Assertions.assertEquals(ITRFVersion.ITRF_2014, ITRFVersion.getITRFVersion("ITRF-2014")); Assertions.assertEquals(ITRFVersion.ITRF_2008, ITRFVersion.getITRFVersion("ItRf-2008")); Assertions.assertEquals(ITRFVersion.ITRF_2005, ITRFVersion.getITRFVersion("iTrF-2005")); @@ -132,7 +135,7 @@ public void testBuildFromName() { } @Test - public void testInexistantName() { + void testInexistantName() { try { ITRFVersion.getITRFVersion("itrf-99"); Assertions.fail("an exception should have been thrown"); @@ -143,7 +146,7 @@ public void testInexistantName() { } @Test - public void testMalformedName() { + void testMalformedName() { try { ITRFVersion.getITRFVersion("YTRF-2014"); Assertions.fail("an exception should have been thrown"); @@ -154,7 +157,7 @@ public void testMalformedName() { } @Test - public void testAllConverters() { + void testAllConverters() { // select the last supported ITRF version ITRFVersion last = ITRFVersion.getLast(); @@ -247,6 +250,52 @@ public void testAllConverters() { } } + @Test + void testConverterGetKinematicTransform() { + // GIVEN + final TransformProvider mockedProvider = Mockito.mock(TransformProvider.class); + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final KinematicTransform expectedTransform = Mockito.mock(KinematicTransform.class); + Mockito.when(mockedProvider.getKinematicTransform(date)).thenReturn(expectedTransform); + final ITRFVersion.Converter converter = new ITRFVersion.Converter(null, null, mockedProvider); + // WHEN + final KinematicTransform actualTransform = converter.getKinematicTransform(date); + // THEN + Assertions.assertEquals(expectedTransform, actualTransform); + } + + @Test + @SuppressWarnings("unchecked") + void testConverterFieldGetKinematicTransform() { + // GIVEN + final TransformProvider mockedProvider = Mockito.mock(TransformProvider.class); + final FieldAbsoluteDate date = new FieldAbsoluteDate<>(ComplexField.getInstance(), + AbsoluteDate.ARBITRARY_EPOCH); + final FieldKinematicTransform expectedTransform = Mockito.mock(FieldKinematicTransform.class); + Mockito.when(mockedProvider.getKinematicTransform(date)).thenReturn(expectedTransform); + final ITRFVersion.Converter converter = new ITRFVersion.Converter(null, null, mockedProvider); + // WHEN + final FieldKinematicTransform actualTransform = converter.getKinematicTransform(date); + // THEN + Assertions.assertEquals(expectedTransform, actualTransform); + } + + @Test + @SuppressWarnings("unchecked") + void testConverterFieldGetStaticTransform() { + // GIVEN + final TransformProvider mockedProvider = Mockito.mock(TransformProvider.class); + final FieldAbsoluteDate date = new FieldAbsoluteDate<>(ComplexField.getInstance(), + AbsoluteDate.ARBITRARY_EPOCH); + final FieldStaticTransform expectedTransform = Mockito.mock(FieldStaticTransform.class); + Mockito.when(mockedProvider.getStaticTransform(date)).thenReturn(expectedTransform); + final ITRFVersion.Converter converter = new ITRFVersion.Converter(null, null, mockedProvider); + // WHEN + final FieldStaticTransform actualTransform = converter.getStaticTransform(date); + // THEN + Assertions.assertEquals(expectedTransform, actualTransform); + } + @BeforeEach public void setUp() { Utils.setDataRoot("compressed-data"); diff --git a/src/test/java/org/orekit/frames/KinematicTransformTest.java b/src/test/java/org/orekit/frames/KinematicTransformTest.java new file mode 100644 index 0000000000..e01fe85279 --- /dev/null +++ b/src/test/java/org/orekit/frames/KinematicTransformTest.java @@ -0,0 +1,142 @@ +/* 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.frames; + +import org.hipparchus.geometry.euclidean.threed.Rotation; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.PVCoordinates; +import org.orekit.utils.TimeStampedPVCoordinates; + +class KinematicTransformTest { + + @Test + void testCompositeVelocity() { + // GIVEN + final KinematicTransform kinematicTransform = createArbitraryKineticTransform(); + // WHEN + final Vector3D actualCompositeVelocity = KinematicTransform.compositeVelocity(kinematicTransform, + KinematicTransform.getIdentity()); + // THEN + final Vector3D expectedCompositeVelocity = kinematicTransform.getVelocity(); + Assertions.assertEquals(expectedCompositeVelocity, actualCompositeVelocity); + } + + @Test + void testCompositeRotationRate() { + // GIVEN + final KinematicTransform kinematicTransform = createArbitraryKineticTransform(); + // WHEN + final Vector3D actualCompositeRotationRate = KinematicTransform.compositeRotationRate(kinematicTransform, + KinematicTransform.getIdentity()); + // THEN + final Vector3D expectedCompositeRotationRate = kinematicTransform.getRotationRate(); + Assertions.assertEquals(expectedCompositeRotationRate, actualCompositeRotationRate); + } + + @Test + void testTransformPVOnly() { + // GIVEN + final KinematicTransform kinematicTransform = createArbitraryKineticTransform(); + final PVCoordinates pvCoordinates = new PVCoordinates(new Vector3D(1, 2, 3), new Vector3D(4, 5, 6)); + // WHEN + final PVCoordinates convertedPV = kinematicTransform.transformOnlyPV(pvCoordinates); + final PVCoordinates reconvertedPV = kinematicTransform.getInverse().transformOnlyPV(convertedPV); + // THEN + comparePVCoordinates(pvCoordinates, reconvertedPV); + } + + @Test + void testTransformTimeStampedPVCoordinatesWithoutA() { + // GIVEN + final KinematicTransform kinematicTransform = createArbitraryKineticTransform(); + final TimeStampedPVCoordinates pvCoordinates = new TimeStampedPVCoordinates(AbsoluteDate.ARBITRARY_EPOCH, + new Vector3D(1, 2, 3), new Vector3D(4, 5, 6)); + // WHEN + final PVCoordinates convertedPV = kinematicTransform.transformOnlyPV(pvCoordinates); + final PVCoordinates reconvertedPV = kinematicTransform.getInverse().transformOnlyPV(convertedPV); + // THEN + comparePVCoordinates(pvCoordinates, reconvertedPV); + } + + private void comparePVCoordinates(final PVCoordinates pvCoordinates, final PVCoordinates reconvertedPV) { + final double tolerance = 1e-10; + final double[] expectedPosition = pvCoordinates.getPosition().toArray(); + final double[] actualPosition = reconvertedPV.getPosition().toArray(); + final double[] expectedVelocity = pvCoordinates.getVelocity().toArray(); + final double[] actualVelocity = reconvertedPV.getVelocity().toArray(); + for (int i = 0; i < 3; i++) { + Assertions.assertEquals(expectedPosition[i], actualPosition[i], tolerance); + Assertions.assertEquals(expectedVelocity[i], actualVelocity[i], tolerance); + } + } + + @Test + void testOfWithoutRotation() { + // GIVEN + final AbsoluteDate expectedDate = AbsoluteDate.ARBITRARY_EPOCH; + final PVCoordinates pvCoordinates = new PVCoordinates(Vector3D.MINUS_I, Vector3D.PLUS_J); + // WHEN + final KinematicTransform kinematicTransform = KinematicTransform.of(expectedDate, pvCoordinates); + // THEN + Assertions.assertEquals(expectedDate, kinematicTransform.getDate()); + Assertions.assertEquals(pvCoordinates.getPosition(), kinematicTransform.getTranslation()); + Assertions.assertEquals(pvCoordinates.getVelocity(), kinematicTransform.getVelocity()); + Assertions.assertEquals(0., Rotation.distance(Rotation.IDENTITY, kinematicTransform.getRotation())); + Assertions.assertEquals(Vector3D.ZERO, kinematicTransform.getRotationRate()); + } + + @Test + void testOfTranslation() { + final AbsoluteDate expectedDate = AbsoluteDate.ARBITRARY_EPOCH; + final Rotation expectedRotation = new Rotation(Vector3D.MINUS_K, Vector3D.PLUS_I); + final Vector3D expectedRotationRate = Vector3D.MINUS_J; + // WHEN + final KinematicTransform kinematicTransform = KinematicTransform.of(expectedDate, expectedRotation, + expectedRotationRate); + // THEN + Assertions.assertEquals(expectedDate, kinematicTransform.getDate()); + Assertions.assertEquals(Vector3D.ZERO, kinematicTransform.getTranslation()); + Assertions.assertEquals(Vector3D.ZERO, kinematicTransform.getVelocity()); + Assertions.assertEquals(0., Rotation.distance(expectedRotation, kinematicTransform.getRotation())); + Assertions.assertEquals(expectedRotationRate, kinematicTransform.getRotationRate()); + } + + @Test + void testOfInverse() { + final KinematicTransform kinematicTransform = createArbitraryKineticTransform(); + // WHEN + final KinematicTransform inverseKinematicTransform = kinematicTransform.getInverse(); + final KinematicTransform composedTransform = KinematicTransform.compose(kinematicTransform.getDate(), + kinematicTransform, inverseKinematicTransform); + // THEN + Assertions.assertEquals(kinematicTransform.getDate(), composedTransform.getDate()); + Assertions.assertEquals(Vector3D.ZERO, composedTransform.getTranslation()); + Assertions.assertEquals(Vector3D.ZERO, composedTransform.getVelocity()); + Assertions.assertEquals(0., Rotation.distance(Rotation.IDENTITY, composedTransform.getRotation())); + Assertions.assertEquals(Vector3D.ZERO, composedTransform.getRotationRate()); + } + + private KinematicTransform createArbitraryKineticTransform() { + return KinematicTransform.of(AbsoluteDate.ARBITRARY_EPOCH, new PVCoordinates(Vector3D.MINUS_I, Vector3D.PLUS_J), + new Rotation(Vector3D.MINUS_K, Vector3D.PLUS_I), Vector3D.MINUS_J); + } + +} diff --git a/src/test/java/org/orekit/frames/LocalOrbitalFrameTest.java b/src/test/java/org/orekit/frames/LocalOrbitalFrameTest.java index d0a796c112..78b9c26ee7 100644 --- a/src/test/java/org/orekit/frames/LocalOrbitalFrameTest.java +++ b/src/test/java/org/orekit/frames/LocalOrbitalFrameTest.java @@ -47,6 +47,21 @@ public class LocalOrbitalFrameTest { + @Test + public void testIssue1282() { + // GIVEN + final Vector3D position = new Vector3D(0,0,1); + final Vector3D velocity = new Vector3D(0,1,0); + final PVCoordinates pvCoordinates = new PVCoordinates(position, velocity); + + // WHEN + final Rotation actualRotation = LOFType.EQW.rotationFromInertial(pvCoordinates); + + // THEN + final Rotation expectedRotation = new Rotation(Vector3D.MINUS_J,Vector3D.MINUS_I, Vector3D.PLUS_I, Vector3D.PLUS_K); + Assertions.assertArrayEquals(expectedRotation.getMatrix(), actualRotation.getMatrix()); + } + @Test public void testIssue977() { LOFType type = LOFType.TNW; diff --git a/src/test/java/org/orekit/frames/StaticTransformTest.java b/src/test/java/org/orekit/frames/StaticTransformTest.java index 671902a3ce..fa135bd195 100644 --- a/src/test/java/org/orekit/frames/StaticTransformTest.java +++ b/src/test/java/org/orekit/frames/StaticTransformTest.java @@ -1,3 +1,20 @@ +/* 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.frames; import org.hamcrest.CoreMatchers; @@ -7,6 +24,7 @@ import org.hipparchus.geometry.euclidean.threed.RotationConvention; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.orekit.OrekitMatchers; import org.orekit.time.AbsoluteDate; @@ -74,4 +92,22 @@ public void testSimpleComposition() { CoreMatchers.is(line.getTolerance())); } + @Test + void testGetStaticInverse() { + // GIVEN + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final Vector3D translation = new Vector3D(1., 2., 3.); + final Rotation rotation = new Rotation(Vector3D.MINUS_J, Vector3D.PLUS_I); + final StaticTransform staticTransform = StaticTransform.of(date, translation, rotation); + // WHEN + final StaticTransform actualInverseStaticTransform = staticTransform.getStaticInverse(); + // THEN + final StaticTransform expectedInverseStaticTransform = staticTransform.getInverse(); + Assertions.assertEquals(expectedInverseStaticTransform.getDate(), actualInverseStaticTransform.getDate()); + Assertions.assertEquals(expectedInverseStaticTransform.getTranslation(), + actualInverseStaticTransform.getTranslation()); + Assertions.assertEquals(0., Rotation.distance(expectedInverseStaticTransform.getRotation(), + actualInverseStaticTransform.getRotation())); + } + } diff --git a/src/test/java/org/orekit/frames/TIRFProviderTest.java b/src/test/java/org/orekit/frames/TIRFProviderTest.java index 2acfb2f219..727ca17e24 100644 --- a/src/test/java/org/orekit/frames/TIRFProviderTest.java +++ b/src/test/java/org/orekit/frames/TIRFProviderTest.java @@ -17,6 +17,8 @@ package org.orekit.frames; import org.hamcrest.MatcherAssert; +import org.hipparchus.complex.Complex; +import org.hipparchus.complex.ComplexField; import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; @@ -29,6 +31,7 @@ import org.orekit.data.DataContext; import org.orekit.time.AbsoluteDate; import org.orekit.time.DateComponents; +import org.orekit.time.FieldAbsoluteDate; import org.orekit.time.TimeComponents; import org.orekit.time.TimeScale; import org.orekit.time.TimeScalesFactory; @@ -74,8 +77,8 @@ public void testAASReferenceLEO() { { 53104, -0.4399619, 0.0015563, -0.140682, 0.333309, Double.NaN, Double.NaN, -0.000199, -0.000252 }, { 53105, -0.4399619, 0.0015563, -0.140682, 0.333309, Double.NaN, Double.NaN, -0.000199, -0.000252 } })); - AbsoluteDate t0 = new AbsoluteDate(new DateComponents(2004, 04, 06), - new TimeComponents(07, 51, 28.386009), + AbsoluteDate t0 = new AbsoluteDate(new DateComponents(2004, 4, 6), + new TimeComponents(7, 51, 28.386009), TimeScalesFactory.getUTC()); // Positions LEO @@ -128,7 +131,7 @@ public void testAASReferenceGEO() { { 53160, -0.4709050, 0.0000000, -0.083853, 0.467217, Double.NaN, Double.NaN, -0.000199, -0.000252 } })); - AbsoluteDate t0 = new AbsoluteDate(new DateComponents(2004, 06, 01), + AbsoluteDate t0 = new AbsoluteDate(new DateComponents(2004, 6, 1), TimeComponents.H00, TimeScalesFactory.getUTC()); @@ -241,7 +244,7 @@ public void testConcurrentGetTransform() // tolerance of comparisons final double absTol = Precision.EPSILON; // the expected result - final List expecteds = new ArrayList(); + final List expecteds = new ArrayList<>(); for (int j = 0; j < nPerJob; j++) { final AbsoluteDate date = start.shiftedBy(timeStep * j); // action @@ -251,19 +254,17 @@ public void testConcurrentGetTransform() } // build jobs for concurrent execution - final List> jobs = new ArrayList>(); + final List> jobs = new ArrayList<>(); for (int i = 0; i < nJobs; i++) { - jobs.add(new Callable() { - public Boolean call() throws Exception { - for (int j = 0; j < nPerJob; j++) { - final AbsoluteDate date = start.shiftedBy(timeStep * j); - // action - final Transform actual = tirf.getTransform(date); - // verify - assertTransformEquals(expecteds.get(j), actual, absTol); - } - return true; + jobs.add(() -> { + for (int j = 0; j < nPerJob; j++) { + final AbsoluteDate date = start.shiftedBy(timeStep * j); + // action + final Transform actual = tirf.getTransform(date); + // verify + assertTransformEquals(expecteds.get(j), actual, absTol); } + return true; }); } @@ -310,6 +311,63 @@ private static void assertTransformEquals(Transform expected, Transform actual, Assertions.assertEquals(expectedPV.getVelocity(), actualPV.getVelocity()); } + @Test + void testGetKinematicTransform() { + // GIVEN + final EOPHistory eopHistory = FramesFactory.getEOPHistory(IERSConventions.IERS_2010, false); + final TimeScale ut1 = DataContext.getDefault().getTimeScales().getUT1(eopHistory); + final TIRFProvider provider = new TIRFProvider(eopHistory, ut1); + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + // WHEN + final KinematicTransform kinematicTransform = provider.getKinematicTransform(date); + // THEN + final Transform transform = provider.getTransform(date); + Assertions.assertEquals(date, kinematicTransform.getDate()); + Assertions.assertEquals(transform.getCartesian().getPosition(), kinematicTransform.getTranslation()); + Assertions.assertEquals(transform.getCartesian().getVelocity(), kinematicTransform.getVelocity()); + Assertions.assertEquals(0., Rotation.distance(transform.getRotation(), kinematicTransform.getRotation())); + Assertions.assertEquals(transform.getRotationRate(), kinematicTransform.getRotationRate()); + } + + @Test + void testFieldGetKinematicTransform() { + // GIVEN + final EOPHistory eopHistory = FramesFactory.getEOPHistory(IERSConventions.IERS_2010, false); + final TimeScale ut1 = DataContext.getDefault().getTimeScales().getUT1(eopHistory); + final TIRFProvider provider = new TIRFProvider(eopHistory, ut1); + final ComplexField field = ComplexField.getInstance(); + final FieldAbsoluteDate date = FieldAbsoluteDate.getArbitraryEpoch(field); + // WHEN + final FieldKinematicTransform fieldKinematicTransform = provider.getKinematicTransform(date); + // THEN + final KinematicTransform kinematicTransform = provider.getKinematicTransform(date.toAbsoluteDate()); + Assertions.assertEquals(kinematicTransform.getDate(), fieldKinematicTransform.getDate()); + Assertions.assertEquals(kinematicTransform.getTranslation(), fieldKinematicTransform.getTranslation().toVector3D()); + Assertions.assertEquals(kinematicTransform.getVelocity(), fieldKinematicTransform.getVelocity().toVector3D()); + Assertions.assertEquals(0., Rotation.distance(kinematicTransform.getRotation(), + fieldKinematicTransform.getRotation().toRotation()), 1e-15); + Assertions.assertEquals(kinematicTransform.getRotationRate(), fieldKinematicTransform.getRotationRate().toVector3D()); + } + + @Test + void testFieldGetStaticTransform() { + // GIVEN + final EOPHistory eopHistory = FramesFactory.getEOPHistory(IERSConventions.IERS_2010, false); + final TimeScale ut1 = DataContext.getDefault().getTimeScales().getUT1(eopHistory); + final TIRFProvider provider = new TIRFProvider(eopHistory, ut1); + final ComplexField field = ComplexField.getInstance(); + final FieldAbsoluteDate date = FieldAbsoluteDate.getArbitraryEpoch(field); + // WHEN + final FieldStaticTransform fieldStaticTransform = provider.getStaticTransform(date); + // THEN + final StaticTransform staticTransform = provider.getStaticTransform(date.toAbsoluteDate()); + Assertions.assertEquals(staticTransform.getDate(), fieldStaticTransform.getDate()); + Assertions.assertEquals(staticTransform.getTranslation(), fieldStaticTransform.getTranslation().toVector3D()); + Assertions.assertEquals(0., Rotation.distance(staticTransform.getRotation(), + fieldStaticTransform.getRotation().toRotation()), 1e-15); + + } + @BeforeEach public void setUp() { Utils.setDataRoot("compressed-data"); diff --git a/src/test/java/org/orekit/frames/TopocentricFrameTest.java b/src/test/java/org/orekit/frames/TopocentricFrameTest.java index 23a409b103..c15ec64aeb 100644 --- a/src/test/java/org/orekit/frames/TopocentricFrameTest.java +++ b/src/test/java/org/orekit/frames/TopocentricFrameTest.java @@ -17,6 +17,7 @@ package org.orekit.frames; import java.io.IOException; +import java.util.stream.Stream; import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; @@ -28,12 +29,18 @@ import org.hipparchus.random.RandomGenerator; import org.hipparchus.random.Well1024a; import org.hipparchus.util.Binary64Field; +import org.hipparchus.util.Binary64; import org.hipparchus.util.FastMath; import org.hipparchus.util.MathUtils; +import org.hipparchus.util.SinCos; +import org.hipparchus.util.FieldSinCos; 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.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.orekit.Utils; import org.orekit.bodies.BodyShape; import org.orekit.bodies.FieldGeodeticPoint; @@ -59,7 +66,7 @@ import org.orekit.utils.TrackingCoordinates; -public class TopocentricFrameTest { +class TopocentricFrameTest { // Computation date private AbsoluteDate date; @@ -75,7 +82,7 @@ public class TopocentricFrameTest { @Test - public void testZero() { + void testZero() { final GeodeticPoint point = new GeodeticPoint(0., 0., 0.); final TopocentricFrame topoFrame = new TopocentricFrame(earthSpheric, point, "zero"); @@ -90,7 +97,7 @@ public void testZero() { } @Test - public void testPole() { + void testPole() { final GeodeticPoint point = new GeodeticPoint(FastMath.PI/2., 0., 0.); final TopocentricFrame topoFrame = new TopocentricFrame(earthSpheric, point, "north pole"); @@ -105,7 +112,7 @@ public void testPole() { } @Test - public void testNormalLatitudes() { + void testNormalLatitudes() { // First point at latitude 45° final GeodeticPoint point1 = new GeodeticPoint(FastMath.toRadians(45.), FastMath.toRadians(30.), 0.); @@ -132,7 +139,7 @@ public void testNormalLatitudes() { } @Test - public void testOppositeLongitudes() { + void testOppositeLongitudes() { // First point at latitude 45° final GeodeticPoint point1 = new GeodeticPoint(FastMath.toRadians(45.), FastMath.toRadians(30.), 0.); @@ -158,7 +165,7 @@ public void testOppositeLongitudes() { } @Test - public void testAntipodes() { + void testAntipodes() { // First point at latitude 45° and longitude 30 final GeodeticPoint point1 = new GeodeticPoint(FastMath.toRadians(45.), FastMath.toRadians(30.), 0.); @@ -184,7 +191,7 @@ public void testAntipodes() { } @Test - public void testSiteAtZenith() { + void testSiteAtZenith() { // Surface point at latitude 45° final GeodeticPoint point = new GeodeticPoint(FastMath.toRadians(45.), FastMath.toRadians(30.), 0.); @@ -204,7 +211,7 @@ public void testSiteAtZenith() { } @Test - public void testFieldSiteAtZenith() { + void testFieldSiteAtZenith() { doTestFieldSiteAtZenith(Binary64Field.getInstance()); } @@ -235,7 +242,7 @@ private > void doTestFieldSiteAtZenith(final F } @Test - public void testAzimuthEquatorial() { + void testAzimuthEquatorial() { // Surface point at latitude 0 final GeodeticPoint point = new GeodeticPoint(FastMath.toRadians(0.), FastMath.toRadians(30.), 0.); @@ -270,7 +277,7 @@ public void testAzimuthEquatorial() { } @Test - public void testFieldAzimuthEquatorial() { + void testFieldAzimuthEquatorial() { doTestFieldAzimuthEquatorial(Binary64Field.getInstance()); } @@ -320,7 +327,7 @@ private > void doTestFieldAzimuthEquatorial(fi } @Test - public void testAzimuthPole() { + void testAzimuthPole() { // Surface point at latitude 0 final GeodeticPoint point = new GeodeticPoint(FastMath.toRadians(89.999), FastMath.toRadians(0.), 0.); @@ -343,7 +350,7 @@ public void testAzimuthPole() { } @Test - public void testFieldAzimuthPole() { + void testFieldAzimuthPole() { doTestFieldAzimuthPole(Binary64Field.getInstance()); } @@ -377,7 +384,7 @@ private > void doTestFieldAzimuthPole(final Fi } @Test - public void testDoppler() { + void testDoppler() { // Surface point at latitude 45, longitude 5 final GeodeticPoint point = new GeodeticPoint(FastMath.toRadians(45.), FastMath.toRadians(5.), 0.); @@ -424,7 +431,7 @@ public void testDoppler() { } @Test - public void testFieldDoppler() { + void testFieldDoppler() { doTestFieldDoppler(Binary64Field.getInstance()); } @@ -482,7 +489,7 @@ private > void doTestFieldDoppler(final Field< } @Test - public void testEllipticEarth() { + void testEllipticEarth() { // Elliptic earth shape final OneAxisEllipsoid earthElliptic = @@ -568,7 +575,7 @@ public void testEllipticEarth() { } @Test - public void testFieldEllipticEarth() { + void testFieldEllipticEarth() { doTestFieldEllipticEarth(Binary64Field.getInstance()); } @@ -674,7 +681,7 @@ private > void doTestFieldEllipticEarth(final } @Test - public void testPointAtDistance() { + void testPointAtDistance() { RandomGenerator random = new Well1024a(0xa1e6bd5cd0578779l); final OneAxisEllipsoid earth = @@ -705,7 +712,7 @@ public void testPointAtDistance() { } @Test - public void testIssue145() { + void testIssue145() { Frame itrf = FramesFactory.getITRF(IERSConventions.IERS_2010, true); BodyShape earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, Constants.WGS84_EARTH_FLATTENING, @@ -720,7 +727,7 @@ public void testIssue145() { } @Test - public void testVisibilityCircle() throws IOException { + void testVisibilityCircle() throws IOException { // a few random from International Laser Ranging Service final BodyShape earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, @@ -753,6 +760,77 @@ public void testVisibilityCircle() throws IOException { } + private static Stream testGetTopocentricCoordinatesValues() { + return Stream.of( + Arguments.of(0,0,1), + Arguments.of(0, FastMath.PI / 2, 1), + Arguments.of(FastMath.PI, FastMath.PI / 3, 10), + Arguments.of(3 * FastMath.PI / 2, FastMath.PI / 4, 1000), + Arguments.of(FastMath.PI / 2, -FastMath.PI / 6, 500), + Arguments.of(FastMath.PI / 7, -FastMath.PI / 5, 100) + ); + } + + @ParameterizedTest + @MethodSource("testGetTopocentricCoordinatesValues") + void testGetTopocentricCoordinates(final double az, final double el, final double r) { + final TrackingCoordinates coords = new TrackingCoordinates(az,el,r); + final Vector3D topoPos = TopocentricFrame.getTopocentricPosition(coords); + + final SinCos sinCosAz = FastMath.sinCos(az); + final SinCos sinCosEl = FastMath.sinCos(el); + final double expectedX, expectedY, expectedZ; + expectedX = sinCosAz.sin() * sinCosEl.cos() * r; + expectedY = sinCosAz.cos() * sinCosEl.cos() * r; + expectedZ = sinCosEl.sin() * r; + + final double distanceTolerance = 1e-7; + Assertions.assertEquals(expectedX,topoPos.getX(), distanceTolerance); + Assertions.assertEquals(expectedY,topoPos.getY(), distanceTolerance); + Assertions.assertEquals(expectedZ,topoPos.getZ(), distanceTolerance); + } + + @ParameterizedTest + @MethodSource("testGetTopocentricCoordinatesValues") + void testGetFieldTopocentricCoordinates(final double azimuth, final double elevation, final double range) { + final Binary64 az, el, r; + az = new Binary64(azimuth); + el = new Binary64(elevation); + r = new Binary64(range); + + final FieldTrackingCoordinates coords = new FieldTrackingCoordinates<>(az, el, r); + final FieldVector3D topoPos = TopocentricFrame.getTopocentricPosition(coords); + + final FieldSinCos sinCosAz = FastMath.sinCos(az); + final FieldSinCos sinCosEl = FastMath.sinCos(el); + final Binary64 expectedX, expectedY, expectedZ; + expectedX = r.multiply(sinCosAz.sin().multiply(sinCosEl.cos())); + expectedY = r.multiply(sinCosEl.cos().multiply(sinCosAz.cos())); + expectedZ = r.multiply(sinCosEl.sin()); + + final double distanceTolerance = 1e-7; + Assertions.assertEquals(expectedX.getReal(), topoPos.getX().getReal(), distanceTolerance); + Assertions.assertEquals(expectedY.getReal(), topoPos.getY().getReal(), distanceTolerance); + Assertions.assertEquals(expectedZ.getReal(), topoPos.getZ().getReal(), distanceTolerance); + } + + @ParameterizedTest + @MethodSource("testGetTopocentricCoordinatesValues") + void testInverseGetTopocentricCoordinates(double az, double el, double r) { + final TrackingCoordinates expectedCoords = new TrackingCoordinates(az, el, r); + + final Vector3D point = TopocentricFrame.getTopocentricPosition(expectedCoords); + final GeodeticPoint geodeticPoint = new GeodeticPoint(0., 0., 0.); + final TopocentricFrame topoFrame = new TopocentricFrame(earthSpheric, geodeticPoint, "geodeticPoint"); + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final TrackingCoordinates coords = topoFrame.getTrackingCoordinates(point, topoFrame, date); + + final double angularTolerance = 1e-12; + Assertions.assertEquals(expectedCoords.getAzimuth(), coords.getAzimuth(), angularTolerance); + Assertions.assertEquals(expectedCoords.getElevation(), coords.getElevation(), angularTolerance); + Assertions.assertEquals(expectedCoords.getRange(), coords.getRange()); + } + @Test void testGetTrackingCoordinates() { // GIVEN diff --git a/src/test/java/org/orekit/frames/TransformProviderTest.java b/src/test/java/org/orekit/frames/TransformProviderTest.java new file mode 100644 index 0000000000..e6af072cc1 --- /dev/null +++ b/src/test/java/org/orekit/frames/TransformProviderTest.java @@ -0,0 +1,120 @@ +/* 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.frames; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.complex.Complex; +import org.hipparchus.complex.ComplexField; +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.RotationConvention; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; + +class TransformProviderTest { + + @Test + void testGetStaticTransform() { + // GIVEN + final TestTransformProvider transformProvider = new TestTransformProvider(); + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + // WHEN + final StaticTransform staticTransform = transformProvider.getStaticTransform(date); + // THEN + final Transform transform = transformProvider.getTransform(date); + Assertions.assertEquals(date, staticTransform.getDate()); + Assertions.assertEquals(transform.getCartesian().getPosition(), staticTransform.getTranslation()); + Assertions.assertEquals(0., Rotation.distance(transform.getRotation(), staticTransform.getRotation())); + } + + @Test + void testGetKinematicTransform() { + // GIVEN + final TestTransformProvider transformProvider = new TestTransformProvider(); + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + // WHEN + final KinematicTransform kinematicTransform = transformProvider.getKinematicTransform(date); + // THEN + final Transform transform = transformProvider.getTransform(date); + Assertions.assertEquals(date, kinematicTransform.getDate()); + Assertions.assertEquals(transform.getCartesian().getPosition(), kinematicTransform.getTranslation()); + Assertions.assertEquals(transform.getCartesian().getVelocity(), kinematicTransform.getVelocity()); + Assertions.assertEquals(0., Rotation.distance(transform.getRotation(), kinematicTransform.getRotation())); + Assertions.assertEquals(transform.getRotationRate(), kinematicTransform.getRotationRate()); + } + + @Test + void testFieldGetKinematicTransform() { + // GIVEN + final TestTransformProvider transformProvider = new TestTransformProvider(); + final ComplexField complexField = ComplexField.getInstance(); + final FieldAbsoluteDate fieldDate = FieldAbsoluteDate.getArbitraryEpoch(complexField); + // WHEN + final FieldKinematicTransform fieldKinematicTransform = transformProvider + .getKinematicTransform(fieldDate); + // THEN + final KinematicTransform kinematicTransform = transformProvider.getKinematicTransform(fieldDate.toAbsoluteDate()); + Assertions.assertEquals(kinematicTransform.getDate(), fieldKinematicTransform.getDate()); + Assertions.assertEquals(kinematicTransform.getTranslation(), fieldKinematicTransform.getTranslation().toVector3D()); + Assertions.assertEquals(kinematicTransform.getVelocity(), fieldKinematicTransform.getVelocity().toVector3D()); + Assertions.assertEquals(0., Rotation.distance(kinematicTransform.getRotation(), + fieldKinematicTransform.getRotation().toRotation())); + Assertions.assertEquals(kinematicTransform.getRotationRate(), + fieldKinematicTransform.getRotationRate().toVector3D()); + } + + @Test + void testFieldGetStaticTransform() { + // GIVEN + final TestTransformProvider transformProvider = new TestTransformProvider(); + final ComplexField complexField = ComplexField.getInstance(); + final FieldAbsoluteDate fieldDate = FieldAbsoluteDate.getArbitraryEpoch(complexField); + // WHEN + final FieldStaticTransform fieldStaticTransform = transformProvider + .getStaticTransform(fieldDate); + // THEN + final StaticTransform staticTransform = transformProvider.getStaticTransform(fieldDate.toAbsoluteDate()); + Assertions.assertEquals(staticTransform.getDate(), fieldStaticTransform.getDate()); + Assertions.assertEquals(staticTransform.getTranslation(), fieldStaticTransform.getTranslation().toVector3D()); + Assertions.assertEquals(0., Rotation.distance(staticTransform.getRotation(), + fieldStaticTransform.getRotation().toRotation())); + } + + private static class TestTransformProvider implements TransformProvider { + + @Override + public Transform getTransform(AbsoluteDate date) { + return new Transform(date, new Rotation(Vector3D.PLUS_I, 1., RotationConvention.FRAME_TRANSFORM), + Vector3D.MINUS_J); + } + + @Override + public > FieldTransform getTransform(FieldAbsoluteDate date) { + final Transform transform = getTransform(date.toAbsoluteDate()); + final Field field = date.getField(); + final FieldRotation fieldRotation = new FieldRotation<>(field, transform.getRotation()); + final FieldVector3D fieldRotationRate = new FieldVector3D<>(field, transform.getRotationRate()); + return new FieldTransform<>(date, fieldRotation, fieldRotationRate); + } + } + +} diff --git a/src/test/java/org/orekit/frames/TransformProviderUtilTest.java b/src/test/java/org/orekit/frames/TransformProviderUtilTest.java index 70e4f9a577..7117d20dff 100644 --- a/src/test/java/org/orekit/frames/TransformProviderUtilTest.java +++ b/src/test/java/org/orekit/frames/TransformProviderUtilTest.java @@ -17,6 +17,9 @@ package org.orekit.frames; import org.hipparchus.CalculusFieldElement; +import org.hipparchus.complex.Complex; +import org.hipparchus.complex.ComplexField; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.random.RandomGenerator; @@ -26,12 +29,133 @@ import org.hipparchus.util.FastMath; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldPVCoordinates; import org.orekit.utils.PVCoordinates; public class TransformProviderUtilTest { + @Test + void testGetCombinedProviderGetKinematicTransform() { + // GIVEN + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final TransformProvider mockedProvider1 = Mockito.mock(TransformProvider.class); + final TransformProvider mockedProvider2 = Mockito.mock(TransformProvider.class); + final KinematicTransform kinematicTransform = KinematicTransform.of(date, new PVCoordinates(Vector3D.MINUS_I, + Vector3D.PLUS_J)); + Mockito.when(mockedProvider1.getKinematicTransform(date)).thenReturn(kinematicTransform); + Mockito.when(mockedProvider2.getKinematicTransform(date)).thenReturn(kinematicTransform.getInverse()); + final TransformProvider combinedProvider = TransformProviderUtils.getCombinedProvider(mockedProvider1, + mockedProvider2); + // WHEN + final KinematicTransform actualTransform = combinedProvider.getKinematicTransform(date); + // THEN + final KinematicTransform expectedTransform = KinematicTransform.getIdentity(); + Assertions.assertEquals(expectedTransform.getDate(), actualTransform.getDate()); + Assertions.assertEquals(expectedTransform.getTranslation(), actualTransform.getTranslation()); + Assertions.assertEquals(expectedTransform.getVelocity(), actualTransform.getVelocity()); + } + + @Test + void testGetCombinedProviderFieldGetKinematicTransform() { + // GIVEN + final ComplexField field = ComplexField.getInstance(); + final FieldAbsoluteDate date = new FieldAbsoluteDate<>(field, AbsoluteDate.ARBITRARY_EPOCH); + final TransformProvider mockedProvider1 = Mockito.mock(TransformProvider.class); + final TransformProvider mockedProvider2 = Mockito.mock(TransformProvider.class); + final FieldKinematicTransform kinematicTransform = FieldKinematicTransform.of(date, + new FieldPVCoordinates<>(field, new PVCoordinates(Vector3D.MINUS_I, Vector3D.PLUS_J))); + Mockito.when(mockedProvider1.getKinematicTransform(date)).thenReturn(kinematicTransform); + Mockito.when(mockedProvider2.getKinematicTransform(date)).thenReturn(kinematicTransform.getInverse()); + final TransformProvider combinedProvider = TransformProviderUtils.getCombinedProvider(mockedProvider1, + mockedProvider2); + // WHEN + final FieldKinematicTransform actualTransform = combinedProvider.getKinematicTransform(date); + // THEN + final FieldKinematicTransform expectedTransform = FieldKinematicTransform.getIdentity(field); + Assertions.assertEquals(expectedTransform.getDate(), actualTransform.getDate()); + Assertions.assertEquals(expectedTransform.getTranslation().toVector3D(), + actualTransform.getTranslation().toVector3D()); + Assertions.assertEquals(expectedTransform.getVelocity().toVector3D(), + actualTransform.getVelocity().toVector3D()); + } + + @Test + void testGetCombinedProviderFieldGetStaticTransform() { + // GIVEN + final ComplexField field = ComplexField.getInstance(); + final FieldAbsoluteDate date = new FieldAbsoluteDate<>(field, AbsoluteDate.ARBITRARY_EPOCH); + final TransformProvider mockedProvider1 = Mockito.mock(TransformProvider.class); + final TransformProvider mockedProvider2 = Mockito.mock(TransformProvider.class); + final FieldStaticTransform staticTransform = FieldStaticTransform.of(date, + new FieldVector3D<>(field, Vector3D.PLUS_J)); + Mockito.when(mockedProvider1.getStaticTransform(date)).thenReturn(staticTransform); + Mockito.when(mockedProvider2.getStaticTransform(date)).thenReturn(staticTransform.getInverse()); + final TransformProvider combinedProvider = TransformProviderUtils.getCombinedProvider(mockedProvider1, + mockedProvider2); + // WHEN + final FieldStaticTransform actualTransform = combinedProvider.getStaticTransform(date); + // THEN + final FieldStaticTransform expectedTransform = FieldStaticTransform.getIdentity(field); + Assertions.assertEquals(expectedTransform.getDate(), actualTransform.getDate()); + Assertions.assertEquals(expectedTransform.getTranslation().toVector3D(), + actualTransform.getTranslation().toVector3D()); + } + + @Test + void testGetReversedProviderGetKinematicTransform() { + // GIVEN + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final TransformProvider mockedProvider = Mockito.mock(TransformProvider.class); + final TransformProvider reversedProvider = TransformProviderUtils.getReversedProvider(mockedProvider); + final KinematicTransform expectedTransform = Mockito.mock(KinematicTransform.class); + final KinematicTransform mockedTransform = Mockito.mock(KinematicTransform.class); + Mockito.when(mockedTransform.getInverse()).thenReturn(expectedTransform); + Mockito.when(mockedProvider.getKinematicTransform(date)).thenReturn(mockedTransform); + // WHEN + final KinematicTransform actualTransform = reversedProvider.getKinematicTransform(date); + // THEN + Assertions.assertEquals(expectedTransform, actualTransform); + } + + @Test + @SuppressWarnings("unchecked") + void testGetReversedProviderFieldGetKinematicTransform() { + // GIVEN + final FieldAbsoluteDate date = new FieldAbsoluteDate<>(ComplexField.getInstance(), + AbsoluteDate.ARBITRARY_EPOCH); + final TransformProvider mockedProvider = Mockito.mock(TransformProvider.class); + final TransformProvider reversedProvider = TransformProviderUtils.getReversedProvider(mockedProvider); + final FieldKinematicTransform expectedTransform = Mockito.mock(FieldKinematicTransform.class); + final FieldKinematicTransform mockedTransform = Mockito.mock(FieldKinematicTransform.class); + Mockito.when(mockedTransform.getInverse()).thenReturn(expectedTransform); + Mockito.when(mockedProvider.getKinematicTransform(date)).thenReturn(mockedTransform); + // WHEN + final FieldKinematicTransform actualTransform = reversedProvider.getKinematicTransform(date); + // THEN + Assertions.assertEquals(expectedTransform, actualTransform); + } + + @Test + @SuppressWarnings("unchecked") + void testGetReversedProviderFieldGetStaticTransform() { + // GIVEN + final FieldAbsoluteDate date = new FieldAbsoluteDate<>(ComplexField.getInstance(), + AbsoluteDate.ARBITRARY_EPOCH); + final TransformProvider mockedProvider = Mockito.mock(TransformProvider.class); + final TransformProvider reversedProvider = TransformProviderUtils.getReversedProvider(mockedProvider); + final FieldStaticTransform expectedTransform = Mockito.mock(FieldStaticTransform.class); + final FieldStaticTransform mockedTransform = Mockito.mock(FieldStaticTransform.class); + Mockito.when(mockedTransform.getInverse()).thenReturn(expectedTransform); + Mockito.when(mockedProvider.getStaticTransform(date)).thenReturn(mockedTransform); + // WHEN + final FieldStaticTransform actualTransform = reversedProvider.getStaticTransform(date); + // THEN + Assertions.assertEquals(expectedTransform, actualTransform); + } + @Test public void testIdentity() { RandomGenerator random = new Well19937a(0x87c3a5c51fb0235el); diff --git a/src/test/java/org/orekit/frames/VersionedITRFProviderTest.java b/src/test/java/org/orekit/frames/VersionedITRFProviderTest.java new file mode 100644 index 0000000000..78559c408d --- /dev/null +++ b/src/test/java/org/orekit/frames/VersionedITRFProviderTest.java @@ -0,0 +1,110 @@ +/* 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.frames; + + +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.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; + +class VersionedITRFProviderTest { + + @Test + void testGetKinematicTransformNullConverter() { + // GIVEN + final ITRFVersion itrfVersion = ITRFVersion.ITRF_2020; + final ITRFProvider mockedRawProvider = Mockito.mock(ITRFProvider.class); + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final KinematicTransform expectedTransform = Mockito.mock(KinematicTransform.class); + Mockito.when(mockedRawProvider.getKinematicTransform(date)).thenReturn(expectedTransform); + final EOPHistory mockedHistory = Mockito.mock(EOPHistory.class); + Mockito.when(mockedHistory.getITRFVersion(date)).thenReturn(itrfVersion); + Mockito.when(mockedRawProvider.getEOPHistory()).thenReturn(mockedHistory); + final VersionedITRFProvider versionedITRFProvider = new VersionedITRFProvider(itrfVersion, mockedRawProvider, + null); + // WHEN + final KinematicTransform actualTransform = versionedITRFProvider.getKinematicTransform(date); + // THEN + Assertions.assertEquals(expectedTransform, actualTransform); + } + + @Test + @SuppressWarnings("unchecked") + void testFieldGetKinematicTransformNullConverter() { + // GIVEN + final ITRFVersion itrfVersion = ITRFVersion.ITRF_2020; + final ITRFProvider mockedRawProvider = Mockito.mock(ITRFProvider.class); + final ComplexField field = ComplexField.getInstance(); + final FieldAbsoluteDate date = new FieldAbsoluteDate<>(field, AbsoluteDate.ARBITRARY_EPOCH); + final FieldKinematicTransform expectedTransform = Mockito.mock(FieldKinematicTransform.class); + Mockito.when(mockedRawProvider.getKinematicTransform(date)).thenReturn(expectedTransform); + final EOPHistory mockedHistory = Mockito.mock(EOPHistory.class); + Mockito.when(mockedHistory.getITRFVersion(date.toAbsoluteDate())).thenReturn(itrfVersion); + Mockito.when(mockedRawProvider.getEOPHistory()).thenReturn(mockedHistory); + final VersionedITRFProvider versionedITRFProvider = new VersionedITRFProvider(itrfVersion, mockedRawProvider, + null); + // WHEN + final FieldKinematicTransform actualTransform = versionedITRFProvider.getKinematicTransform(date); + // THEN + Assertions.assertEquals(expectedTransform, actualTransform); + } + + @Test + void testGetStaticTransformNullConverter() { + // GIVEN + final ITRFVersion itrfVersion = ITRFVersion.ITRF_2020; + final ITRFProvider mockedRawProvider = Mockito.mock(ITRFProvider.class); + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final StaticTransform expectedTransform = Mockito.mock(StaticTransform.class); + Mockito.when(mockedRawProvider.getStaticTransform(date)).thenReturn(expectedTransform); + final EOPHistory mockedHistory = Mockito.mock(EOPHistory.class); + Mockito.when(mockedHistory.getITRFVersion(date)).thenReturn(itrfVersion); + Mockito.when(mockedRawProvider.getEOPHistory()).thenReturn(mockedHistory); + final VersionedITRFProvider versionedITRFProvider = new VersionedITRFProvider(itrfVersion, mockedRawProvider, + null); + // WHEN + final StaticTransform actualTransform = versionedITRFProvider.getStaticTransform(date); + // THEN + Assertions.assertEquals(expectedTransform, actualTransform); + } + + @Test + @SuppressWarnings("unchecked") + void testFieldGetStaticTransformNullConverter() { + // GIVEN + final ITRFVersion itrfVersion = ITRFVersion.ITRF_2020; + final ITRFProvider mockedRawProvider = Mockito.mock(ITRFProvider.class); + final ComplexField field = ComplexField.getInstance(); + final FieldAbsoluteDate date = new FieldAbsoluteDate<>(field, AbsoluteDate.ARBITRARY_EPOCH); + final FieldStaticTransform expectedTransform = Mockito.mock(FieldStaticTransform.class); + Mockito.when(mockedRawProvider.getStaticTransform(date)).thenReturn(expectedTransform); + final EOPHistory mockedHistory = Mockito.mock(EOPHistory.class); + Mockito.when(mockedHistory.getITRFVersion(date.toAbsoluteDate())).thenReturn(itrfVersion); + Mockito.when(mockedRawProvider.getEOPHistory()).thenReturn(mockedHistory); + final VersionedITRFProvider versionedITRFProvider = new VersionedITRFProvider(itrfVersion, mockedRawProvider, + null); + // WHEN + final FieldStaticTransform actualTransform = versionedITRFProvider.getStaticTransform(date); + // THEN + Assertions.assertEquals(expectedTransform, actualTransform); + } + +} \ No newline at end of file diff --git a/src/test/java/org/orekit/geometry/fov/AbstractSmoothFieldOfViewTest.java b/src/test/java/org/orekit/geometry/fov/AbstractSmoothFieldOfViewTest.java index a9e345698e..915cd7b9ac 100644 --- a/src/test/java/org/orekit/geometry/fov/AbstractSmoothFieldOfViewTest.java +++ b/src/test/java/org/orekit/geometry/fov/AbstractSmoothFieldOfViewTest.java @@ -97,7 +97,7 @@ protected void doTestFootprint(final SmoothFieldOfView fov, final AttitudeProvid Assertions.assertEquals(0.0, loop.get(i).getAltitude(), 9.0e-9); - Vector3D los = fovToBody.toStaticTransform().getInverse().transformPosition(earth.transform(loop.get(i))); + Vector3D los = fovToBody.getStaticInverse().transformPosition(earth.transform(loop.get(i))); double offset = Vector3D.angle(fov.getCenter(), los); minOffset = FastMath.min(minOffset, offset); maxOffset = FastMath.max(maxOffset, offset); diff --git a/src/test/java/org/orekit/geometry/fov/PolygonalFieldOfViewTest.java b/src/test/java/org/orekit/geometry/fov/PolygonalFieldOfViewTest.java index a99a048a62..59963a7b1a 100644 --- a/src/test/java/org/orekit/geometry/fov/PolygonalFieldOfViewTest.java +++ b/src/test/java/org/orekit/geometry/fov/PolygonalFieldOfViewTest.java @@ -248,7 +248,7 @@ private void doTest(final PolygonalFieldOfView fov, final AttitudeProvider attit getTrackingCoordinates(state.getPosition(), state.getFrame(), state.getDate()). getElevation(); if (elevation > 0.001) { - Vector3D los = fovToBody.toStaticTransform().getInverse().transformPosition(earth.transform(loop.get(i))); + Vector3D los = fovToBody.getStaticInverse().transformPosition(earth.transform(loop.get(i))); Assertions.assertEquals(-fov.getMargin(), fov.offsetFromBoundary(los, 0.0, VisibilityTrigger.VISIBLE_ONLY_WHEN_FULLY_IN_FOV), 4.0e-15); diff --git a/src/test/java/org/orekit/gnss/IGSUtilsTest.java b/src/test/java/org/orekit/gnss/IGSUtilsTest.java new file mode 100644 index 0000000000..1fbea73649 --- /dev/null +++ b/src/test/java/org/orekit/gnss/IGSUtilsTest.java @@ -0,0 +1,131 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.gnss; + +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.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.frames.EOPBasedTransformProvider; +import org.orekit.frames.FactoryManagedFrame; +import org.orekit.frames.Frame; +import org.orekit.frames.FramesFactory; +import org.orekit.frames.ITRFVersion; +import org.orekit.frames.Predefined; +import org.orekit.frames.VersionedITRF; +import org.orekit.utils.IERSConventions; + +public class IGSUtilsTest { + + @BeforeEach + public void setUp() { + Utils.setDataRoot("gnss"); + } + + @Test + public void testItrfVersion() { + Assertions.assertSame(ITRFVersion.ITRF_1996, getItrfVersion("ITRF96")); + Assertions.assertSame(ITRFVersion.ITRF_2014, getItrfVersion("IGS14")); + Assertions.assertSame(ITRFVersion.ITRF_2020, getItrfVersion("ITR20")); + Assertions.assertSame(ITRFVersion.ITRF_2008, getItrfVersion("SLR08")); + } + + @Test public void testUnknown() { + Assertions.assertSame(Predefined.ITRF_CIO_CONV_2010_ACCURATE_EOP, + ((FactoryManagedFrame) IGSUtils.guessFrame("UNDEF")).getFactoryKey()); + Assertions.assertSame(Predefined.ITRF_CIO_CONV_2010_ACCURATE_EOP, + ((FactoryManagedFrame) IGSUtils.guessFrame("WGS84")).getFactoryKey()); + } + + @Test + public void testIersConvention() { + Assertions.assertSame(IERSConventions.IERS_1996, getConvention("ITRF88")); + Assertions.assertSame(IERSConventions.IERS_1996, getConvention("ITRF89")); + Assertions.assertSame(IERSConventions.IERS_1996, getConvention("ITRF96")); + Assertions.assertSame(IERSConventions.IERS_1996, getConvention("ITRF00")); + Assertions.assertSame(IERSConventions.IERS_2003, getConvention("ITRF05")); + Assertions.assertSame(IERSConventions.IERS_2003, getConvention("ITRF08")); + Assertions.assertSame(IERSConventions.IERS_2010, getConvention("ITRF14")); + Assertions.assertSame(IERSConventions.IERS_2010, getConvention("ITRF20")); + } + + @Test + public void testGcrf() { + Assertions.assertNull(IGSUtils.guessFrame("GCRF").getParent()); + Assertions.assertNull(IGSUtils.guessFrame(" GCRF").getParent()); + Assertions.assertNull(IGSUtils.guessFrame("GCRF ").getParent()); + } + + @Test + public void testEME2000() { + Assertions.assertSame(FramesFactory.getEME2000(), IGSUtils.guessFrame("EME00")); + Assertions.assertSame(FramesFactory.getEME2000(), IGSUtils.guessFrame("EME2K")); + } + + @Test + public void testITRFNames() { + Assertions.assertEquals("IGS89", IGSUtils.frameName(FramesFactory.getITRF(ITRFVersion.ITRF_1989, + IERSConventions.IERS_2010, + false))); + Assertions.assertEquals("IGS96", IGSUtils.frameName(FramesFactory.getITRF(ITRFVersion.ITRF_1996, + IERSConventions.IERS_2010, + false))); + Assertions.assertEquals("IGS00", IGSUtils.frameName(FramesFactory.getITRF(ITRFVersion.ITRF_2000, + IERSConventions.IERS_2010, + false))); + Assertions.assertEquals("IGS05", IGSUtils.frameName(FramesFactory.getITRF(ITRFVersion.ITRF_2005, + IERSConventions.IERS_2010, + false))); + Assertions.assertEquals("IGS14", IGSUtils.frameName(FramesFactory.getITRF(ITRFVersion.ITRF_2014, + IERSConventions.IERS_2010, + false))); + Assertions.assertEquals("IGS20", IGSUtils.frameName(FramesFactory.getITRF(ITRFVersion.ITRF_2020, + IERSConventions.IERS_2010, + false))); + } + + @Test + public void testInertialNames() { + Assertions.assertEquals("GCRF", IGSUtils.frameName(FramesFactory.getGCRF())); + Assertions.assertEquals("EME2K", IGSUtils.frameName(FramesFactory.getEME2000())); + } + + @Test + public void testUnsupportedFrame() { + try { + IGSUtils.frameName(FramesFactory.getMOD(IERSConventions.IERS_2010)); + Assertions.fail("an exception should have been thrown"); + } catch (OrekitException oe) { + Assertions.assertEquals(OrekitMessages.FRAME_NOT_ALLOWED, oe.getSpecifier()); + Assertions.assertEquals("MOD/2010", oe.getParts()[0]); + } + } + + private ITRFVersion getItrfVersion(String key) { + return ((VersionedITRF) IGSUtils.guessFrame(key)).getITRFVersion(); + } + + private IERSConventions getConvention(final String key) { + final Frame frame = IGSUtils.guessFrame(key); + final EOPBasedTransformProvider provider = + (EOPBasedTransformProvider) frame.getTransformProvider(); + return provider.getEOPHistory().getConventions(); + } + +} diff --git a/src/test/java/org/orekit/gnss/attitude/BeidouGeoTest.java b/src/test/java/org/orekit/gnss/attitude/BeidouGeoTest.java index 184f6ce8c9..8d4c69e66a 100644 --- a/src/test/java/org/orekit/gnss/attitude/BeidouGeoTest.java +++ b/src/test/java/org/orekit/gnss/attitude/BeidouGeoTest.java @@ -19,45 +19,45 @@ import org.junit.jupiter.api.Test; -public class BeidouGeoTest extends AbstractGNSSAttitudeProviderTest { +class BeidouGeoTest extends AbstractGNSSAttitudeProviderTest { @Test - public void testPatchedLargeNegativeBeta() { + void testPatchedLargeNegativeBeta() { doTestAxes("patched-eclips/beta-large-negative-BEIDOU-2G.txt", 6.6e-15, 7.8e-16, false); } @Test - public void testPatchedSmallNegativeBeta() { - doTestAxes("patched-eclips/beta-small-negative-BEIDOU-2G.txt", 8.0e-15, 8.3e-16, false); + void testPatchedSmallNegativeBeta() { + doTestAxes("patched-eclips/beta-small-negative-BEIDOU-2G.txt", 8.0e-15, 9.3e-16, false); } @Test - public void testPatchedCrossingBeta() { + void testPatchedCrossingBeta() { doTestAxes("patched-eclips/beta-crossing-BEIDOU-2G.txt", 6.2e-15, 8.6e-16, false); } @Test - public void testPatchedSmallPositiveBeta() { + void testPatchedSmallPositiveBeta() { doTestAxes("patched-eclips/beta-small-positive-BEIDOU-2G.txt", 7.9e-15, 7.1e-16, false); } @Test - public void testOriginalLargeNegativeBeta() { + void testOriginalLargeNegativeBeta() { doTestAxes("original-eclips/beta-large-negative-BEIDOU-2G.txt", 7.6e-4, 7.8e-16, false); } @Test - public void testOriginalSmallNegativeBeta() { - doTestAxes("original-eclips/beta-small-negative-BEIDOU-2G.txt", 5.0e-4, 8.6e-16, false); + void testOriginalSmallNegativeBeta() { + doTestAxes("original-eclips/beta-small-negative-BEIDOU-2G.txt", 5.0e-4, 9.3e-16, false); } @Test - public void testOriginalCrossingBeta() { + void testOriginalCrossingBeta() { doTestAxes("original-eclips/beta-crossing-BEIDOU-2G.txt", 9.0e-4, 8.6e-16, false); } @Test - public void testOriginalSmallPositiveBeta() { + void testOriginalSmallPositiveBeta() { doTestAxes("original-eclips/beta-small-positive-BEIDOU-2G.txt", 9.4e-4, 7.1e-16, false); } diff --git a/src/test/java/org/orekit/gnss/attitude/BeidouIGSOTest.java b/src/test/java/org/orekit/gnss/attitude/BeidouIGSOTest.java index 10a4c28e7e..aecd741340 100644 --- a/src/test/java/org/orekit/gnss/attitude/BeidouIGSOTest.java +++ b/src/test/java/org/orekit/gnss/attitude/BeidouIGSOTest.java @@ -19,45 +19,45 @@ import org.junit.jupiter.api.Test; -public class BeidouIGSOTest extends AbstractGNSSAttitudeProviderTest { +class BeidouIGSOTest extends AbstractGNSSAttitudeProviderTest { @Test - public void testPatchedLargeNegativeBeta() { + void testPatchedLargeNegativeBeta() { doTestAxes("patched-eclips/beta-large-negative-BEIDOU-2I.txt", 7.8e-15, 1.1e-15, false); } @Test - public void testPatchedSmallNegativeBeta() { - doTestAxes("patched-eclips/beta-small-negative-BEIDOU-2I.txt", 8.1e-15, 9.3e-16, false); + void testPatchedSmallNegativeBeta() { + doTestAxes("patched-eclips/beta-small-negative-BEIDOU-2I.txt", 8.1e-15, 9.5e-16, false); } @Test - public void testPatchedSmallPositiveBeta() { - doTestAxes("patched-eclips/beta-small-positive-BEIDOU-2I.txt", 7.5e-15, 9.9e-16, false); + void testPatchedSmallPositiveBeta() { + doTestAxes("patched-eclips/beta-small-positive-BEIDOU-2I.txt", 8.2e-15, 9.9e-16, false); } @Test - public void testPatchedLargePositiveBeta() { + void testPatchedLargePositiveBeta() { doTestAxes("patched-eclips/beta-large-positive-BEIDOU-2I.txt", 7.9e-15, 1.2e-15, false); } @Test - public void testOriginalLargeNegativeBeta() { + void testOriginalLargeNegativeBeta() { doTestAxes("original-eclips/beta-large-negative-BEIDOU-2I.txt", 7.8e-15, 1.1e-15, false); } @Test - public void testOriginalSmallNegativeBeta() { - doTestAxes("original-eclips/beta-small-negative-BEIDOU-2I.txt", 3.9e-3, 9.3e-16, false); + void testOriginalSmallNegativeBeta() { + doTestAxes("original-eclips/beta-small-negative-BEIDOU-2I.txt", 3.9e-3, 9.5e-16, false); } @Test - public void testOriginalSmallPositiveBeta() { + void testOriginalSmallPositiveBeta() { doTestAxes("original-eclips/beta-small-positive-BEIDOU-2I.txt", 4.5e-3, 9.9e-16, false); } @Test - public void testOriginalLargePositiveBeta() { + void testOriginalLargePositiveBeta() { doTestAxes("original-eclips/beta-large-positive-BEIDOU-2I.txt", 7.9e-15, 1.2e-15, false); } diff --git a/src/test/java/org/orekit/gnss/attitude/BeidouMeoTest.java b/src/test/java/org/orekit/gnss/attitude/BeidouMeoTest.java index 4a39228166..c16b877892 100644 --- a/src/test/java/org/orekit/gnss/attitude/BeidouMeoTest.java +++ b/src/test/java/org/orekit/gnss/attitude/BeidouMeoTest.java @@ -18,45 +18,45 @@ import org.junit.jupiter.api.Test; -public class BeidouMeoTest extends AbstractGNSSAttitudeProviderTest { +class BeidouMeoTest extends AbstractGNSSAttitudeProviderTest { @Test - public void testPatchedLargeNegativeBeta() { - doTestAxes("patched-eclips/beta-large-negative-BEIDOU-2M.txt", 9.2e-15, 1.1e-15, false); + void testPatchedLargeNegativeBeta() { + doTestAxes("patched-eclips/beta-large-negative-BEIDOU-2M.txt", 9.2e-15, 1.2e-15, false); } @Test - public void testPatchedSmallNegativeBeta() { + void testPatchedSmallNegativeBeta() { doTestAxes("patched-eclips/beta-small-negative-BEIDOU-2M.txt", 7.7e-15, 9.4e-16, false); } @Test - public void testPatchedSmallPositiveBeta() { + void testPatchedSmallPositiveBeta() { doTestAxes("patched-eclips/beta-small-positive-BEIDOU-2M.txt", 9.5e-15, 9.6e-16, false); } @Test - public void testPatchedLargePositiveBeta() { + void testPatchedLargePositiveBeta() { doTestAxes("patched-eclips/beta-large-positive-BEIDOU-2M.txt", 8.4e-15, 1.1e-15, false); } @Test - public void testOriginalLargeNegativeBeta() { - doTestAxes("original-eclips/beta-large-negative-BEIDOU-2M.txt", 9.2e-15, 1.1e-15, false); + void testOriginalLargeNegativeBeta() { + doTestAxes("original-eclips/beta-large-negative-BEIDOU-2M.txt", 9.2e-15, 1.2e-15, false); } @Test - public void testOriginalSmallNegativeBeta() { + void testOriginalSmallNegativeBeta() { doTestAxes("original-eclips/beta-small-negative-BEIDOU-2M.txt", 2.2e-3, 9.4e-16, false); } @Test - public void testOriginalSmallPositiveBeta() { + void testOriginalSmallPositiveBeta() { doTestAxes("original-eclips/beta-small-positive-BEIDOU-2M.txt", 2.7e-3, 9.6e-16, false); } @Test - public void testOriginalLargePositiveBeta() { + void testOriginalLargePositiveBeta() { doTestAxes("original-eclips/beta-large-positive-BEIDOU-2M.txt", 8.4e-15, 1.1e-15, false); } diff --git a/src/test/java/org/orekit/gnss/attitude/GPSBlockIIATest.java b/src/test/java/org/orekit/gnss/attitude/GPSBlockIIATest.java index bfc9fd5aa0..85024ea1c7 100644 --- a/src/test/java/org/orekit/gnss/attitude/GPSBlockIIATest.java +++ b/src/test/java/org/orekit/gnss/attitude/GPSBlockIIATest.java @@ -19,45 +19,45 @@ import org.junit.jupiter.api.Test; -public class GPSBlockIIATest extends AbstractGNSSAttitudeProviderTest { +class GPSBlockIIATest extends AbstractGNSSAttitudeProviderTest { @Test - public void testPatchedLargeNegativeBeta() { - doTestAxes("patched-eclips/beta-large-negative-BLOCK-IIA.txt", 6.1e-15, 8.7e-16, false); + void testPatchedLargeNegativeBeta() { + doTestAxes("patched-eclips/beta-large-negative-BLOCK-IIA.txt", 6.3e-15, 1.1e-15, false); } @Test - public void testPatchedSmallNegativeBeta() { + void testPatchedSmallNegativeBeta() { doTestAxes("patched-eclips/beta-small-negative-BLOCK-IIA.txt", 5.1e-6, 1.2e-15, false); } @Test - public void testPatchedCrossingBeta() { + void testPatchedCrossingBeta() { doTestAxes("patched-eclips/beta-crossing-BLOCK-IIA.txt", 5.2e-4, 8.3e-16, false); } @Test - public void testPatchedSmallPositiveBeta() { + void testPatchedSmallPositiveBeta() { doTestAxes("patched-eclips/beta-small-positive-BLOCK-IIA.txt", 1.1e-5, 1.1e-15, false); } @Test - public void testPatchedLargePositiveBeta() { + void testPatchedLargePositiveBeta() { doTestAxes("patched-eclips/beta-large-positive-BLOCK-IIA.txt", 7.2e-15, 8.8e-16, false); } @Test - public void testOriginalLargeNegativeBeta() { - doTestAxes("original-eclips/beta-large-negative-BLOCK-IIA.txt", 6.1e-15, 8.7e-16, false); + void testOriginalLargeNegativeBeta() { + doTestAxes("original-eclips/beta-large-negative-BLOCK-IIA.txt", 6.3e-15, 1.1e-15, false); } @Test - public void testOriginalSmallNegativeBeta() { + void testOriginalSmallNegativeBeta() { doTestAxes("original-eclips/beta-small-negative-BLOCK-IIA.txt", 1.2e-3, 1.2e-15, false); } @Test - public void testOriginalCrossingBeta() { + void testOriginalCrossingBeta() { // the very high threshold (2.13 radians) is due to a probable bug in original eclips // the output of the routine is limited to the x-sat vector, the yaw angle itself // is not output. However, in some cases the x-sat vector is not normalized at all. @@ -88,12 +88,12 @@ public void testOriginalCrossingBeta() { } @Test - public void testOriginalSmallPositiveBeta() { + void testOriginalSmallPositiveBeta() { doTestAxes("original-eclips/beta-small-positive-BLOCK-IIA.txt", 1.2e-3, 1.1e-15, false); } @Test - public void testOriginalLargePositiveBeta() { + void testOriginalLargePositiveBeta() { doTestAxes("original-eclips/beta-large-positive-BLOCK-IIA.txt", 7.2e-15, 8.8e-16, false); } diff --git a/src/test/java/org/orekit/gnss/attitude/GPSBlockIIFTest.java b/src/test/java/org/orekit/gnss/attitude/GPSBlockIIFTest.java index 95f28626a4..babaea5526 100644 --- a/src/test/java/org/orekit/gnss/attitude/GPSBlockIIFTest.java +++ b/src/test/java/org/orekit/gnss/attitude/GPSBlockIIFTest.java @@ -19,45 +19,45 @@ import org.junit.jupiter.api.Test; -public class GPSBlockIIFTest extends AbstractGNSSAttitudeProviderTest { +class GPSBlockIIFTest extends AbstractGNSSAttitudeProviderTest { @Test - public void testPatchedLargeNegativeBeta() { + void testPatchedLargeNegativeBeta() { doTestAxes("patched-eclips/beta-large-negative-BLOCK-IIF.txt", 6.8e-15, 7.6e-16, false); } @Test - public void testPatchedSmallNegativeBeta() { + void testPatchedSmallNegativeBeta() { doTestAxes("patched-eclips/beta-small-negative-BLOCK-IIF.txt", 1.8e-12, 9.2e-16, false); } @Test - public void testPatchedCrossingBeta() { + void testPatchedCrossingBeta() { doTestAxes("patched-eclips/beta-crossing-BLOCK-IIF.txt", 5.7e-4, 7.8e-16, false); } @Test - public void testPatchedSmallPositiveBeta() { + void testPatchedSmallPositiveBeta() { doTestAxes("patched-eclips/beta-small-positive-BLOCK-IIF.txt", 2.9e-12, 6.0e-16, false); } @Test - public void testPatchedLargePositiveBeta() { - doTestAxes("patched-eclips/beta-large-positive-BLOCK-IIF.txt", 7.4e-15, 6.7e-16, false); + void testPatchedLargePositiveBeta() { + doTestAxes("patched-eclips/beta-large-positive-BLOCK-IIF.txt", 7.4e-15, 7.0e-16, false); } @Test - public void testOriginalLargeNegativeBeta() { + void testOriginalLargeNegativeBeta() { doTestAxes("original-eclips/beta-large-negative-BLOCK-IIF.txt", 6.8e-15, 7.6e-16, false); } @Test - public void testOriginalSmallNegativeBeta() { + void testOriginalSmallNegativeBeta() { doTestAxes("original-eclips/beta-small-negative-BLOCK-IIF.txt", 2.8e-3, 9.2e-16, false); } @Test - public void testOriginalCrossingBeta() { + void testOriginalCrossingBeta() { // the very high threshold (0.24 radians) is due to a probable bug in original eclips // the output of the routine is limited to the x-sat vector, the yaw angle itself // is not output. However, in some cases the x-sat vector is not normalised at all. @@ -79,13 +79,13 @@ public void testOriginalCrossingBeta() { } @Test - public void testOriginalSmallPositiveBeta() { + void testOriginalSmallPositiveBeta() { doTestAxes("original-eclips/beta-small-positive-BLOCK-IIF.txt", 2.8e-4, 6.0e-16, false); } @Test - public void testOriginalLargePositiveBeta() { - doTestAxes("original-eclips/beta-large-positive-BLOCK-IIF.txt", 7.4e-15, 6.7e-16, false); + void testOriginalLargePositiveBeta() { + doTestAxes("original-eclips/beta-large-positive-BLOCK-IIF.txt", 7.4e-15, 7.0e-16, false); } } diff --git a/src/test/java/org/orekit/gnss/attitude/GenericGNSSTest.java b/src/test/java/org/orekit/gnss/attitude/GenericGNSSTest.java index 9f90dfa6f2..6664b43d1f 100644 --- a/src/test/java/org/orekit/gnss/attitude/GenericGNSSTest.java +++ b/src/test/java/org/orekit/gnss/attitude/GenericGNSSTest.java @@ -19,105 +19,105 @@ import org.junit.jupiter.api.Test; -public class GenericGNSSTest extends AbstractGNSSAttitudeProviderTest { +class GenericGNSSTest extends AbstractGNSSAttitudeProviderTest { @Test - public void testPatchedLargeNegativeBetaGalileo() { + void testPatchedLargeNegativeBetaGalileo() { doTestAxes("patched-eclips/beta-large-negative-GALILEO.txt", 7.3e-15, 5.8e-16, true); } @Test - public void testPatchedLargePositiveBetaGalileo() { + void testPatchedLargePositiveBetaGalileo() { doTestAxes("patched-eclips/beta-large-positive-GALILEO.txt", 7.3e-15, 7.9e-16, true); } @Test - public void testPatchedLargeNegativeBetaGlonass() { + void testPatchedLargeNegativeBetaGlonass() { doTestAxes("patched-eclips/beta-large-negative-GLONASS.txt", 7.2e-15, 1.1e-15, true); } @Test - public void testPatchedLargePositiveBetaGLONASS() { + void testPatchedLargePositiveBetaGLONASS() { doTestAxes("patched-eclips/beta-large-positive-GLONASS.txt", 6.8e-15, 9.2e-16, true); } @Test - public void testPatchedLargeNegativeBetaBlockIIA() { - doTestAxes("patched-eclips/beta-large-negative-BLOCK-IIA.txt", 6.1e-15, 8.7e-16, true); + void testPatchedLargeNegativeBetaBlockIIA() { + doTestAxes("patched-eclips/beta-large-negative-BLOCK-IIA.txt", 6.3e-15, 1.1e-15, true); } @Test - public void testPatchedLargePositiveBetaBlockIIA() { + void testPatchedLargePositiveBetaBlockIIA() { doTestAxes("patched-eclips/beta-large-positive-BLOCK-IIA.txt", 7.2e-15, 8.8e-16, true); } @Test - public void testPatchedLargeNegativeBetaBlockIIF() { + void testPatchedLargeNegativeBetaBlockIIF() { doTestAxes("patched-eclips/beta-large-negative-BLOCK-IIF.txt", 6.8e-15, 7.6e-16, true); } @Test - public void testPatchedLargePositiveBetaBlockIIF() { - doTestAxes("patched-eclips/beta-large-positive-BLOCK-IIF.txt", 7.4e-15, 6.7e-16, true); + void testPatchedLargePositiveBetaBlockIIF() { + doTestAxes("patched-eclips/beta-large-positive-BLOCK-IIF.txt", 7.4e-15, 7.0e-16, true); } @Test - public void testPatchedLargeNegativeBetaBlockIIR() { + void testPatchedLargeNegativeBetaBlockIIR() { doTestAxes("patched-eclips/beta-large-negative-BLOCK-IIR.txt", 8.0e-15, 8.7e-16, true); } @Test - public void testPatchedLargePositiveBetaBlockIIR() { + void testPatchedLargePositiveBetaBlockIIR() { doTestAxes("patched-eclips/beta-large-positive-BLOCK-IIR.txt", 6.7e-15, 9.1e-16, true); } @Test - public void testOriginalLargeNegativeBetaGalileo() { + void testOriginalLargeNegativeBetaGalileo() { doTestAxes("original-eclips/beta-large-negative-GALILEO.txt", 7.3e-15, 5.8e-16, true); } @Test - public void testOriginalLargePositiveBetaGalileo() { + void testOriginalLargePositiveBetaGalileo() { doTestAxes("original-eclips/beta-large-positive-GALILEO.txt", 7.3e-15, 7.9e-16, true); } @Test - public void testOriginalLargeNegativeBetaGlonass() { + void testOriginalLargeNegativeBetaGlonass() { doTestAxes("original-eclips/beta-large-negative-GLONASS.txt", 7.2e-15, 1.1e-15, true); } @Test - public void testOriginalLargePositiveBetaGLONASS() { + void testOriginalLargePositiveBetaGLONASS() { doTestAxes("original-eclips/beta-large-positive-GLONASS.txt", 6.8e-15, 9.2e-16, true); } @Test - public void testOriginalLargeNegativeBetaBlockIIA() { - doTestAxes("original-eclips/beta-large-negative-BLOCK-IIA.txt", 6.1e-15, 8.7e-16, true); + void testOriginalLargeNegativeBetaBlockIIA() { + doTestAxes("original-eclips/beta-large-negative-BLOCK-IIA.txt", 6.3e-15, 1.1e-15, true); } @Test - public void testOriginalLargePositiveBetaBlockIIA() { + void testOriginalLargePositiveBetaBlockIIA() { doTestAxes("original-eclips/beta-large-positive-BLOCK-IIA.txt", 7.2e-15, 8.8e-16, true); } @Test - public void testOriginalLargeNegativeBetaBlockIIF() { + void testOriginalLargeNegativeBetaBlockIIF() { doTestAxes("original-eclips/beta-large-negative-BLOCK-IIF.txt", 6.8e-15, 7.6e-16, true); } @Test - public void testOriginalLargePositiveBetaBlockIIF() { - doTestAxes("original-eclips/beta-large-positive-BLOCK-IIF.txt", 7.4e-15, 6.7e-16, true); + void testOriginalLargePositiveBetaBlockIIF() { + doTestAxes("original-eclips/beta-large-positive-BLOCK-IIF.txt", 7.4e-15, 7.0e-16, true); } @Test - public void testOriginalLargeNegativeBetaBlockIIR() { + void testOriginalLargeNegativeBetaBlockIIR() { doTestAxes("original-eclips/beta-large-negative-BLOCK-IIR.txt", 8.0e-15, 8.8e-16, true); } @Test - public void testOriginalLargePositiveBetaBlockIIR() { + void testOriginalLargePositiveBetaBlockIIR() { doTestAxes("original-eclips/beta-large-positive-BLOCK-IIR.txt", 6.7e-15, 9.1e-16, true); } diff --git a/src/test/java/org/orekit/gnss/attitude/GlonassTest.java b/src/test/java/org/orekit/gnss/attitude/GlonassTest.java index a0b1226af8..b0ce0316c4 100644 --- a/src/test/java/org/orekit/gnss/attitude/GlonassTest.java +++ b/src/test/java/org/orekit/gnss/attitude/GlonassTest.java @@ -19,45 +19,45 @@ import org.junit.jupiter.api.Test; -public class GlonassTest extends AbstractGNSSAttitudeProviderTest { +class GlonassTest extends AbstractGNSSAttitudeProviderTest { @Test - public void testPatchedLargeNegativeBeta() { + void testPatchedLargeNegativeBeta() { doTestAxes("patched-eclips/beta-large-negative-GLONASS.txt", 7.2e-15, 1.1e-15, false); } @Test - public void testPatchedSmallNegativeBeta() { + void testPatchedSmallNegativeBeta() { doTestAxes("patched-eclips/beta-small-negative-GLONASS.txt", 7.8e-11, 9.8e-16, false); } @Test - public void testPatchedCrossingBeta() { - doTestAxes("patched-eclips/beta-crossing-GLONASS.txt", 5.2e-6, 9.8e-16, false); + void testPatchedCrossingBeta() { + doTestAxes("patched-eclips/beta-crossing-GLONASS.txt", 5.2e-6, 1.1e-15, false); } @Test - public void testPatchedSmallPositiveBeta() { + void testPatchedSmallPositiveBeta() { doTestAxes("patched-eclips/beta-small-positive-GLONASS.txt", 2.4e-12, 7.4e-16, false); } @Test - public void testPatchedLargePositiveBeta() { + void testPatchedLargePositiveBeta() { doTestAxes("patched-eclips/beta-large-positive-GLONASS.txt", 6.8e-15, 9.2e-16, false); } @Test - public void testOriginalLargeNegativeBeta() { + void testOriginalLargeNegativeBeta() { doTestAxes("original-eclips/beta-large-negative-GLONASS.txt", 7.2e-15, 1.1e-15, false); } @Test - public void testOriginalSmallNegativeBeta() { + void testOriginalSmallNegativeBeta() { doTestAxes("original-eclips/beta-small-negative-GLONASS.txt", 1.6e-4, 9.8e-16, false); } @Test - public void testOriginalCrossingBeta() { + void testOriginalCrossingBeta() { // the very high threshold (0.54 radians) is due to a probable bug in original eclips // the output of the routine is limited to the x-sat vector, the yaw angle itself // is not output. However, in some cases the x-sat vector is not normalized at all. @@ -75,16 +75,16 @@ public void testOriginalCrossingBeta() { // As a conclusion, we consider here that the reference output is wrong and that // Orekit behavior is correct, so we increased the threshold so the test pass, // and wrote this big comment to explain the situation - doTestAxes("original-eclips/beta-crossing-GLONASS.txt", 0.54, 9.8e-16, false); + doTestAxes("original-eclips/beta-crossing-GLONASS.txt", 0.54, 1.1e-15, false); } @Test - public void testOriginalSmallPositiveBeta() { + void testOriginalSmallPositiveBeta() { doTestAxes("original-eclips/beta-small-positive-GLONASS.txt", 1.6e-4, 7.4e-16, false); } @Test - public void testOriginalLargePositiveBeta() { + void testOriginalLargePositiveBeta() { doTestAxes("original-eclips/beta-large-positive-GLONASS.txt", 6.8e-15, 9.2e-16, false); } diff --git a/src/test/java/org/orekit/models/earth/atmosphere/AtmosphereTest.java b/src/test/java/org/orekit/models/earth/atmosphere/AtmosphereTest.java index fc3d37ec96..57858892d4 100644 --- a/src/test/java/org/orekit/models/earth/atmosphere/AtmosphereTest.java +++ b/src/test/java/org/orekit/models/earth/atmosphere/AtmosphereTest.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2024 Romain Serra +/* 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. @@ -78,6 +78,8 @@ void testGetVelocityNonConstantFieldAbsoluteDate() { private static class TestAtmosphere implements Atmosphere { + private static final long serialVersionUID = 1L; + @Override public Frame getFrame() { return FramesFactory.getITRF(IERSConventions.IERS_2003, false); @@ -91,7 +93,7 @@ public double getDensity(AbsoluteDate date, Vector3D position, Frame frame) { @Override public > T getDensity(FieldAbsoluteDate date, FieldVector3D position, Frame frame) { final CalculusFieldElement zero = date.getField().getZero(); - return zero.add(getDensity(date.toAbsoluteDate(), position.toVector3D(), frame)); + return zero.newInstance(getDensity(date.toAbsoluteDate(), position.toVector3D(), frame)); } } diff --git a/src/test/java/org/orekit/models/earth/atmosphere/DTM2000Test.java b/src/test/java/org/orekit/models/earth/atmosphere/DTM2000Test.java index f9f759e8e4..c501c16e29 100644 --- a/src/test/java/org/orekit/models/earth/atmosphere/DTM2000Test.java +++ b/src/test/java/org/orekit/models/earth/atmosphere/DTM2000Test.java @@ -16,27 +16,36 @@ */ package org.orekit.models.earth.atmosphere; +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.geometry.euclidean.threed.RotationConvention; 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; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.orekit.SolarInputs97to05; import org.orekit.Utils; +import org.orekit.bodies.CelestialBody; import org.orekit.bodies.CelestialBodyFactory; import org.orekit.bodies.OneAxisEllipsoid; import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; import org.orekit.frames.Transform; +import org.orekit.models.earth.ReferenceEllipsoid; import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; import org.orekit.time.TimeScale; import org.orekit.time.TimeScalesFactory; import org.orekit.utils.Constants; import org.orekit.utils.IERSConventions; import org.orekit.utils.PVCoordinatesProvider; +import org.orekit.utils.TimeStampedFieldPVCoordinates; +import org.orekit.utils.TimeStampedPVCoordinates; import java.util.TimeZone; @@ -192,6 +201,55 @@ public void testField() { } + /** Test issue 1365: NaN appears during integration due to bad computation of density. */ + @Test + public void testIssue1365() { + + // GIVEN + // ----- + + // Build the input params provider + final DTM2000InputParameters ip = new InputParamsIssue1365(); + + // Prepare field + final Field field = Binary64Field.getInstance(); + + // Build the date + final AbsoluteDate date = new AbsoluteDate("2005-09-08T19:19:10.000", TimeScalesFactory.getUTC()); + final FieldAbsoluteDate fieldDate = new FieldAbsoluteDate<>(field, date); + + // Sun position at "date" in J2000 + final Frame itrf = FramesFactory.getITRF(IERSConventions.IERS_2010, true); + final Vector3D sunPositionItrf = new Vector3D(-5.230565928624774E10, -1.4059879929943967E11, 1.4263029155223734E10); + + // Get Sun at date + final CelestialBody sunAtDate = new SunPositionIssue1365(sunPositionItrf, itrf); + + // Get Earth body shape + + final OneAxisEllipsoid earth = ReferenceEllipsoid.getWgs84(itrf); + // Build the model + final DTM2000 atm = new DTM2000(ip, sunAtDate, earth); + + // Set the position & frame + final Vector3D position = new Vector3D(4355380.654425714, 2296727.21938809, -4575242.76838552); + final FieldVector3D fieldPosition = new FieldVector3D<>(field, position); + final Frame j2000 = FramesFactory.getEME2000(); + + // WHEN + // ---- + + final double density = atm.getDensity(date, position, j2000); + final Binary64 fieldDensity = atm.getDensity(fieldDate, fieldPosition, j2000); + + // THEN + // ---- + + // Check that densities are not NaN + Assertions.assertFalse(Double.isNaN(density)); + Assertions.assertFalse(Double.isNaN(fieldDensity.getReal())); + } + /** Test issue 539. Density computation should be independent of user's default time zone. * See issue 539 on Orekit forge. */ @@ -239,4 +297,55 @@ public void setUp() { Utils.setDataRoot("regular-data"); } + /** DTM2000 solar activity input parameters for testIssue1365. */ + @SuppressWarnings("serial") + private static class InputParamsIssue1365 implements DTM2000InputParameters { + @Override + public AbsoluteDate getMinDate() { return new AbsoluteDate(2005, 9, 8, TimeScalesFactory.getUTC()); } + @Override + public AbsoluteDate getMaxDate() { return new AbsoluteDate(2005, 9, 9, TimeScalesFactory.getUTC()); } + @Override + public double getInstantFlux(AbsoluteDate date) { return 587.9532986111111; } + @Override + public double getMeanFlux(AbsoluteDate date) { return 99.5; } + @Override + public double getThreeHourlyKP(AbsoluteDate date) { return 1.7; } + @Override + public double get24HoursKp(AbsoluteDate date) { return 1.5875; } + } + + /** Sun position for testIssue1365. */ + @SuppressWarnings("serial") + private static class SunPositionIssue1365 implements CelestialBody { + + private final Vector3D sunPositionItrf; + private final Frame itrf; + + /** Constructor. + * @param sunPositionItrf + * @param itrf + */ + private SunPositionIssue1365(Vector3D sunPositionItrf, + Frame itrf) { + this.sunPositionItrf = sunPositionItrf; + this.itrf = itrf; + } + + @Override + public TimeStampedPVCoordinates getPVCoordinates(AbsoluteDate date, Frame frame) { + return new TimeStampedPVCoordinates(date, sunPositionItrf, Vector3D.ZERO); + } + @Override + public > TimeStampedFieldPVCoordinates getPVCoordinates(FieldAbsoluteDate date, Frame frame) { + return new TimeStampedFieldPVCoordinates(date, date.getField().getOne(), getPVCoordinates(date.toAbsoluteDate(), frame)); + } + @Override + public String getName() { return "SUN"; } + @Override + public Frame getInertiallyOrientedFrame() { return itrf; } + @Override + public double getGM() { return Constants.JPL_SSD_SUN_GM; } + @Override + public Frame getBodyOrientedFrame() { return itrf; } + } } diff --git a/src/test/java/org/orekit/models/earth/atmosphere/HarrisPriesterTest.java b/src/test/java/org/orekit/models/earth/atmosphere/HarrisPriesterTest.java index d47d100b0e..7f6be52b6c 100644 --- a/src/test/java/org/orekit/models/earth/atmosphere/HarrisPriesterTest.java +++ b/src/test/java/org/orekit/models/earth/atmosphere/HarrisPriesterTest.java @@ -20,6 +20,8 @@ import org.hipparchus.analysis.differentiation.DSFactory; import org.hipparchus.analysis.differentiation.DerivativeStructure; import org.hipparchus.analysis.differentiation.FiniteDifferencesDifferentiator; +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.AfterEach; @@ -39,9 +41,10 @@ import org.orekit.time.TimeComponents; import org.orekit.time.TimeScale; import org.orekit.time.TimeScalesFactory; +import org.orekit.utils.Constants; import org.orekit.utils.PVCoordinatesProvider; -public class HarrisPriesterTest { +class HarrisPriesterTest { // Sun private PVCoordinatesProvider sun; @@ -59,7 +62,7 @@ public class HarrisPriesterTest { private AbsoluteDate date; @Test - public void testStandard() { + void testStandard() { final HarrisPriester hp = new HarrisPriester(sun, earth); @@ -75,7 +78,7 @@ public void testStandard() { } @Test - public void testParameterN() { + void testParameterN() { final HarrisPriester hp = new HarrisPriester(sun, earth); @@ -104,7 +107,7 @@ public void testParameterN() { } @Test - public void testMaxAlt() { + void testMaxAlt() { final HarrisPriester hp = new HarrisPriester(sun, earth); @@ -119,7 +122,7 @@ public void testMaxAlt() { } @Test - public void testUserTab() { + void testUserTab() { final double[][] userTab = { {100000., 4.974e+02, 4.974e+02}, @@ -208,7 +211,7 @@ public void testUserTab() { } @Test - public void testOutOfRange() { + void testOutOfRange() { Assertions.assertThrows(OrekitException.class, () -> { final HarrisPriester hp = new HarrisPriester(sun, earth); @@ -222,7 +225,24 @@ public void testOutOfRange() { } @Test - public void testVelocityDerivative() { + void testGetDensityField() { + // GIVEN + final ComplexField field = ComplexField.getInstance(); + final HarrisPriester hp = new HarrisPriester(sun, earth); + final Frame frame = FramesFactory.getGCRF(); + final FieldAbsoluteDate fieldDate = new FieldAbsoluteDate<>(field, date); + // WHEN + for (double height = 300e3; height <= 2000e3; height += 100) { + final Vector3D position = new Vector3D(Constants.EGM96_EARTH_EQUATORIAL_RADIUS + height, 0., 0.); + final FieldVector3D fieldPosition = new FieldVector3D<>(field, position); + final Complex fieldDensity = hp.getDensity(fieldDate, fieldPosition, frame); + final double density = hp.getDensity(date, position, frame); + Assertions.assertEquals(density, fieldDensity.getReal()); + } + } + + @Test + void testVelocityDerivative() { final Frame eme2000 = FramesFactory.getEME2000(); final HarrisPriester hp = new HarrisPriester(sun, earth); final Vector3D pos = earth.getBodyFrame().getStaticTransformTo(eme2000, date). diff --git a/src/test/java/org/orekit/models/earth/atmosphere/NRLMSISE00Test.java b/src/test/java/org/orekit/models/earth/atmosphere/NRLMSISE00Test.java index 640962eda5..9e78d5e0c4 100644 --- a/src/test/java/org/orekit/models/earth/atmosphere/NRLMSISE00Test.java +++ b/src/test/java/org/orekit/models/earth/atmosphere/NRLMSISE00Test.java @@ -34,6 +34,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.orekit.Utils; +import org.orekit.bodies.CelestialBody; import org.orekit.bodies.CelestialBodyFactory; import org.orekit.bodies.GeodeticPoint; import org.orekit.bodies.OneAxisEllipsoid; @@ -41,6 +42,7 @@ import org.orekit.errors.OrekitMessages; import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; +import org.orekit.models.earth.ReferenceEllipsoid; import org.orekit.time.AbsoluteDate; import org.orekit.time.DateComponents; import org.orekit.time.FieldAbsoluteDate; @@ -49,6 +51,8 @@ import org.orekit.utils.Constants; import org.orekit.utils.IERSConventions; import org.orekit.utils.PVCoordinatesProvider; +import org.orekit.utils.TimeStampedFieldPVCoordinates; +import org.orekit.utils.TimeStampedPVCoordinates; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; @@ -59,8 +63,8 @@ public class NRLMSISE00Test { @Test public void testLegacy() throws - NoSuchMethodException, SecurityException, InstantiationException, - IllegalAccessException, IllegalArgumentException, InvocationTargetException { + NoSuchMethodException, SecurityException, InstantiationException, + IllegalAccessException, IllegalArgumentException, InvocationTargetException { // Build the model Frame itrf = FramesFactory.getITRF(IERSConventions.IERS_2010, true); @@ -190,9 +194,9 @@ public void testLegacy() throws @Test public void testDensity() throws - InstantiationException, IllegalAccessException, - IllegalArgumentException, InvocationTargetException, - NoSuchMethodException, SecurityException { + InstantiationException, IllegalAccessException, + IllegalArgumentException, InvocationTargetException, + NoSuchMethodException, SecurityException { // Build the input params provider final InputParams ip = new InputParams(); // Get Sun @@ -288,8 +292,8 @@ public void testDensityField() { final double rho = atm.getDensity(date, pos, itrf); final Binary64 rho64 = atm.getDensity(new FieldAbsoluteDate<>(field, date), - new FieldVector3D<>(field.getOne(), pos), - itrf); + new FieldVector3D<>(field.getOne(), pos), + itrf); Assertions.assertEquals(rho, rho64.getReal(), rho * 2.0e-13); @@ -352,22 +356,22 @@ public void testDensityGradient() { Field field = factory3.getDerivativeField(); final DerivativeStructure rhoDS = atm.getDensity(new FieldAbsoluteDate<>(field, date), new FieldVector3D<>(factory3.variable(0, pos.getX()), - factory3.variable(1, pos.getY()), - factory3.variable(2, pos.getZ())), + factory3.variable(1, pos.getY()), + factory3.variable(2, pos.getZ())), itrf); Assertions.assertEquals(rhoX.getValue(), rhoDS.getReal(), rhoX.getValue() * 2.0e-13); Assertions.assertEquals(rhoY.getValue(), rhoDS.getReal(), rhoY.getValue() * 2.0e-13); Assertions.assertEquals(rhoZ.getValue(), rhoDS.getReal(), rhoZ.getValue() * 2.0e-13); Assertions.assertEquals(rhoX.getPartialDerivative(1), - rhoDS.getPartialDerivative(1, 0, 0), - FastMath.abs(2.0e-10 * rhoX.getPartialDerivative(1))); + rhoDS.getPartialDerivative(1, 0, 0), + FastMath.abs(2.0e-10 * rhoX.getPartialDerivative(1))); Assertions.assertEquals(rhoY.getPartialDerivative(1), - rhoDS.getPartialDerivative(0, 1, 0), - FastMath.abs(2.0e-10 * rhoY.getPartialDerivative(1))); + rhoDS.getPartialDerivative(0, 1, 0), + FastMath.abs(2.0e-10 * rhoY.getPartialDerivative(1))); Assertions.assertEquals(rhoZ.getPartialDerivative(1), - rhoDS.getPartialDerivative(0, 0, 1), - FastMath.abs(2.0e-10 * rhoY.getPartialDerivative(1))); + rhoDS.getPartialDerivative(0, 0, 1), + FastMath.abs(2.0e-10 * rhoY.getPartialDerivative(1))); } @@ -547,9 +551,164 @@ public void testgtd7dSwitchesRandom() { doTestVoidMethod(atm, random, "gtd7d", 1.0e-50, 3.0e-14); } + /** Test issue 1365: NaN appears during integration due to bad computation of density. */ + @Test + public void testIssue1365AvoidNan1() { + + // GIVEN + // ----- + + // Build the input params provider + final NRLMSISE00InputParameters ip = new InputParamsIssue1365AvoidNan1(); + + // Prepare field + final Field field = Binary64Field.getInstance(); + + // Build the date + final AbsoluteDate date = new AbsoluteDate("2005-09-10T00:26:47.232", TimeScalesFactory.getUTC()); + final FieldAbsoluteDate fieldDate = new FieldAbsoluteDate<>(field, date); + + // Sun position at "date" in J2000 + final Frame j2000 = FramesFactory.getEME2000(); + final Vector3D sunPosition = new Vector3D(-1.469767604504155E11, 3.030095780108449E10, 1.3136383992886505E10); + + // Get Sun at date + final CelestialBody sunAtDate = new SunPositionIssue1365AvoidNan(sunPosition, j2000); + + // Get Earth body shape + final Frame itrf = FramesFactory.getITRF(IERSConventions.IERS_2010, true); + final OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, + Constants.WGS84_EARTH_FLATTENING, itrf); + // Build the model + final NRLMSISE00 atm = new NRLMSISE00(ip, sunAtDate, earth); + + // Set the position & frame + final Vector3D position = new Vector3D(-2519211.5855839024, -135107.58355852086, -6238233.867025304); + final FieldVector3D fieldPosition = new FieldVector3D<>(field, position); + + // WHEN + // ---- + + final double density = atm.getDensity(date, position, j2000); + final Binary64 fieldDensity = atm.getDensity(fieldDate, fieldPosition, j2000); + + // THEN + // ---- + + // Check that densities are not NaN + Assertions.assertFalse(Double.isNaN(density)); + Assertions.assertFalse(Double.isNaN(fieldDensity.getReal())); + } + + /** Test issue 1365: NaN appears during integration due to bad computation of density. + * Bumping in the first protection against NaNs. + */ + @Test + public void testIssue1365AvoidNan2() { + + // GIVEN + // ----- + + // Build the input params provider + final NRLMSISE00InputParameters ip = new InputParamsIssue1365AvoidNan2(); + + // Prepare field + final Field field = Binary64Field.getInstance(); + + // Build the date + final AbsoluteDate date = new AbsoluteDate("2005-09-10T00:00:50.000", TimeScalesFactory.getUTC()); + final FieldAbsoluteDate fieldDate = new FieldAbsoluteDate<>(field, date); + + // Sun position at "date" in J2000 + final Frame j2000 = FramesFactory.getEME2000(); + final Vector3D sunPosition = new Vector3D(-1.469673551020242E11, 3.0342331223223766E10, 1.3154322212769707E10); + + // Get Sun at date + final CelestialBody sunAtDate = new SunPositionIssue1365AvoidNan(sunPosition, j2000); + + // Get Earth body shape + final Frame itrf = FramesFactory.getITRF(IERSConventions.IERS_2010, true); + final OneAxisEllipsoid earth = ReferenceEllipsoid.getWgs84(itrf); + // Build the model + final NRLMSISE00 atm = new NRLMSISE00(ip, sunAtDate, earth); + + // Set the position & frame + final Vector3D position = new Vector3D(-2094968.638528762, 58037.02211604678, -6396195.230946325); + final FieldVector3D fieldPosition = new FieldVector3D<>(field, position); + + + // WHEN + // ---- + + final double density = atm.getDensity(date, position, j2000); + final Binary64 fieldDensity = atm.getDensity(fieldDate, fieldPosition, j2000); + + // THEN + // ---- + + // Check that densities are not NaN + Assertions.assertFalse(Double.isNaN(density)); + Assertions.assertFalse(Double.isNaN(fieldDensity.getReal())); + } + + /** Test issue 1365: NaN appears during integration due to bad computation of density. + * Throwing exception for crossing 0m altitude boundary. + */ + @Test + public void testIssue1365AvoidNanExceptionRaised() { + + // GIVEN + // ----- + + // Build the input params provider + final NRLMSISE00InputParameters ip = new InputParamsIssue1365AvoidNanExceptionRaised(); + + // Prepare field + final Field field = Binary64Field.getInstance(); + + // Build the date + final AbsoluteDate date = new AbsoluteDate("2005-10-30T06:49:10.000", TimeScalesFactory.getUTC()); + final FieldAbsoluteDate fieldDate = new FieldAbsoluteDate<>(field, date); + + // Sun position at "date" in J2000 + final Frame j2000 = FramesFactory.getEME2000(); + final Vector3D sunPosition = new Vector3D(-1.1883503376589482E11, -8.178200712683229E10, -3.545541568807778E10); + + // Get Sun at date + final CelestialBody sunAtDate = new SunPositionIssue1365AvoidNan(sunPosition, j2000); + + // Get Earth body shape + final Frame itrf = FramesFactory.getITRF(IERSConventions.IERS_2010, true); + final OneAxisEllipsoid earth = ReferenceEllipsoid.getWgs84(itrf); + + // Build the model + final NRLMSISE00 atm = new NRLMSISE00(ip, sunAtDate, earth); + + // Set the dummy position & frame + final Vector3D position = new Vector3D(1.e49, 1.e49, 1.e49); + final FieldVector3D fieldPosition = new FieldVector3D<>(field, position); + + // WHEN / THEN + // ----------- + + try { + atm.getDensity(date, position, j2000); + Assertions.fail("an exception should have been thrown"); + } catch (OrekitException oe) { + Assertions.assertEquals(OrekitMessages.INFINITE_NRLMSISE00_DENSITY, oe.getSpecifier()); + } + + try { + atm.getDensity(fieldDate, fieldPosition, j2000); + Assertions.fail("an exception should have been thrown"); + } catch (OrekitException oe) { + Assertions.assertEquals(OrekitMessages.INFINITE_NRLMSISE00_DENSITY, oe.getSpecifier()); + } + } + private void doTestDoubleMethod(NRLMSISE00 atm, RandomGenerator random, String methodName, double absTolerance, double relTolerance) - { + { try { // Common data for all cases final int doy = 172; @@ -596,7 +755,7 @@ private void doTestDoubleMethod(NRLMSISE00 atm, RandomGenerator random, String m private void doTestVoidMethod(NRLMSISE00 atm, RandomGenerator random, String methodName, double temperatureRelativeTolerance, double densityRelativeTolerance) - { + { try { // Common data for all cases @@ -682,7 +841,7 @@ private Object createOutput(final NRLMSISE00 atm, return cons.newInstance(atm, doy, sec, lat, lon, hl, f107a, f107, ap); } catch (NoSuchMethodException | SecurityException | InstantiationException | - IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { Assertions.fail(e.getLocalizedMessage()); return null; } @@ -692,12 +851,12 @@ private Object createOutput(final NRLMSISE00 atm, private double getOutputDensity(Object o, int index) { try { Method getDensity = getOutputClass(). - getDeclaredMethod("getDensity", Integer.TYPE); + getDeclaredMethod("getDensity", Integer.TYPE); getDensity.setAccessible(true); return ((Double) getDensity.invoke(o, index)).doubleValue(); } catch (NoSuchMethodException | SecurityException | - IllegalAccessException | IllegalArgumentException | - InvocationTargetException e) { + IllegalAccessException | IllegalArgumentException | + InvocationTargetException e) { Assertions.fail(e.getLocalizedMessage()); return Double.NaN; } @@ -708,8 +867,8 @@ private double getOutputTemperature(Object o, int index) { java.lang.reflect.Field temperaturesField = getOutputClass().getDeclaredField("temperatures"); temperaturesField.setAccessible(true); return ((double[]) temperaturesField.get(o))[index]; - } catch (NoSuchFieldException | SecurityException | - IllegalAccessException | IllegalArgumentException e) { + } catch (NoSuchFieldException | SecurityException | + IllegalAccessException | IllegalArgumentException e) { Assertions.fail(e.getLocalizedMessage()); return Double.NaN; } @@ -726,12 +885,12 @@ private Class getFieldOutputClass() { } private > Object createFieldOutput(Fieldfield, - final NRLMSISE00 atm, - final int doy, final double sec, - final double lat, final double lon, - final double hl, - final double f107a, final double f107, - final double[] ap) { + final NRLMSISE00 atm, + final int doy, final double sec, + final double lat, final double lon, + final double hl, + final double f107a, final double f107, + final double[] ap) { try { Class fieldOutputClass = getFieldOutputClass(); Constructor cons = fieldOutputClass.getDeclaredConstructor(NRLMSISE00.class, @@ -751,7 +910,7 @@ private > Object createFieldOutput(Fieldfie field.getZero().add(hl), f107a, f107, ap); } catch (NoSuchMethodException | SecurityException | InstantiationException | - IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { Assertions.fail(e.getLocalizedMessage()); return null; } @@ -761,12 +920,12 @@ private > Object createFieldOutput(Fieldfie private double getFieldOutputDensity(Object o, int index) { try { Method getDensity = getFieldOutputClass(). - getDeclaredMethod("getDensity", Integer.TYPE); + getDeclaredMethod("getDensity", Integer.TYPE); getDensity.setAccessible(true); return ((CalculusFieldElement) getDensity.invoke(o, index)).getReal(); } catch (NoSuchMethodException | SecurityException | - IllegalAccessException | IllegalArgumentException | - InvocationTargetException e) { + IllegalAccessException | IllegalArgumentException | + InvocationTargetException e) { Assertions.fail(e.getLocalizedMessage()); return Double.NaN; } @@ -778,7 +937,7 @@ private double getFieldOutputTemperature(Object o, int index) { temperaturesField.setAccessible(true); return ((CalculusFieldElement[]) temperaturesField.get(o))[index].getReal(); } catch (NoSuchFieldException | SecurityException | - IllegalAccessException | IllegalArgumentException e) { + IllegalAccessException | IllegalArgumentException e) { Assertions.fail(e.getLocalizedMessage()); return Double.NaN; } @@ -786,62 +945,62 @@ private double getFieldOutputTemperature(Object o, int index) { } private void checkLegacy(final int nb, final Object out, final boolean print) - throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { + throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { final double[] tInfRef = {1.250540E+03, 1.166754E+03, 1.239892E+03, 1.027318E+03, - 1.212396E+03, 1.220146E+03, 1.116385E+03, 1.031247E+03, - 1.306052E+03, 1.361868E+03, 1.027318E+03, 1.027318E+03, - 1.027318E+03, 1.027318E+03, 1.027318E+03, 1.426412E+03, - 1.027318E+03}; + 1.212396E+03, 1.220146E+03, 1.116385E+03, 1.031247E+03, + 1.306052E+03, 1.361868E+03, 1.027318E+03, 1.027318E+03, + 1.027318E+03, 1.027318E+03, 1.027318E+03, 1.426412E+03, + 1.027318E+03}; final double[] tAltRef = {1.241416E+03, 1.161710E+03, 1.239891E+03, 2.068878E+02, - 1.208135E+03, 1.212712E+03, 1.112999E+03, 1.024848E+03, - 1.293374E+03, 1.347389E+03, 2.814648E+02, 2.274180E+02, - 2.374389E+02, 2.795551E+02, 2.190732E+02, 1.408608E+03, - 1.934071E+02}; + 1.208135E+03, 1.212712E+03, 1.112999E+03, 1.024848E+03, + 1.293374E+03, 1.347389E+03, 2.814648E+02, 2.274180E+02, + 2.374389E+02, 2.795551E+02, 2.190732E+02, 1.408608E+03, + 1.934071E+02}; final double[] dHeRef = {6.665177E+05, 3.407293E+06, 1.123767E+05, 5.411554E+07, - 1.851122E+06, 8.673095E+05, 5.776251E+05, 3.740304E+05, - 6.748339E+05, 5.528601E+05, 1.375488E+14, 4.427443E+13, - 2.127829E+12, 1.412184E+11, 1.254884E+10, 5.196477E+05, - 4.260860E+07}; + 1.851122E+06, 8.673095E+05, 5.776251E+05, 3.740304E+05, + 6.748339E+05, 5.528601E+05, 1.375488E+14, 4.427443E+13, + 2.127829E+12, 1.412184E+11, 1.254884E+10, 5.196477E+05, + 4.260860E+07}; final double[] dORef = {1.138806E+08, 1.586333E+08, 6.934130E+04, 1.918893E+11, - 1.476555E+08, 1.278862E+08, 6.979139E+07, 4.782720E+07, - 1.245315E+08, 1.198041E+08, 0.000000E+00, 0.000000E+00, - 0.000000E+00, 0.000000E+00, 0.000000E+00, 1.274494E+08, - 1.241342E+11}; + 1.476555E+08, 1.278862E+08, 6.979139E+07, 4.782720E+07, + 1.245315E+08, 1.198041E+08, 0.000000E+00, 0.000000E+00, + 0.000000E+00, 0.000000E+00, 0.000000E+00, 1.274494E+08, + 1.241342E+11}; final double[] dN2Ref = {1.998211E+07, 1.391117E+07, 4.247105E+01, 6.115826E+12, - 1.579356E+07, 1.822577E+07, 1.236814E+07, 5.240380E+06, - 2.369010E+07, 3.495798E+07, 2.049687E+19, 6.597567E+18, - 3.170791E+17, 2.104370E+16, 1.874533E+15, 4.850450E+07, - 4.929562E+12}; + 1.579356E+07, 1.822577E+07, 1.236814E+07, 5.240380E+06, + 2.369010E+07, 3.495798E+07, 2.049687E+19, 6.597567E+18, + 3.170791E+17, 2.104370E+16, 1.874533E+15, 4.850450E+07, + 4.929562E+12}; final double[] dO2Ref = {4.022764E+05, 3.262560E+05, 1.322750E-01, 1.225201E+12, - 2.633795E+05, 2.922214E+05, 2.492868E+05, 1.759875E+05, - 4.911583E+05, 9.339618E+05, 5.498695E+18, 1.769929E+18, - 8.506280E+16, 5.645392E+15, 4.923051E+14, 1.720838E+06, - 1.048407E+12}; + 2.633795E+05, 2.922214E+05, 2.492868E+05, 1.759875E+05, + 4.911583E+05, 9.339618E+05, 5.498695E+18, 1.769929E+18, + 8.506280E+16, 5.645392E+15, 4.923051E+14, 1.720838E+06, + 1.048407E+12}; final double[] dARRef = {3.557465E+03, 1.559618E+03, 2.618848E-05, 6.023212E+10, - 1.588781E+03, 2.402962E+03, 1.405739E+03, 5.501649E+02, - 4.578781E+03, 1.096255E+04, 2.451733E+17, 7.891680E+16, - 3.792741E+15, 2.517142E+14, 2.239685E+13, 2.354487E+04, - 4.993465E+10}; + 1.588781E+03, 2.402962E+03, 1.405739E+03, 5.501649E+02, + 4.578781E+03, 1.096255E+04, 2.451733E+17, 7.891680E+16, + 3.792741E+15, 2.517142E+14, 2.239685E+13, 2.354487E+04, + 4.993465E+10}; final double[] dHRef = {3.475312E+04, 4.854208E+04, 2.016750E+04, 1.059880E+07, - 5.816167E+04, 3.686389E+04, 5.291986E+04, 8.896776E+04, - 3.244595E+04, 2.686428E+04, 0.000000E+00, 0.000000E+00, - 0.000000E+00, 0.000000E+00, 0.000000E+00, 2.500078E+04, - 8.831229E+06}; + 5.816167E+04, 3.686389E+04, 5.291986E+04, 8.896776E+04, + 3.244595E+04, 2.686428E+04, 0.000000E+00, 0.000000E+00, + 0.000000E+00, 0.000000E+00, 0.000000E+00, 2.500078E+04, + 8.831229E+06}; final double[] dNRef = {4.095913E+06, 4.380967E+06, 5.741256E+03, 2.615737E+05, - 5.478984E+06, 3.897276E+06, 1.069814E+06, 1.979741E+06, - 5.370833E+06, 4.889974E+06, 0.000000E+00, 0.000000E+00, - 0.000000E+00, 0.000000E+00, 0.000000E+00, 6.279210E+06, - 2.252516E+05}; + 5.478984E+06, 3.897276E+06, 1.069814E+06, 1.979741E+06, + 5.370833E+06, 4.889974E+06, 0.000000E+00, 0.000000E+00, + 0.000000E+00, 0.000000E+00, 0.000000E+00, 6.279210E+06, + 2.252516E+05}; final double[] dAnORef = {2.667273E+04, 6.956682E+03, 2.374394E+04, 2.819879E-42, - 1.264446E+03, 2.667273E+04, 2.667273E+04, 9.121815E+03, - 2.667273E+04, 2.805445E+04, 0.000000E+00, 0.000000E+00, - 0.000000E+00, 0.000000E+00, 0.000000E+00, 2.667273E+04, - 2.415246E-42}; + 1.264446E+03, 2.667273E+04, 2.667273E+04, 9.121815E+03, + 2.667273E+04, 2.805445E+04, 0.000000E+00, 0.000000E+00, + 0.000000E+00, 0.000000E+00, 0.000000E+00, 2.667273E+04, + 2.415246E-42}; final double[] rhoRef = {4.074714E-15, 5.001846E-15, 2.756772E-18, 3.584426E-10, - 4.809630E-15, 4.355866E-15, 2.470651E-15, 1.571889E-15, - 4.564420E-15, 4.974543E-15, 1.261066E-03, 4.059139E-04, - 1.950822E-05, 1.294709E-06, 1.147668E-07, 5.881940E-15, - 2.914304E-10}; + 4.809630E-15, 4.355866E-15, 2.470651E-15, 1.571889E-15, + 4.564420E-15, 4.974543E-15, 1.261066E-03, 4.059139E-04, + 1.950822E-05, 1.294709E-06, 1.147668E-07, 5.881940E-15, + 2.914304E-10}; final double deltaT = 1.e-2; final double deltaD = 5.e-7; final int id = nb - 1; @@ -914,4 +1073,86 @@ public double[] getAp(AbsoluteDate date) { return new double[] {4., 100., 100., 100., 100., 100., 100.}; } } + + /** NRLMSISE00 solar activity input parameters for testIssue1365AvoidNan1. */ + @SuppressWarnings("serial") + private static class InputParamsIssue1365AvoidNan1 implements NRLMSISE00InputParameters { + + @Override + public AbsoluteDate getMinDate() { return new AbsoluteDate(2005, 9, 10, TimeScalesFactory.getUTC()); } + @Override + public AbsoluteDate getMaxDate() { return new AbsoluteDate(2005, 9, 11, TimeScalesFactory.getUTC()); } + @Override + public double getDailyFlux(AbsoluteDate date) { return 707.6; } + @Override + public double getAverageFlux(AbsoluteDate date) { return 98.8; } + @Override + public double[] getAp(AbsoluteDate date) { return new double[] {33.0, 9.0, 18.0, 32.0, 32.0, 8.875, 7.25}; } + } + + /** NRLMSISE00 solar activity input parameters for testIssue1365AvoidNan2. */ + @SuppressWarnings("serial") + private static class InputParamsIssue1365AvoidNan2 implements NRLMSISE00InputParameters { + + @Override + public AbsoluteDate getMinDate() { return new AbsoluteDate(2005, 9, 10, TimeScalesFactory.getUTC()); } + @Override + public AbsoluteDate getMaxDate() { return new AbsoluteDate(2005, 9, 11, TimeScalesFactory.getUTC()); } + @Override + public double getDailyFlux(AbsoluteDate date) { return 707.6; } + @Override + public double getAverageFlux(AbsoluteDate date) { return 98.8; } + @Override + public double[] getAp(AbsoluteDate date) { return new double[] {33.0, 9.0, 18.0, 32.0, 32.0, 8.875, 7.25}; } + } + + /** NRLMSISE00 solar activity input parameters for testIssue1365AvoidNanExceptionRaised. */ + @SuppressWarnings("serial") + private static class InputParamsIssue1365AvoidNanExceptionRaised implements NRLMSISE00InputParameters { + + @Override + public AbsoluteDate getMinDate() { return new AbsoluteDate(2005, 10, 30, TimeScalesFactory.getUTC()); } + @Override + public AbsoluteDate getMaxDate() { return new AbsoluteDate(2005, 10, 31, TimeScalesFactory.getUTC()); } + @Override + public double getDailyFlux(AbsoluteDate date) { return 707.6; } + @Override + public double getAverageFlux(AbsoluteDate date) { return 98.8; } + @Override + public double[] getAp(AbsoluteDate date) { return new double[] {33.0, 9.0, 18.0, 32.0, 32.0, 8.875, 7.25}; } + } + + /** Sun position for testIssue1365xxx. */ + @SuppressWarnings("serial") + private static class SunPositionIssue1365AvoidNan implements CelestialBody { + + private final Vector3D sunPosition; + private final Frame j2000; + + /** Constructor. + * @param sunPosition + * @param j2000 + */ + private SunPositionIssue1365AvoidNan(Vector3D sunPosition, + Frame j2000) { + this.sunPosition = sunPosition; + this.j2000 = j2000; + } + @Override + public TimeStampedPVCoordinates getPVCoordinates(AbsoluteDate date, Frame frame) { + return new TimeStampedPVCoordinates(date, sunPosition, Vector3D.ZERO); + } + @Override + public > TimeStampedFieldPVCoordinates getPVCoordinates(FieldAbsoluteDate date, Frame frame) { + return new TimeStampedFieldPVCoordinates(date, date.getField().getOne(), getPVCoordinates(date.toAbsoluteDate(), frame)); + } + @Override + public String getName() { return "SUN"; } + @Override + public Frame getInertiallyOrientedFrame() { return j2000; } + @Override + public double getGM() { return Constants.JPL_SSD_SUN_GM; } + @Override + public Frame getBodyOrientedFrame() { return j2000; } + } } diff --git a/src/test/java/org/orekit/models/earth/displacement/PostSeismicDeformationTest.java b/src/test/java/org/orekit/models/earth/displacement/PostSeismicDeformationTest.java new file mode 100644 index 0000000000..a4c282bc6a --- /dev/null +++ b/src/test/java/org/orekit/models/earth/displacement/PostSeismicDeformationTest.java @@ -0,0 +1,88 @@ +/* Copyright 2002-2024 Thales Alenia Space + * Licensed to CS Communication & Systèmes (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.models.earth.displacement; + +import org.hipparchus.geometry.euclidean.threed.Vector3D; +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.GeodeticPoint; +import org.orekit.bodies.OneAxisEllipsoid; +import org.orekit.data.DataSource; +import org.orekit.data.FundamentalNutationArguments; +import org.orekit.files.sinex.SinexLoader; +import org.orekit.files.sinex.Station; +import org.orekit.frames.Frame; +import org.orekit.frames.FramesFactory; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.TimeScale; +import org.orekit.time.TimeScalesFactory; +import org.orekit.utils.Constants; +import org.orekit.utils.IERSConventions; + +import java.net.URISyntaxException; +import java.net.URL; + +public class PostSeismicDeformationTest { + + @Test + public void testShintotsukawa() throws URISyntaxException { + final URL url = PostSeismicDeformationTest.class.getClassLoader(). + getResource("sinex/ITRF2020-psd-gnss.snx"); + final Station stk2 = new SinexLoader(new DataSource(url.toURI())).getStation("STK2"); + + final IERSConventions conventions = IERSConventions.IERS_2010; + final TimeScale utc = TimeScalesFactory.getUTC(); + final TimeScale ut1 = TimeScalesFactory.getUT1(conventions, false); + final FundamentalNutationArguments fna = conventions.getNutationArguments(ut1); + final Frame earthFrame = FramesFactory.getITRF(conventions, false); + final OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, + Constants.WGS84_EARTH_FLATTENING, + earthFrame); + + // https://earthquake.usgs.gov/earthquakes/eventpage/usp000c8kv/executive + final AbsoluteDate date2003 = new AbsoluteDate(2003, 9, 25, 19, 50, 6, utc); + final GeodeticPoint base = earth.transform(stk2.getPosition(), earthFrame, null); + final PostSeismicDeformation psd = new PostSeismicDeformation(base, stk2.getPsdTimeSpanMap()); + + for (double years = 0; years < 1.0; years += 0.01) { + final Vector3D dp = psd.displacement(fna.evaluateAll( date2003.shiftedBy(years * Constants.JULIAN_YEAR)), + earthFrame, stk2.getPosition()); + + // reference values from manual analysis of PSD data file + final double refEast = -3.12544483981059e-02 * (1 - FastMath.exp(-years / 3.90382962596289e-01)) + + 2.74994599757755e-02 * FastMath.log(1 + years / 6.84349501571032e-02); + final double refNorth = -3.97046280518505e-02 * (1 - FastMath.exp(-years / 1.38871759192151e+00)) + + -1.44921478188329e-02 * (1 - FastMath.exp(-years / 6.08619870701242e-02)); + final double refUp = 0.0; + + Assertions.assertEquals(refEast, Vector3D.dotProduct(dp, base.getEast()), 1.0e-15); + Assertions.assertEquals(refNorth, Vector3D.dotProduct(dp, base.getNorth()), 1.0e-15); + Assertions.assertEquals(refUp, Vector3D.dotProduct(dp, base.getZenith()), 1.0e-15); + + } + + } + + @BeforeEach + public void setUp() throws Exception { + Utils.setDataRoot("regular-data"); + } + +} diff --git a/src/test/java/org/orekit/models/earth/ionosphere/EstimatedIonosphericModelTest.java b/src/test/java/org/orekit/models/earth/ionosphere/EstimatedIonosphericModelTest.java index 139316aeb2..903ed1c4bb 100644 --- a/src/test/java/org/orekit/models/earth/ionosphere/EstimatedIonosphericModelTest.java +++ b/src/test/java/org/orekit/models/earth/ionosphere/EstimatedIonosphericModelTest.java @@ -37,6 +37,7 @@ import org.orekit.frames.FramesFactory; import org.orekit.frames.TopocentricFrame; import org.orekit.gnss.Frequency; +import org.orekit.models.earth.troposphere.TroposphericModelUtils; import org.orekit.orbits.FieldKeplerianOrbit; import org.orekit.orbits.FieldOrbit; import org.orekit.orbits.KeplerianOrbit; @@ -70,7 +71,7 @@ public void testL1GPS() { final EstimatedIonosphericModel model = new EstimatedIonosphericModel(mapping, 1.0); // Delay final double delay = model.pathDelay(0.5 * FastMath.PI, - Frequency.G01.getMHzFrequency() * 1.0e6, + Frequency.G01.getFrequency(), model.getParameters(new AbsoluteDate())); // Verify Assertions.assertEquals(0.162, delay, 0.001); @@ -89,7 +90,7 @@ private > void doTestFieldL1GPS(final Field final EstimatedIonosphericModel model = new EstimatedIonosphericModel(mapping, 1.0); // Delay final T delay = model.pathDelay(zero.add(0.5 * FastMath.PI), - Frequency.G01.getMHzFrequency() * 1.0e6, + Frequency.G01.getFrequency(), model.getParameters(field)); // Verify Assertions.assertEquals(0.162, delay.getReal(), 0.001); @@ -105,7 +106,7 @@ public void testDelay() { // the pamater driver has no validity period, so only 1 values estimated over // the all period, that is why getParameters is called with no argument double delayMeters = model.pathDelay(FastMath.toRadians(elevation), - Frequency.G01.getMHzFrequency() * 1.0e6, + Frequency.G01.getFrequency(), model.getParameters()); Assertions.assertTrue(Precision.compareTo(delayMeters, 12., 1.0e-6) < 0); @@ -124,7 +125,7 @@ private > void doTestFieldDelay(final Field final EstimatedIonosphericModel model = new EstimatedIonosphericModel(mapping, 50.0); T zero = field.getZero(); T delayMeters = model.pathDelay(zero.add(FastMath.toRadians(elevation)), - Frequency.G01.getMHzFrequency() * 1.0e6, + Frequency.G01.getFrequency(), model.getParameters(field)); Assertions.assertTrue(Precision.compareTo(delayMeters.getReal(), 12., 1.0e-6) < 0); @@ -134,7 +135,7 @@ private > void doTestFieldDelay(final Field @Test public void testZeroDelay() { // Frequency - final double frequency = Frequency.G01.getMHzFrequency() * 1.0e6; + final double frequency = Frequency.G01.getFrequency(); // Geodetic point final double height = 0.0; @@ -171,7 +172,7 @@ public void testFieldZeroDelay() { private > void doTestFieldZeroDelay(final Field field) { final T zero = field.getZero(); // Frequency - final double frequency = Frequency.G01.getMHzFrequency() * 1.0e6; + final double frequency = Frequency.G01.getFrequency(); // Geodetic point final double height = 0.0; @@ -212,11 +213,11 @@ private > void doTestEquality(final Field f final EstimatedIonosphericModel model = new EstimatedIonosphericModel(mapping, 50.0); T zero = field.getZero(); T delayMetersF = model.pathDelay(zero.add(FastMath.toRadians(elevation)), - Frequency.G01.getMHzFrequency() * 1.0e6, + Frequency.G01.getFrequency(), model.getParameters(field)); double delayMetersR = model.pathDelay(FastMath.toRadians(elevation), - Frequency.G01.getMHzFrequency() * 1.0e6, + Frequency.G01.getFrequency(), model.getParameters()); Assertions.assertEquals(delayMetersR, delayMetersF.getReal(), 1.0e-15); @@ -226,7 +227,7 @@ private > void doTestEquality(final Field f public void testDelayStateDerivatives() { // Frequency - final double frequency = Frequency.G01.getMHzFrequency() * 1.0e6; + final double frequency = Frequency.G01.getFrequency(); // Geodetic point final double height = 0.0; @@ -239,7 +240,8 @@ public void testDelayStateDerivatives() { final TopocentricFrame baseFrame = new TopocentricFrame(earth, point, "topo"); // Station - final GroundStation station = new GroundStation(baseFrame); + final GroundStation station = new GroundStation(baseFrame, + TroposphericModelUtils.STANDARD_ATMOSPHERE_PROVIDER); // Ionospheric model final IonosphericMappingFunction mapping = new SingleLayerModelMappingFunction(); @@ -377,7 +379,7 @@ private SpacecraftState shiftState(SpacecraftState state, OrbitType orbitType, P public void testParametersDerivatives() { // Frequency - final double frequency = Frequency.G01.getMHzFrequency() * 1.0e6; + final double frequency = Frequency.G01.getFrequency(); // Geodetic point final double latitude = FastMath.toRadians(45.0); diff --git a/src/test/java/org/orekit/models/earth/ionosphere/GlobalIonosphereMapModelTest.java b/src/test/java/org/orekit/models/earth/ionosphere/GlobalIonosphereMapModelTest.java index 0edafbf413..2e60143305 100644 --- a/src/test/java/org/orekit/models/earth/ionosphere/GlobalIonosphereMapModelTest.java +++ b/src/test/java/org/orekit/models/earth/ionosphere/GlobalIonosphereMapModelTest.java @@ -86,7 +86,7 @@ public void testDelayAtIPP() { final double delay = (Double) pathDelay.invoke(model, new AbsoluteDate(2019, 1, 15, 3, 43, 12.0, TimeScalesFactory.getUTC()), new GeodeticPoint(latitude, longitude, 0.0), - 0.5 * FastMath.PI, Frequency.G01.getMHzFrequency() * 1.0e6); + 0.5 * FastMath.PI, Frequency.G01.getFrequency()); Assertions.assertEquals(1.557, delay, epsilonDelay); } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { @@ -116,7 +116,7 @@ private > void doTestFieldDelayAtIPP(final Fie new FieldAbsoluteDate<>(field, 2019, 1, 15, 3, 43, 12.0, TimeScalesFactory.getUTC()), new GeodeticPoint(latitude, longitude, 0.0), zero.add(0.5 * FastMath.PI), - Frequency.G01.getMHzFrequency() * 1.0e6); + Frequency.G01.getFrequency()); Assertions.assertEquals(1.557, delay.getReal(), epsilonDelay); } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { @@ -144,7 +144,7 @@ public void testSpacecraftState() { FastMath.toRadians(85.7881), 36.0), "Cuttack"); - final double delay = model.pathDelay(state, topo, Frequency.G01.getMHzFrequency() * 1.0e6, null); + final double delay = model.pathDelay(state, topo, Frequency.G01.getFrequency(), null); Assertions.assertEquals(2.810, delay, epsilonDelay); // the delay at station longitude is different, due to IPP @@ -156,7 +156,7 @@ public void testSpacecraftState() { pathDelay.setAccessible(true); final double delayIPP = (Double) pathDelay.invoke(model, date, topo.getPoint(), 0.5 * FastMath.PI, - Frequency.G01.getMHzFrequency() * 1.0e6); + Frequency.G01.getFrequency()); Assertions.assertEquals(2.173, delayIPP, epsilonDelay); } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { @@ -184,7 +184,7 @@ public void testAboveIono() { FastMath.toRadians(85.7881), 650000.0), "very-high"); - final double delay = model.pathDelay(state, topo, Frequency.G01.getMHzFrequency() * 1.0e6, null); + final double delay = model.pathDelay(state, topo, Frequency.G01.getFrequency(), null); Assertions.assertEquals(0.0, delay, epsilonDelay); } @@ -214,7 +214,7 @@ private > void doTestSpacecraftStateField(fina FastMath.toRadians(85.7881), 36.0), "Cuttack"); - final T delay = model.pathDelay(state, topo, Frequency.G01.getMHzFrequency() * 1.0e6, null); + final T delay = model.pathDelay(state, topo, Frequency.G01.getFrequency(), null); Assertions.assertEquals(2.810, delay.getReal(), epsilonDelay); // the delay at station longitude is different, due to IPP @@ -228,7 +228,7 @@ private > void doTestSpacecraftStateField(fina @SuppressWarnings("unchecked") final T delayIPP = (T) pathDelay.invoke(model, date, topo.getPoint(), field.getZero().newInstance(0.5 * FastMath.PI), - Frequency.G01.getMHzFrequency() * 1.0e6); + Frequency.G01.getFrequency()); Assertions.assertEquals(2.173, delayIPP.getReal(), epsilonDelay); } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { @@ -261,7 +261,7 @@ private > void doTestAboveIonoField(final Fiel FastMath.toRadians(85.7881), 650000.0), "very-high"); - final T delay = model.pathDelay(state, topo, Frequency.G01.getMHzFrequency() * 1.0e6, null); + final T delay = model.pathDelay(state, topo, Frequency.G01.getFrequency(), null); Assertions.assertEquals(0.0, delay.getReal(), epsilonDelay); } @@ -296,9 +296,9 @@ public void testParser() throws URISyntaxException { longitude2 = FastMath.toRadians(-179.0); AbsoluteDate date1 = new AbsoluteDate(2019, 1, 15, 1, 0, 0.0, TimeScalesFactory.getUTC()); Assertions.assertEquals(((Double) pathDelay.invoke(model, date1, new GeodeticPoint(latitude, longitude1, 0.0), - 0.01, Frequency.G01.getMHzFrequency() * 1.0e6)).doubleValue(), + 0.01, Frequency.G01.getFrequency())).doubleValue(), ((Double) pathDelay.invoke(model, date1, new GeodeticPoint(latitude, longitude2, 0.0), - 0.01, Frequency.G01.getMHzFrequency() * 1.0e6)).doubleValue(), + 0.01, Frequency.G01.getFrequency())).doubleValue(), epsilonParser); // Test longitude = 180° and longitude = -180° @@ -307,9 +307,9 @@ public void testParser() throws URISyntaxException { longitude2 = FastMath.toRadians(-180.0); Assertions.assertEquals(((Double) pathDelay.invoke(model, date2, new GeodeticPoint(latitude, longitude1, 0.0), - 0.01, Frequency.G01.getMHzFrequency() * 1.0e6)).doubleValue(), + 0.01, Frequency.G01.getFrequency())).doubleValue(), ((Double) pathDelay.invoke(model, date2, new GeodeticPoint(latitude, longitude2, 0.0), - 0.01, Frequency.G01.getMHzFrequency() * 1.0e6)).doubleValue(), + 0.01, Frequency.G01.getFrequency())).doubleValue(), epsilonParser); // Test longitude = 0° and longitude = 360° @@ -318,9 +318,9 @@ public void testParser() throws URISyntaxException { longitude2 = FastMath.toRadians(360.0); Assertions.assertEquals(((Double) pathDelay.invoke(model, date3, new GeodeticPoint(latitude, longitude1, 0.0), - 0.01, Frequency.G01.getMHzFrequency() * 1.0e6)).doubleValue(), + 0.01, Frequency.G01.getFrequency())).doubleValue(), ((Double) pathDelay.invoke(model, date3, new GeodeticPoint(latitude, longitude2, 0.0), - 0.01, Frequency.G01.getMHzFrequency() * 1.0e6)).doubleValue(), + 0.01, Frequency.G01.getFrequency())).doubleValue(), epsilonParser); } catch (NoSuchMethodException | SecurityException | IllegalAccessException | @@ -351,7 +351,7 @@ public void testEarlierDate() { try { model.pathDelay(state, new TopocentricFrame(earth, point, null), - Frequency.G01.getMHzFrequency() * 1.0e6, + Frequency.G01.getFrequency(), model.getParameters(new AbsoluteDate())); Assertions.fail("An exception should have been thrown"); } catch (OrekitException oe) { @@ -372,7 +372,7 @@ private > void doTestFieldEarlierDate(final Fi try { model.pathDelay(new FieldSpacecraftState<>(field, state), new TopocentricFrame(earth, point, null), - Frequency.G01.getMHzFrequency() * 1.0e6, + Frequency.G01.getFrequency(), model.getParameters(field, new FieldAbsoluteDate<>(field))); Assertions.fail("An exception should have been thrown"); } catch (OrekitException oe) { @@ -389,7 +389,7 @@ public void testLaterDate() { try { model.pathDelay(state, new TopocentricFrame(earth, point, null), - Frequency.G01.getMHzFrequency() * 1.0e6, + Frequency.G01.getFrequency(), model.getParameters(new AbsoluteDate())); Assertions.fail("An exception should have been thrown"); } catch (OrekitException oe) { @@ -410,7 +410,7 @@ private > void doTestFieldLaterDate(final Fiel final GeodeticPoint point = new GeodeticPoint(latitude, longitude, 0.0); try { model.pathDelay(new FieldSpacecraftState<>(field, state), new TopocentricFrame(earth, point, null), - Frequency.G01.getMHzFrequency() * 1.0e6, + Frequency.G01.getFrequency(), model.getParameters(field, new FieldAbsoluteDate<>(field))); Assertions.fail("An exception should have been thrown"); } catch (OrekitException oe) { 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 149b0d71bd..c4e45e7b8b 100644 --- a/src/test/java/org/orekit/models/earth/ionosphere/NeQuickModelTest.java +++ b/src/test/java/org/orekit/models/earth/ionosphere/NeQuickModelTest.java @@ -184,7 +184,7 @@ public void testDelay() { final SpacecraftState state = new SpacecraftState(orbit); final double delay = model.pathDelay(state, new TopocentricFrame(ellipsoid, recP, null), - Frequency.G01.getMHzFrequency() * 1.0E6, model.getParameters()); + Frequency.G01.getFrequency(), model.getParameters()); // Verify Assertions.assertEquals(1.13, delay, 0.01); @@ -229,7 +229,7 @@ private > void doTestFieldDelay(final Field final FieldSpacecraftState state = new FieldSpacecraftState<>(orbit); final T delay = model.pathDelay(state, new TopocentricFrame(ellipsoid, recP, null), - Frequency.G01.getMHzFrequency() * 1.0E6, model.getParameters(field)); + Frequency.G01.getFrequency(), model.getParameters(field)); // Verify Assertions.assertEquals(1.13, delay.getReal(), 0.01); diff --git a/src/test/java/org/orekit/models/earth/ionosphere/SsrVtecIonosphericModelTest.java b/src/test/java/org/orekit/models/earth/ionosphere/SsrVtecIonosphericModelTest.java index 9554924e8e..5e663d4c66 100644 --- a/src/test/java/org/orekit/models/earth/ionosphere/SsrVtecIonosphericModelTest.java +++ b/src/test/java/org/orekit/models/earth/ionosphere/SsrVtecIonosphericModelTest.java @@ -88,7 +88,7 @@ public void setUp() { public void testDelay() { // Frequency - final double frequency = Frequency.G01.getMHzFrequency() * 1.0e6; + final double frequency = Frequency.G01.getFrequency(); // Geodetic point final double height = 0.0; @@ -125,7 +125,7 @@ public void testFieldDelay() { private > void doTestFieldDelay(final Field field) { final T zero = field.getZero(); // Frequency - final double frequency = Frequency.G01.getMHzFrequency() * 1.0e6; + final double frequency = Frequency.G01.getFrequency(); // Geodetic point final double height = 0.0; @@ -156,7 +156,7 @@ private > void doTestFieldDelay(final Field @Test public void testZeroDelay() { // Frequency - final double frequency = Frequency.G01.getMHzFrequency() * 1.0e6; + final double frequency = Frequency.G01.getFrequency(); // Geodetic point final double height = 0.0; @@ -192,7 +192,7 @@ public void testFieldZeroDelay() { private > void doTestFieldZeroDelay(final Field field) { final T zero = field.getZero(); // Frequency - final double frequency = Frequency.G01.getMHzFrequency() * 1.0e6; + final double frequency = Frequency.G01.getFrequency(); // Geodetic point final double height = 0.0; @@ -224,7 +224,7 @@ private > void doTestFieldZeroDelay(final Fiel public void testDelayStateDerivatives() { // Frequency - final double frequency = Frequency.G01.getMHzFrequency() * 1.0e6; + final double frequency = Frequency.G01.getFrequency(); // Geodetic point final double height = 0.0; @@ -319,7 +319,7 @@ public void testDelayStateDerivatives() { public void testDelayRange() { // Frequency - final double frequency = Frequency.G01.getMHzFrequency() * 1.0e6; + final double frequency = Frequency.G01.getFrequency(); // Geodetic point final double height = 0.0; @@ -360,7 +360,7 @@ public void testFieldDelayRange() { private > void doTestFieldDelayRange(final Field field) { final T zero = field.getZero(); // Frequency - final double frequency = Frequency.G01.getMHzFrequency() * 1.0e6; + final double frequency = Frequency.G01.getFrequency(); // Geodetic point final double height = 0.0; diff --git a/src/test/java/org/orekit/models/earth/tessellation/EllipsoidTessellatorTest.java b/src/test/java/org/orekit/models/earth/tessellation/EllipsoidTessellatorTest.java index 4286981980..9bfce58a81 100644 --- a/src/test/java/org/orekit/models/earth/tessellation/EllipsoidTessellatorTest.java +++ b/src/test/java/org/orekit/models/earth/tessellation/EllipsoidTessellatorTest.java @@ -41,7 +41,6 @@ import org.orekit.utils.Constants; import org.orekit.utils.IERSConventions; -import java.io.IOException; import java.util.List; public class EllipsoidTessellatorTest { @@ -54,7 +53,7 @@ public void testTilesAlongDescendingTrackWithoutTruncation() { 50000.0, 150000.0, 5000.0, 5000.0, false, false); Assertions.assertEquals(2, tiles.size()); - Assertions.assertEquals(109, FastMath.max(tiles.get(0).size(), tiles.get(1).size())); + Assertions.assertEquals(108, FastMath.max(tiles.get(0).size(), tiles.get(1).size())); Assertions.assertEquals(4, FastMath.min(tiles.get(0).size(), tiles.get(1).size())); } @@ -78,7 +77,7 @@ public void testSampleAlongDescendingTrack() { new EllipsoidTessellator(ellipsoid, new AlongTrackAiming(ellipsoid, orbit, false), 4); final List> samples = tessellator.sample(buildFrance(), 25000.0, 50000.0); Assertions.assertEquals(2, samples.size()); - Assertions.assertEquals(452, FastMath.max(samples.get(0).size(), samples.get(1).size())); + Assertions.assertEquals(455, FastMath.max(samples.get(0).size(), samples.get(1).size())); Assertions.assertEquals(9, FastMath.min(samples.get(0).size(), samples.get(1).size())); } @@ -87,10 +86,10 @@ public void testTilesAlongAscendingTrack() { final EllipsoidTessellator tessellator = new EllipsoidTessellator(ellipsoid, new AlongTrackAiming(ellipsoid, orbit, true), 4); final List> tiles = tessellator.tessellate(buildFrance(), - 50000.0, 150000.0, 5000.0, 5000.0, + 50000.0, 140000.0, 5000.0, 5000.0, false, false); Assertions.assertEquals(2, tiles.size()); - Assertions.assertEquals(112, FastMath.max(tiles.get(0).size(), tiles.get(1).size())); + Assertions.assertEquals(121, FastMath.max(tiles.get(0).size(), tiles.get(1).size())); Assertions.assertEquals(6, FastMath.min(tiles.get(0).size(), tiles.get(1).size())); } @@ -101,7 +100,7 @@ public void testSampleAlongAscendingTrack() { final List> samples = tessellator.sample(buildFrance(), 25000.0, 50000.0); Assertions.assertEquals(2, samples.size()); - Assertions.assertEquals(452, FastMath.max(samples.get(0).size(), samples.get(1).size())); + Assertions.assertEquals(454, FastMath.max(samples.get(0).size(), samples.get(1).size())); Assertions.assertEquals(10, FastMath.min(samples.get(0).size(), samples.get(1).size())); } @@ -124,7 +123,7 @@ public void testSampleConstantAzimuth() { new EllipsoidTessellator(ellipsoid, new ConstantAzimuthAiming(ellipsoid, FastMath.toRadians(120)), 4); final List> samples = tessellator.sample(buildFrance(), 25000.0, 50000.0); Assertions.assertEquals(2, samples.size()); - Assertions.assertEquals(455, FastMath.max(samples.get(0).size(), samples.get(1).size())); + Assertions.assertEquals(452, FastMath.max(samples.get(0).size(), samples.get(1).size())); Assertions.assertEquals(9, FastMath.min(samples.get(0).size(), samples.get(1).size())); } @@ -136,12 +135,12 @@ public void testTilesIslandJoining() { 150000.0, 250000.0, -5000.0, -5000.0, false, false); Assertions.assertEquals(1, tiles.size()); - Assertions.assertEquals(28, tiles.get(0).size()); + Assertions.assertEquals(30, tiles.get(0).size()); checkTilesDontOverlap(tiles); } @Test - public void testTilesSmallZoneWithoutTruncation() throws IOException { + public void testTilesSmallZoneWithoutTruncation() { TileAiming aiming = new ConstantAzimuthAiming(ellipsoid, FastMath.toRadians(193.7)); EllipsoidTessellator tessellator = @@ -180,7 +179,7 @@ public void testTilesSmallZoneWithoutTruncation() throws IOException { } @Test - public void testTilesSmallZoneWithTruncation() throws IOException { + public void testTilesSmallZoneWithTruncation() { TileAiming aiming = new ConstantAzimuthAiming(ellipsoid, FastMath.toRadians(193.7)); EllipsoidTessellator tessellator = @@ -193,27 +192,28 @@ public void testTilesSmallZoneWithTruncation() throws IOException { final List> tiles = tessellator.tessellate(small, 50000.0, 150000.0, 0, 0, true, true); + Assertions.assertEquals(1, tiles.size()); Assertions.assertEquals(1, tiles.get(0).size()); Tile t = tiles.get(0).get(0); // with truncation, the tile is a fraction of the width and length specification - Assertions.assertEquals(3.0 / 16.0 * 150000.0, - Vector3D.distance(ellipsoid.transform(t.getVertices()[0]), - ellipsoid.transform(t.getVertices()[1])), - 10.0); + Assertions.assertEquals(2.0 / 16.0 * 150000.0, + Vector3D.distance(ellipsoid.transform(t.getVertices()[0]), + ellipsoid.transform(t.getVertices()[1])), + 10.0); Assertions.assertEquals(4.0 / 16.0 * 50000.0, - Vector3D.distance(ellipsoid.transform(t.getVertices()[1]), - ellipsoid.transform(t.getVertices()[2])), - 0.01); - Assertions.assertEquals(3.0 / 16.0 * 150000.0, - Vector3D.distance(ellipsoid.transform(t.getVertices()[2]), - ellipsoid.transform(t.getVertices()[3])), - 10.0); + Vector3D.distance(ellipsoid.transform(t.getVertices()[1]), + ellipsoid.transform(t.getVertices()[2])), + 0.01); + Assertions.assertEquals(2.0 / 16.0 * 150000.0, + Vector3D.distance(ellipsoid.transform(t.getVertices()[2]), + ellipsoid.transform(t.getVertices()[3])), + 10.0); Assertions.assertEquals(4.0 / 16.0 * 50000.0, - Vector3D.distance(ellipsoid.transform(t.getVertices()[3]), - ellipsoid.transform(t.getVertices()[0])), - 0.01); + Vector3D.distance(ellipsoid.transform(t.getVertices()[3]), + ellipsoid.transform(t.getVertices()[0])), + 0.01); } @Test @@ -348,21 +348,19 @@ private void checkTilesDontOverlap(final List> tiles) { @Test public void testSampleAroundPoleConstantAzimuth() { - SphericalPolygonsSet aoi = new SphericalPolygonsSet(1.e-9, new S2Point[] { - new S2Point(FastMath.toRadians(-120.0), FastMath.toRadians(5.0)), - new S2Point(FastMath.toRadians( 0.0), FastMath.toRadians(5.0)), - new S2Point(FastMath.toRadians( 120.0), FastMath.toRadians(5.0)) - }); + SphericalPolygonsSet aoi = new SphericalPolygonsSet(1.e-9, + new S2Point(FastMath.toRadians(-120.0), FastMath.toRadians(5.0)), + new S2Point(FastMath.toRadians( 0.0), FastMath.toRadians(5.0)), + new S2Point(FastMath.toRadians( 120.0), FastMath.toRadians(5.0))); doTestSampleAroundPole(aoi, new ConstantAzimuthAiming(ellipsoid, 0.0), -1); } @Test public void testSampleAroundPoleDivertedSingularity() { - SphericalPolygonsSet aoi = new SphericalPolygonsSet(1.e-9, new S2Point[] { - new S2Point(FastMath.toRadians(-120.0), FastMath.toRadians(5.0)), - new S2Point(FastMath.toRadians( 0.0), FastMath.toRadians(5.0)), - new S2Point(FastMath.toRadians( 120.0), FastMath.toRadians(5.0)) - }); + SphericalPolygonsSet aoi = new SphericalPolygonsSet(1.e-9, + new S2Point(FastMath.toRadians(-120.0), FastMath.toRadians(5.0)), + new S2Point(FastMath.toRadians( 0.0), FastMath.toRadians(5.0)), + new S2Point(FastMath.toRadians( 120.0), FastMath.toRadians(5.0))); doTestSampleAroundPole(aoi, new DivertedSingularityAiming(aoi), 993); } diff --git a/src/test/java/org/orekit/models/earth/troposphere/AbstractFieldMappingFunctionTest.java b/src/test/java/org/orekit/models/earth/troposphere/AbstractFieldMappingFunctionTest.java new file mode 100644 index 0000000000..aa570407ae --- /dev/null +++ b/src/test/java/org/orekit/models/earth/troposphere/AbstractFieldMappingFunctionTest.java @@ -0,0 +1,328 @@ +/* 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.models.earth.troposphere; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.analysis.differentiation.DSFactory; +import org.hipparchus.analysis.differentiation.DerivativeStructure; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.util.Binary64Field; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.MathArrays; +import org.hipparchus.util.Precision; +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.Attitude; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.bodies.OneAxisEllipsoid; +import org.orekit.estimation.measurements.GroundStation; +import org.orekit.frames.Frame; +import org.orekit.frames.FramesFactory; +import org.orekit.frames.TopocentricFrame; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; +import org.orekit.orbits.FieldKeplerianOrbit; +import org.orekit.orbits.FieldOrbit; +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.numerical.NumericalPropagator; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.TimeScalesFactory; +import org.orekit.utils.Constants; +import org.orekit.utils.FieldTrackingCoordinates; +import org.orekit.utils.IERSConventions; +import org.orekit.utils.TrackingCoordinates; + +public abstract class AbstractFieldMappingFunctionTest { + + protected abstract TroposphereMappingFunction buildMappingFunction(); + + @Test + public abstract void testMappingFactors(); + + protected > void doTestMappingFactors(final Field field, + final double expectedHydro, + final double expectedWet) { + + final T zero = field.getZero(); + + // Site (Le Mans, France): latitude: 48.0° + // longitude: 0.20° + // height: 68 m + // + // Date: 1st January 1994 at 0h UT + // + // Ref: Mercier F., Perosanz F., Mesures GNSS, Résolution des ambiguités. + + final FieldAbsoluteDate date = new FieldAbsoluteDate<>(field, 1994, 1, 1, TimeScalesFactory.getUTC()); + + final double latitude = FastMath.toRadians(48.0); + final double longitude = FastMath.toRadians(0.20); + final double height = 68.0; + + final FieldGeodeticPoint point = new FieldGeodeticPoint<>(zero.add(latitude), zero.add(longitude), zero.add(height)); + + final FieldTrackingCoordinates trackingCoordinates = new FieldTrackingCoordinates<>(zero, + FastMath.toRadians(zero.newInstance(5.0)), + zero); + + final TroposphereMappingFunction model = buildMappingFunction(); + + final T[] computedMapping = model.mappingFactors(trackingCoordinates, point, + new FieldPressureTemperatureHumidity<>(field, + TroposphericModelUtils.STANDARD_ATMOSPHERE), + date); + + Assertions.assertEquals(expectedHydro, computedMapping[0].getReal(), 1.0e-2); + Assertions.assertEquals(expectedWet, computedMapping[1].getReal(), 1.0e-2); + } + + @Test + public void testFixedHeight() { + doTestFixedHeight(Binary64Field.getInstance()); + } + + private > void doTestFixedHeight(final Field field) { + final T zero = field.getZero(); + final FieldAbsoluteDate date = new FieldAbsoluteDate<>(field); + final FieldGeodeticPoint point = new FieldGeodeticPoint<>(zero.add(FastMath.toRadians(45.0)), zero.add(FastMath.toRadians(45.0)), zero.add(350.0)); + final TroposphereMappingFunction model = buildMappingFunction(); + T[] lastFactors = MathArrays.buildArray(field, 2); + lastFactors[0] = zero.add(Double.MAX_VALUE); + lastFactors[1] = zero.add(Double.MAX_VALUE); + // mapping functions shall decline with increasing elevation angle + for (double elev = 10d; elev < 90d; elev += 8d) { + final T[] factors = model.mappingFactors(new FieldTrackingCoordinates<>(zero, + FastMath.toRadians(zero.newInstance(elev)), + zero), + point, + new FieldPressureTemperatureHumidity<>(field, + TroposphericModelUtils.STANDARD_ATMOSPHERE), + date); + Assertions.assertTrue(Precision.compareTo(factors[0].getReal(), lastFactors[0].getReal(), 1.0e-6) < 0); + Assertions.assertTrue(Precision.compareTo(factors[1].getReal(), lastFactors[1].getReal(), 1.0e-6) < 0); + lastFactors[0] = factors[0]; + lastFactors[1] = factors[1]; + } + } + + @Test + public abstract void testMFStateDerivatives(); + + protected void doTestMFStateDerivatives(final double epsMFH, final double epsMFW) { + + // Geodetic point + final double latitude = FastMath.toRadians(45.0); + final double longitude = FastMath.toRadians(45.0); + final double height = 0.0; + final GeodeticPoint point = new GeodeticPoint(latitude, longitude, height); + // Body: earth + final OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, + Constants.WGS84_EARTH_FLATTENING, + FramesFactory.getITRF(IERSConventions.IERS_2010, true)); + // Topocentric frame + final TopocentricFrame baseFrame = new TopocentricFrame(earth, point, "topo"); + + // Station + final GroundStation station = new GroundStation(baseFrame, + TroposphericModelUtils.STANDARD_ATMOSPHERE_PROVIDER); + + // Mapping Function model + final TroposphereMappingFunction model = buildMappingFunction(); + + // Derivative Structure + final DSFactory factory = new DSFactory(6, 1); + final DerivativeStructure a0 = factory.variable(0, 24464560.0); + final DerivativeStructure e0 = factory.variable(1, 0.05); + final DerivativeStructure i0 = factory.variable(2, 0.122138); + final DerivativeStructure pa0 = factory.variable(3, 3.10686); + final DerivativeStructure raan0 = factory.variable(4, 1.00681); + final DerivativeStructure anomaly0 = factory.variable(5, 0.048363); + final Field field = a0.getField(); + final DerivativeStructure zero = field.getZero(); + + final FieldPressureTemperatureHumidity weather = + new FieldPressureTemperatureHumidity<>(field, + TroposphericModelUtils.STANDARD_ATMOSPHERE); + + // Field Date + final FieldAbsoluteDate dsDate = new FieldAbsoluteDate<>(field); + // Field Orbit + final Frame frame = FramesFactory.getEME2000(); + final FieldOrbit dsOrbit = new FieldKeplerianOrbit<>(a0, e0, i0, pa0, raan0, anomaly0, + PositionAngleType.MEAN, frame, + dsDate, zero.add(3.9860047e14)); + // Field State + final FieldSpacecraftState dsState = new FieldSpacecraftState<>(dsOrbit); + + // Initial satellite elevation + final FieldVector3D position = dsState.getPosition(); + final FieldTrackingCoordinates dsTrackingCoordinates = + baseFrame.getTrackingCoordinates(position, frame, dsDate); + + // Compute mapping factors with state derivatives + final FieldGeodeticPoint dsPoint = new FieldGeodeticPoint<>(zero.add(latitude), zero.add(longitude), zero.add(height)); + final DerivativeStructure[] factors = model.mappingFactors(dsTrackingCoordinates, dsPoint, weather, dsDate); + + final double[] compMFH = factors[0].getAllDerivatives(); + final double[] compMFW = factors[1].getAllDerivatives(); + + // Field -> non-field + final Orbit orbit = dsOrbit.toOrbit(); + final SpacecraftState state = dsState.toSpacecraftState(); + + // Finite differences for reference values + final double[][] refMF = new double[2][6]; + final OrbitType orbitType = OrbitType.KEPLERIAN; + final PositionAngleType angleType = PositionAngleType.MEAN; + double dP = 0.001; + double[] steps = NumericalPropagator.tolerances(1000000 * dP, orbit, orbitType)[0]; + for (int i = 0; i < 6; i++) { + SpacecraftState stateM4 = shiftState(state, orbitType, angleType, -4 * steps[i], i); + final Vector3D positionM4 = stateM4.getPosition(); + final TrackingCoordinates trackingCoordinatesM4 = station.getBaseFrame(). + getTrackingCoordinates(positionM4, stateM4.getFrame(), stateM4.getDate()); + double[] delayM4 = model.mappingFactors(trackingCoordinatesM4, point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + stateM4.getDate()); + + SpacecraftState stateM3 = shiftState(state, orbitType, angleType, -3 * steps[i], i); + final Vector3D positionM3 = stateM3.getPosition(); + final TrackingCoordinates trackingCoordinatesM3 = station.getBaseFrame(). + getTrackingCoordinates(positionM3, stateM3.getFrame(), stateM3.getDate()); + double[] delayM3 = model.mappingFactors(trackingCoordinatesM3, point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + stateM3.getDate()); + + SpacecraftState stateM2 = shiftState(state, orbitType, angleType, -2 * steps[i], i); + final Vector3D positionM2 = stateM2.getPosition(); + final TrackingCoordinates trackingCoordinatesM2 = station.getBaseFrame(). + getTrackingCoordinates(positionM2, stateM2.getFrame(), stateM2.getDate()); + double[] delayM2 = model.mappingFactors(trackingCoordinatesM2, point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + stateM2.getDate()); + + SpacecraftState stateM1 = shiftState(state, orbitType, angleType, -1 * steps[i], i); + final Vector3D positionM1 = stateM1.getPosition(); + final TrackingCoordinates trackingCoordinatesM1 = station.getBaseFrame(). + getTrackingCoordinates(positionM1, stateM1.getFrame(), stateM1.getDate()); + double[] delayM1 = model.mappingFactors(trackingCoordinatesM1, point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + stateM1.getDate()); + + SpacecraftState stateP1 = shiftState(state, orbitType, angleType, 1 * steps[i], i); + final Vector3D positionP1 = stateP1.getPosition(); + final TrackingCoordinates trackingCoordinatesP1 = station.getBaseFrame(). + getTrackingCoordinates(positionP1, stateP1.getFrame(), stateP1.getDate()); + double[] delayP1 = model.mappingFactors(trackingCoordinatesP1, point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + stateP1.getDate()); + + SpacecraftState stateP2 = shiftState(state, orbitType, angleType, 2 * steps[i], i); + final Vector3D positionP2 = stateP2.getPosition(); + final TrackingCoordinates trackingCoordinatesP2 = station.getBaseFrame(). + getTrackingCoordinates(positionP2, stateP2.getFrame(), stateP2.getDate()); + double[] delayP2 = model.mappingFactors(trackingCoordinatesP2, point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + stateP2.getDate()); + + SpacecraftState stateP3 = shiftState(state, orbitType, angleType, 3 * steps[i], i); + final Vector3D positionP3 = stateP3.getPosition(); + final TrackingCoordinates trackingCoordinatesP3 = station.getBaseFrame(). + getTrackingCoordinates(positionP3, stateP3.getFrame(), stateP3.getDate()); + double[] delayP3 = model.mappingFactors(trackingCoordinatesP3, point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + stateP3.getDate()); + + SpacecraftState stateP4 = shiftState(state, orbitType, angleType, 4 * steps[i], i); + final Vector3D positionP4 = stateP4.getPosition(); + final TrackingCoordinates trackingCoordinatesP4 = station.getBaseFrame(). + getTrackingCoordinates(positionP4, stateP4.getFrame(), stateP4.getDate()); + double[] delayP4 = model.mappingFactors(trackingCoordinatesP4, point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + stateP4.getDate()); + + fillJacobianColumn(refMF, i, orbitType, angleType, steps[i], + delayM4, delayM3, delayM2, delayM1, + delayP1, delayP2, delayP3, delayP4); + } + + // Tolerances + for (int i = 0; i < 6; i++) { + Assertions.assertEquals(0., FastMath.abs(compMFH[i + 1] - refMF[0][i]), epsMFH); + Assertions.assertEquals(0., FastMath.abs(compMFW[i + 1] - refMF[1][i]), epsMFW); + } + } + + private void fillJacobianColumn(double[][] jacobian, int column, + OrbitType orbitType, PositionAngleType angleType, double h, + double[] sM4h, double[] sM3h, + double[] sM2h, double[] sM1h, + double[] sP1h, double[] sP2h, + double[] sP3h, double[] sP4h) { + for (int i = 0; i < jacobian.length; ++i) { + jacobian[i][column] = ( -3 * (sP4h[i] - sM4h[i]) + + 32 * (sP3h[i] - sM3h[i]) - + 168 * (sP2h[i] - sM2h[i]) + + 672 * (sP1h[i] - sM1h[i])) / (840 * h); + } + } + + private SpacecraftState shiftState(SpacecraftState state, OrbitType orbitType, PositionAngleType angleType, + double delta, int column) { + + double[][] array = stateToArray(state, orbitType, angleType, true); + array[0][column] += delta; + + return arrayToState(array, orbitType, angleType, state.getFrame(), state.getDate(), + state.getMu(), state.getAttitude()); + + } + + private double[][] stateToArray(SpacecraftState state, OrbitType orbitType, PositionAngleType angleType, + boolean withMass) { + double[][] array = new double[2][withMass ? 7 : 6]; + orbitType.mapOrbitToArray(state.getOrbit(), angleType, array[0], array[1]); + if (withMass) { + array[0][6] = state.getMass(); + } + return array; + } + + private SpacecraftState arrayToState(double[][] array, OrbitType orbitType, PositionAngleType angleType, + Frame frame, AbsoluteDate date, double mu, + Attitude attitude) { + Orbit orbit = orbitType.mapArrayToOrbit(array[0], array[1], angleType, date, mu, frame); + return (array.length > 6) ? + new SpacecraftState(orbit, attitude) : + new SpacecraftState(orbit, attitude, array[0][6]); + } + + @BeforeEach + public void setUp() { + Utils.setDataRoot("regular-data"); + } + +} diff --git a/src/test/java/org/orekit/models/earth/troposphere/AbstractMappingFunctionTest.java b/src/test/java/org/orekit/models/earth/troposphere/AbstractMappingFunctionTest.java new file mode 100644 index 0000000000..1e1f097562 --- /dev/null +++ b/src/test/java/org/orekit/models/earth/troposphere/AbstractMappingFunctionTest.java @@ -0,0 +1,90 @@ +/* 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.models.earth.troposphere; + +import org.hipparchus.util.FastMath; +import org.hipparchus.util.Precision; +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.GeodeticPoint; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.TimeScalesFactory; +import org.orekit.utils.TrackingCoordinates; + +public abstract class AbstractMappingFunctionTest { + + protected abstract TroposphereMappingFunction buildMappingFunction(); + + @Test + public abstract void testMappingFactors(); + + protected void doTestMappingFactors(final double expectedHydro, + final double expectedWet) { + + // Site (Le Mans, France): latitude: 48.0° + // longitude: 0.20° + // height: 68 m + + final AbsoluteDate date = new AbsoluteDate(1994, 1, 1, TimeScalesFactory.getUTC()); + + final double latitude = FastMath.toRadians(48.0); + final double longitude = FastMath.toRadians(0.20); + final double height = 68.0; + final GeodeticPoint point = new GeodeticPoint(latitude, longitude, height); + + final TrackingCoordinates trackingCoordinates = new TrackingCoordinates(0.0, FastMath.toRadians(5.0), 0.0); + + final TroposphereMappingFunction model = buildMappingFunction(); + + final double[] computedMapping = model.mappingFactors(trackingCoordinates, point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + date); + Assertions.assertEquals(expectedHydro, computedMapping[0], 1.0e-2); + Assertions.assertEquals(expectedWet, computedMapping[1], 1.0e-2); + + } + + @Test + public void testFixedHeight() { + final AbsoluteDate date = new AbsoluteDate(); + final GeodeticPoint point = new GeodeticPoint(FastMath.toRadians(45.0), FastMath.toRadians(45.0), 350.0); + TroposphereMappingFunction model = buildMappingFunction(); + double[] lastFactors = new double[] { + Double.MAX_VALUE, + Double.MAX_VALUE + }; + // mapping functions shall decline with increasing elevation angle + for (double elev = 10d; elev < 90d; elev += 8d) { + final double[] factors = model.mappingFactors(new TrackingCoordinates(0.0, FastMath.toRadians(elev), 0.0), + point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + date); + Assertions.assertTrue(Precision.compareTo(factors[0], lastFactors[0], 1.0e-6) < 0); + Assertions.assertTrue(Precision.compareTo(factors[1], lastFactors[1], 1.0e-6) < 0); + lastFactors[0] = factors[0]; + lastFactors[1] = factors[1]; + } + } + + @BeforeEach + public void setUp() { + Utils.setDataRoot("regular-data"); + } + +} diff --git a/src/test/java/org/orekit/models/earth/troposphere/AskneNordiusModelTest.java b/src/test/java/org/orekit/models/earth/troposphere/AskneNordiusModelTest.java new file mode 100644 index 0000000000..c0a6d8d627 --- /dev/null +++ b/src/test/java/org/orekit/models/earth/troposphere/AskneNordiusModelTest.java @@ -0,0 +1,130 @@ +/* Copyright 2002-2024 Thales Alenia Space + * Licensed to CS Communication & Systèmes (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.models.earth.troposphere; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.util.Binary64Field; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.Precision; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.orekit.Utils; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; +import org.orekit.models.earth.weather.HeightDependentPressureTemperatureHumidityConverter; +import org.orekit.models.earth.weather.PressureTemperatureHumidity; +import org.orekit.models.earth.weather.water.CIPM2007; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldTrackingCoordinates; +import org.orekit.utils.TrackingCoordinates; + + +public class AskneNordiusModelTest { + + @Test + public void testFixedElevation() { + Utils.setDataRoot("atmosphere"); + // first line of GPT 2w grid + PressureTemperatureHumidity pth = new PressureTemperatureHumidity(0.0, + 101421, 259.2, + new CIPM2007().waterVaporPressure(101421, 259.2, 1.64), + 255.1, 1.665); + AskneNordiusModel model = new AskneNordiusModel(new DummyMappingFunction()); + + HeightDependentPressureTemperatureHumidityConverter converter = + new HeightDependentPressureTemperatureHumidityConverter(new CIPM2007()); + double lastDelay = Double.MAX_VALUE; + // delay shall decline with increasing height of the station + for (double height = 0; height < 5000; height += 100) { + final double delay = model.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(5), 0.0), + new GeodeticPoint(0.0, 0.0, height), + converter.convert(pth, height), + null, AbsoluteDate.J2000_EPOCH).getZw(); + Assertions.assertTrue(Precision.compareTo(delay, lastDelay, 1.0e-6) < 0); + lastDelay = delay; + } + } + + @Test + public void testFieldFixedElevation() { + doTestFieldFixedElevation(Binary64Field.getInstance()); + } + + private > void doTestFieldFixedElevation(final Field field) { + final T zero = field.getZero(); + Utils.setDataRoot("atmosphere"); + // first line of GPT 2w grid + PressureTemperatureHumidity pth = new PressureTemperatureHumidity(0.0, + 101421, 259.2, + new CIPM2007().waterVaporPressure(101421, 259.2, 1.64), + 255.1, 1.665); + AskneNordiusModel model = new AskneNordiusModel(new DummyMappingFunction()); + HeightDependentPressureTemperatureHumidityConverter converter = + new HeightDependentPressureTemperatureHumidityConverter(new CIPM2007()); + T lastDelay = zero.newInstance(Double.MAX_VALUE); + // delay shall decline with increasing height of the station + for (double height = 0; height < 5000; height += 100) { + final T delay = model.pathDelay(new FieldTrackingCoordinates<>(zero, + zero.newInstance(FastMath.toRadians(5)), + zero), + new FieldGeodeticPoint<>(zero, zero, zero.newInstance(height)), + converter.convert(new FieldPressureTemperatureHumidity<>(field, pth), + zero.newInstance(height)), + null, FieldAbsoluteDate.getJ2000Epoch(field)).getZw(); + Assertions.assertTrue(Precision.compareTo(delay.getReal(), lastDelay.getReal(), 1.0e-6) < 0); + lastDelay = delay; + } + } + + @Test + public void testFieldVsNative() { + doTestFieldVsNative(Binary64Field.getInstance()); + } + + private > void doTestFieldVsNative(final Field field) { + final T zero = field.getZero(); + Utils.setDataRoot("atmosphere"); + // first line of GPT 2w grid + PressureTemperatureHumidity pth = new PressureTemperatureHumidity(0.0, + 101421, 259.2, + new CIPM2007().waterVaporPressure(101421, 259.2, 1.64), + 255.1, 1.665); + AskneNordiusModel model = new AskneNordiusModel(new DummyMappingFunction()); + HeightDependentPressureTemperatureHumidityConverter converter = + new HeightDependentPressureTemperatureHumidityConverter(new CIPM2007()); + for (int h = 0; h < 5000.0; h += 100) { + for (int e = 0; e < 90; e += 1.0) { + final double delayN = model.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(e), 0.0), + new GeodeticPoint(0, 0, h), + converter.convert(pth, h), + null, AbsoluteDate.J2000_EPOCH).getZw(); + final T delayT = model.pathDelay(new FieldTrackingCoordinates<>(zero, + zero.newInstance(FastMath.toRadians(e)), + zero), + new FieldGeodeticPoint<>(zero, zero, zero.newInstance(h)), + converter.convert(new FieldPressureTemperatureHumidity<>(field, pth), + zero.newInstance(h)), + null, FieldAbsoluteDate.getJ2000Epoch(field)).getZw(); + Assertions.assertEquals(delayN, delayT.getReal(), 1.0e-6); + } + } + } + +} diff --git a/src/test/java/org/orekit/models/earth/troposphere/CanonicalSaastamoinenModelTest.java b/src/test/java/org/orekit/models/earth/troposphere/CanonicalSaastamoinenModelTest.java new file mode 100644 index 0000000000..0d8fa3c302 --- /dev/null +++ b/src/test/java/org/orekit/models/earth/troposphere/CanonicalSaastamoinenModelTest.java @@ -0,0 +1,83 @@ +/* Copyright 2002-2024 Thales Alenia Space + * Licensed to CS Communication & Systèmes (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.models.earth.troposphere; + +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.orekit.Utils; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldTrackingCoordinates; +import org.orekit.utils.TrackingCoordinates; + + +public class CanonicalSaastamoinenModelTest { + + @Test + public void testComparisonToModifiedModelLowElevation() { + doTestComparisonToModifiedModel(FastMath.toRadians(5), -13.4, 0.14); + } + + @Test + public void testComparisonToModifiedModelHighElevation() { + doTestComparisonToModifiedModel(FastMath.toRadians(60), -1.36, 0.002); + } + + private void doTestComparisonToModifiedModel(final double elevation, + final double minDifference, final double maxDifference) { + final TrackingCoordinates trackingCoordinates = new TrackingCoordinates(0.0, elevation, 0.0); + final CanonicalSaastamoinenModel canonical = new CanonicalSaastamoinenModel(); + final ModifiedSaastamoinenModel modified = ModifiedSaastamoinenModel.getStandardModel(); + Assertions.assertTrue(canonical.getParametersDrivers().isEmpty()); + canonical.setLowElevationThreshold(0.125); + Assertions.assertEquals(0.125, canonical.getLowElevationThreshold(), 1.0e-12); + canonical.setLowElevationThreshold(CanonicalSaastamoinenModel.DEFAULT_LOW_ELEVATION_THRESHOLD); + for (double height = 0; height < 5000; height += 100) { + final GeodeticPoint location = new GeodeticPoint(0.0, 0.0, height); + final double canonicalDelay = canonical.pathDelay(trackingCoordinates, location, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + null, AbsoluteDate.J2000_EPOCH).getDelay(); + final double modifiedDelay = modified.pathDelay(trackingCoordinates, location, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + null, AbsoluteDate.J2000_EPOCH).getDelay(); + Assertions.assertTrue(modifiedDelay - canonicalDelay > minDifference); + Assertions.assertTrue(modifiedDelay - canonicalDelay < maxDifference); + final Binary64Field field = Binary64Field.getInstance(); + Assertions.assertEquals(canonicalDelay, + canonical.pathDelay(new FieldTrackingCoordinates<>(field, trackingCoordinates), + new FieldGeodeticPoint<>(field, location), + new FieldPressureTemperatureHumidity<>(field, + TroposphericModelUtils.STANDARD_ATMOSPHERE), + MathArrays.buildArray(field, 0), + FieldAbsoluteDate.getJ2000Epoch(field)).getDelay().getReal(), + 1.0e-10); + } + } + + @BeforeEach + public void setUp() { + Utils.setDataRoot("atmosphere"); + } + +} diff --git a/src/test/java/org/orekit/models/earth/troposphere/ChaoMappingFunctionTest.java b/src/test/java/org/orekit/models/earth/troposphere/ChaoMappingFunctionTest.java new file mode 100644 index 0000000000..3381e43342 --- /dev/null +++ b/src/test/java/org/orekit/models/earth/troposphere/ChaoMappingFunctionTest.java @@ -0,0 +1,32 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.troposphere; + +import org.junit.jupiter.api.Test; + +public class ChaoMappingFunctionTest extends AbstractMappingFunctionTest { + + protected TroposphereMappingFunction buildMappingFunction() { + return new ChaoMappingFunction(); + } + + @Test + public void testMappingFactors() { + doTestMappingFactors(10.21, 11.05); + } + +} diff --git a/src/test/java/org/orekit/models/earth/troposphere/DummyMappingFunctionTest.java b/src/test/java/org/orekit/models/earth/troposphere/DummyMappingFunctionTest.java new file mode 100644 index 0000000000..e0043f6183 --- /dev/null +++ b/src/test/java/org/orekit/models/earth/troposphere/DummyMappingFunctionTest.java @@ -0,0 +1,37 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.troposphere; + +import org.junit.jupiter.api.Test; + +public class DummyMappingFunctionTest extends AbstractMappingFunctionTest { + + protected TroposphereMappingFunction buildMappingFunction() { + return new DummyMappingFunction(); + } + + @Test + public void testMappingFactors() { + doTestMappingFactors(1.0, 1.0); + } + + @Test + public void testFixedHeight() { + // this function does *not* decrease with elevation + // so we disable this test + } +} diff --git a/src/test/java/org/orekit/models/earth/troposphere/EstimatedModelTest.java b/src/test/java/org/orekit/models/earth/troposphere/EstimatedModelTest.java new file mode 100644 index 0000000000..42ed48f18d --- /dev/null +++ b/src/test/java/org/orekit/models/earth/troposphere/EstimatedModelTest.java @@ -0,0 +1,453 @@ +/* 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.models.earth.troposphere; + +import org.hipparchus.Field; +import org.hipparchus.analysis.differentiation.DSFactory; +import org.hipparchus.analysis.differentiation.DerivativeStructure; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.Precision; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.orekit.Utils; +import org.orekit.attitudes.Attitude; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.bodies.OneAxisEllipsoid; +import org.orekit.errors.OrekitException; +import org.orekit.estimation.measurements.GroundStation; +import org.orekit.frames.Frame; +import org.orekit.frames.FramesFactory; +import org.orekit.frames.TopocentricFrame; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; +import org.orekit.orbits.FieldKeplerianOrbit; +import org.orekit.orbits.FieldOrbit; +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.numerical.NumericalPropagator; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.TimeScalesFactory; +import org.orekit.utils.Constants; +import org.orekit.utils.FieldTrackingCoordinates; +import org.orekit.utils.IERSConventions; +import org.orekit.utils.ParameterDriver; +import org.orekit.utils.ParameterDriversList; +import org.orekit.utils.TrackingCoordinates; + +import java.util.List; + +public class EstimatedModelTest { + + @BeforeAll + public static void setUpGlobal() { + Utils.setDataRoot("atmosphere"); + } + + @BeforeEach + public void setUp() throws OrekitException { + Utils.setDataRoot("regular-data:potential/shm-format"); + } + + @Test + public void testFixedHeight() { + final AbsoluteDate date = new AbsoluteDate(); + GeodeticPoint point = new GeodeticPoint(FastMath.toRadians(45.0), FastMath.toRadians(45.0), 350.0); + TroposphereMappingFunction mapping = new NiellMappingFunctionModel(); + TroposphericModel model = new EstimatedModel(mapping, 2.0); + double lastDelay = Double.MAX_VALUE; + // delay shall decline with increasing elevation angle + for (double elev = 10d; elev < 90d; elev += 8d) { + final double delay = model.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(elev), 0.0), + point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), date).getDelay(); + Assertions.assertTrue(Precision.compareTo(delay, lastDelay, 1.0e-6) < 0); + lastDelay = delay; + } + } + + @Test + public void testDelay() { + final double elevation = 10d; + final double height = 100d; + final AbsoluteDate date = new AbsoluteDate(); + GeodeticPoint point = new GeodeticPoint(FastMath.toRadians(45.0), FastMath.toRadians(45.0), height); + TroposphereMappingFunction mapping = new NiellMappingFunctionModel(); + TroposphericModel model = new EstimatedModel(mapping, 2.0); + final double path = model.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(elevation), 0.0), + point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), date).getDelay(); + Assertions.assertTrue(Precision.compareTo(path, 20d, 1.0e-6) < 0); + Assertions.assertTrue(Precision.compareTo(path, 0d, 1.0e-6) > 0); + } + + @Test + public void testStateDerivativesGMF() { + final double latitude = FastMath.toRadians(45.0); + final double longitude = FastMath.toRadians(45.0); + GeodeticPoint point = new GeodeticPoint(latitude, longitude, 0.0); + final TroposphereMappingFunction gmf = new GlobalMappingFunctionModel(); + doTestDelayStateDerivatives(gmf, point, 4.7e-9); + } + + @Test + public void testStateDerivativesNMF() { + final double latitude = FastMath.toRadians(45.0); + final double longitude = FastMath.toRadians(45.0); + GeodeticPoint point = new GeodeticPoint(latitude, longitude, 0.0); + final TroposphereMappingFunction nmf = new NiellMappingFunctionModel(); + doTestDelayStateDerivatives(nmf, point, 4.4e-9); + } + + private void doTestDelayStateDerivatives(final TroposphereMappingFunction func, final GeodeticPoint point, final double tolerance) { + + // Body: earth + final OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, + Constants.WGS84_EARTH_FLATTENING, + FramesFactory.getITRF(IERSConventions.IERS_2010, true)); + // Topocentric frame + final TopocentricFrame baseFrame = new TopocentricFrame(earth, point, "topo"); + + // Station + final GroundStation station = new GroundStation(baseFrame, + TroposphericModelUtils.STANDARD_ATMOSPHERE_PROVIDER); + + // Tropospheric model + final TroposphericModel model = new EstimatedModel(func, 2.0); + + // Derivative Structure + final DSFactory factory = new DSFactory(6, 1); + final DerivativeStructure a0 = factory.variable(0, 24464560.0); + final DerivativeStructure e0 = factory.variable(1, 0.05); + final DerivativeStructure i0 = factory.variable(2, 0.122138); + final DerivativeStructure pa0 = factory.variable(3, 3.10686); + final DerivativeStructure raan0 = factory.variable(4, 1.00681); + final DerivativeStructure anomaly0 = factory.variable(5, 0.048363); + final Field field = a0.getField(); + final DerivativeStructure zero = field.getZero(); + + // Field Date + final FieldAbsoluteDate dsDate = new FieldAbsoluteDate<>(field); + // Field Orbit + final Frame frame = FramesFactory.getEME2000(); + final FieldOrbit dsOrbit = new FieldKeplerianOrbit<>(a0, e0, i0, pa0, raan0, anomaly0, + PositionAngleType.MEAN, frame, + dsDate, zero.add(3.9860047e14)); + // Field State + final FieldSpacecraftState dsState = new FieldSpacecraftState<>(dsOrbit); + + // Initial satellite elevation + final FieldVector3D position = dsState.getPosition(); + final FieldTrackingCoordinates dsTrackingCoordinates = + baseFrame.getTrackingCoordinates(position, frame, dsDate); + + // Set drivers reference date + for (final ParameterDriver driver : model.getParametersDrivers()) { + driver.setReferenceDate(dsDate.toAbsoluteDate()); + } + + // Compute Delay with state derivatives + final FieldGeodeticPoint dsPoint = new FieldGeodeticPoint<>(zero.add(point.getLatitude()), zero.add(point.getLongitude()), zero.add(point.getAltitude())); + final DerivativeStructure delay = model.pathDelay(dsTrackingCoordinates, dsPoint, + new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + model.getParameters(field), dsDate).getDelay(); + + final double[] compDeriv = delay.getAllDerivatives(); + + // Field -> non-field + final Orbit orbit = dsOrbit.toOrbit(); + final SpacecraftState state = dsState.toSpacecraftState(); + + // Finite differences for reference values + final double[][] refDeriv = new double[1][6]; + final OrbitType orbitType = OrbitType.KEPLERIAN; + final PositionAngleType angleType = PositionAngleType.MEAN; + double dP = 0.001; + double[] steps = NumericalPropagator.tolerances(1000000 * dP, orbit, orbitType)[0]; + for (int i = 0; i < 6; i++) { + SpacecraftState stateM4 = shiftState(state, orbitType, angleType, -4 * steps[i], i); + final Vector3D positionM4 = stateM4.getPosition(); + final TrackingCoordinates trackingCoordinatesM4 = station.getBaseFrame(). + getTrackingCoordinates(positionM4, stateM4.getFrame(), stateM4.getDate()); + double delayM4 = model.pathDelay(trackingCoordinatesM4, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateM4.getDate()).getDelay(); + + SpacecraftState stateM3 = shiftState(state, orbitType, angleType, -3 * steps[i], i); + final Vector3D positionM3 = stateM3.getPosition(); + final TrackingCoordinates trackingCoordinatesM3 = station.getBaseFrame(). + getTrackingCoordinates(positionM3, stateM3.getFrame(), stateM3.getDate()); + double delayM3 = model.pathDelay(trackingCoordinatesM3, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateM3.getDate()).getDelay(); + + SpacecraftState stateM2 = shiftState(state, orbitType, angleType, -2 * steps[i], i); + final Vector3D positionM2 = stateM2.getPosition(); + final TrackingCoordinates trackingCoordinatesM2 = station.getBaseFrame(). + getTrackingCoordinates(positionM2, stateM2.getFrame(), stateM2.getDate()); + double delayM2 = model.pathDelay(trackingCoordinatesM2, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateM2.getDate()).getDelay(); + + SpacecraftState stateM1 = shiftState(state, orbitType, angleType, -1 * steps[i], i); + final Vector3D positionM1 = stateM1.getPosition(); + final TrackingCoordinates trackingCoordinatesM1 = station.getBaseFrame(). + getTrackingCoordinates(positionM1, stateM1.getFrame(), stateM1.getDate()); + double delayM1 = model.pathDelay(trackingCoordinatesM1, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateM1.getDate()).getDelay(); + + SpacecraftState stateP1 = shiftState(state, orbitType, angleType, 1 * steps[i], i); + final Vector3D positionP1 = stateP1.getPosition(); + final TrackingCoordinates trackingCoordinatesP1 = station.getBaseFrame(). + getTrackingCoordinates(positionP1, stateP1.getFrame(), stateP1.getDate()); + double delayP1 = model.pathDelay(trackingCoordinatesP1, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateP1.getDate()).getDelay(); + + SpacecraftState stateP2 = shiftState(state, orbitType, angleType, 2 * steps[i], i); + final Vector3D positionP2 = stateP2.getPosition(); + final TrackingCoordinates trackingCoordinatesP2 = station.getBaseFrame(). + getTrackingCoordinates(positionP2, stateP2.getFrame(), stateP2.getDate()); + double delayP2 = model.pathDelay(trackingCoordinatesP2, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateP2.getDate()).getDelay(); + + SpacecraftState stateP3 = shiftState(state, orbitType, angleType, 3 * steps[i], i); + final Vector3D positionP3 = stateP3.getPosition(); + final TrackingCoordinates trackingCoordinatesP3 = station.getBaseFrame(). + getTrackingCoordinates(positionP3, stateP3.getFrame(), stateP3.getDate()); + double delayP3 = model.pathDelay(trackingCoordinatesP3, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateP3.getDate()).getDelay(); + + SpacecraftState stateP4 = shiftState(state, orbitType, angleType, 4 * steps[i], i); + final Vector3D positionP4 = stateP4.getPosition(); + final TrackingCoordinates trackingCoordinatesP4 = station.getBaseFrame(). + getTrackingCoordinates(positionP4, stateP4.getFrame(), stateP4.getDate()); + double delayP4 = model.pathDelay(trackingCoordinatesP4, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateP4.getDate()).getDelay(); + + fillJacobianColumn(refDeriv, i, orbitType, angleType, steps[i], + delayM4, delayM3, delayM2, delayM1, + delayP1, delayP2, delayP3, delayP4); + } + + for (int i = 0; i < 6; i++) { + Assertions.assertEquals(compDeriv[i + 1], refDeriv[0][i], tolerance); + } + } + + @Test + public void testDelayParameterDerivative() { + doTestParametersDerivatives(EstimatedModel.TOTAL_ZENITH_DELAY, 5.0e-15); + } + + private void doTestParametersDerivatives(String parameterName, double tolerance) { + + // Geodetic point + final double latitude = FastMath.toRadians(45.0); + final double longitude = FastMath.toRadians(45.0); + final double height = 0.0; + final GeodeticPoint point = new GeodeticPoint(latitude, longitude, height); + // Body: earth + final OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, + Constants.WGS84_EARTH_FLATTENING, + FramesFactory.getITRF(IERSConventions.IERS_2010, true)); + // Topocentric frame + final TopocentricFrame baseFrame = new TopocentricFrame(earth, point, "topo"); + + // Tropospheric model + final TroposphereMappingFunction gmf = new GlobalMappingFunctionModel(); + final TroposphericModel model = new EstimatedModel(gmf, 5.0); + + // Set Parameter Driver + for (final ParameterDriver driver : model.getParametersDrivers()) { + driver.setValue(driver.getReferenceValue()); + driver.setSelected(driver.getName().equals(parameterName)); + } + + // Count the required number of parameters + int nbParams = 0; + for (final ParameterDriver driver : model.getParametersDrivers()) { + if (driver.isSelected()) { + ++nbParams; + } + } + + // Derivative Structure + final DSFactory factory = new DSFactory(6 + nbParams, 1); + final DerivativeStructure a0 = factory.variable(0, 24464560.0); + final DerivativeStructure e0 = factory.variable(1, 0.05); + final DerivativeStructure i0 = factory.variable(2, 0.122138); + final DerivativeStructure pa0 = factory.variable(3, 3.10686); + final DerivativeStructure raan0 = factory.variable(4, 1.00681); + final DerivativeStructure anomaly0 = factory.variable(5, 0.048363); + final Field field = a0.getField(); + final DerivativeStructure zero = field.getZero(); + + // Field Date + final FieldAbsoluteDate dsDate = new FieldAbsoluteDate<>(field, 2018, 11, 19, 18, 0, 0.0, + TimeScalesFactory.getUTC()); + + // Set drivers reference date + for (final ParameterDriver driver : model.getParametersDrivers()) { + driver.setReferenceDate(dsDate.toAbsoluteDate()); + } + + // Field Orbit + final Frame frame = FramesFactory.getEME2000(); + final FieldOrbit dsOrbit = new FieldKeplerianOrbit<>(a0, e0, i0, pa0, raan0, anomaly0, + PositionAngleType.MEAN, frame, + dsDate, zero.add(3.9860047e14)); + + // Field State + final FieldSpacecraftState dsState = new FieldSpacecraftState<>(dsOrbit); + + // Initial satellite elevation + final FieldVector3D position = dsState.getPosition(); + final FieldTrackingCoordinates dsTrackingCoordinates = + baseFrame.getTrackingCoordinates(position, frame, dsState.getDate()); + + // Add parameter as a variable + final List drivers = model.getParametersDrivers(); + final DerivativeStructure[] parameters = new DerivativeStructure[drivers.size()]; + int index = 6; + for (int i = 0; i < drivers.size(); ++i) { + parameters[i] = drivers.get(i).isSelected() ? + factory.variable(index++, drivers.get(i).getValue()) : + factory.constant(drivers.get(i).getValue()); + } + + // Compute delay state derivatives + final FieldGeodeticPoint dsPoint = new FieldGeodeticPoint<>(zero.add(point.getLatitude()), zero.add(point.getLongitude()), zero.add(point.getAltitude())); + final DerivativeStructure delay = model.pathDelay(dsTrackingCoordinates, dsPoint, + new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + parameters, dsState.getDate()).getDelay(); + + final double[] compDeriv = delay.getAllDerivatives(); + + // Field -> non-field + final SpacecraftState state = dsState.toSpacecraftState(); + final TrackingCoordinates trackingCoordinates = new TrackingCoordinates(dsTrackingCoordinates.getAzimuth().getReal(), + dsTrackingCoordinates.getElevation().getReal(), + dsTrackingCoordinates.getRange().getReal()); + + // Finite differences for reference values + final double[][] refDeriv = new double[1][1]; + ParameterDriversList bound = new ParameterDriversList(); + for (final ParameterDriver driver : model.getParametersDrivers()) { + if (driver.getName().equals(parameterName)) { + driver.setSelected(true); + bound.add(driver); + } else { + driver.setSelected(false); + } + } + ParameterDriver selected = bound.getDrivers().get(0); + double p0 = selected.getReferenceValue(); + double h = selected.getScale(); + + final OrbitType orbitType = OrbitType.KEPLERIAN; + final PositionAngleType angleType = PositionAngleType.MEAN; + + selected.setValue(p0 - 4 * h); + double delayM4 = model.pathDelay(trackingCoordinates, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), state.getDate()).getDelay(); + + selected.setValue(p0 - 3 * h); + double delayM3 = model.pathDelay(trackingCoordinates, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), state.getDate()).getDelay(); + + selected.setValue(p0 - 2 * h); + double delayM2 = model.pathDelay(trackingCoordinates, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), state.getDate()).getDelay(); + + selected.setValue(p0 - 1 * h); + double delayM1 = model.pathDelay(trackingCoordinates, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), state.getDate()).getDelay(); + + selected.setValue(p0 + 1 * h); + double delayP1 = model.pathDelay(trackingCoordinates, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), state.getDate()).getDelay(); + + selected.setValue(p0 + 2 * h); + double delayP2 = model.pathDelay(trackingCoordinates, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), state.getDate()).getDelay(); + + selected.setValue(p0 + 3 * h); + double delayP3 = model.pathDelay(trackingCoordinates, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), state.getDate()).getDelay(); + + selected.setValue(p0 + 4 * h); + double delayP4 = model.pathDelay(trackingCoordinates, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), state.getDate()).getDelay(); + + fillJacobianColumn(refDeriv, 0, orbitType, angleType, h, + delayM4, delayM3, delayM2, delayM1, + delayP1, delayP2, delayP3, delayP4); + + Assertions.assertEquals(compDeriv[7], refDeriv[0][0], tolerance); + + } + + private SpacecraftState shiftState(SpacecraftState state, OrbitType orbitType, PositionAngleType angleType, + double delta, int column) { + + double[][] array = stateToArray(state, orbitType, angleType, true); + array[0][column] += delta; + + return arrayToState(array, orbitType, angleType, state.getFrame(), state.getDate(), + state.getMu(), state.getAttitude()); + + } + + private double[][] stateToArray(SpacecraftState state, OrbitType orbitType, PositionAngleType angleType, + boolean withMass) { + double[][] array = new double[2][withMass ? 7 : 6]; + orbitType.mapOrbitToArray(state.getOrbit(), angleType, array[0], array[1]); + if (withMass) { + array[0][6] = state.getMass(); + } + return array; + } + + private SpacecraftState arrayToState(double[][] array, OrbitType orbitType, PositionAngleType angleType, + Frame frame, AbsoluteDate date, double mu, + Attitude attitude) { + Orbit orbit = orbitType.mapArrayToOrbit(array[0], array[1], angleType, date, mu, frame); + return (array.length > 6) ? + new SpacecraftState(orbit, attitude) : + new SpacecraftState(orbit, attitude, array[0][6]); + } + + private void fillJacobianColumn(double[][] jacobian, int column, + OrbitType orbitType, PositionAngleType angleType, double h, + double sM4h, double sM3h, + double sM2h, double sM1h, + double sP1h, double sP2h, + double sP3h, double sP4h) { + + jacobian[0][column] = ( -3 * (sP4h - sM4h) + + 32 * (sP3h - sM3h) - + 168 * (sP2h - sM2h) + + 672 * (sP1h - sM1h)) / (840 * h); + } + +} diff --git a/src/test/java/org/orekit/models/earth/troposphere/EstimatedTroposphericModelTest.java b/src/test/java/org/orekit/models/earth/troposphere/EstimatedTroposphericModelTest.java index 34b63d6bea..c3ea9b3c13 100644 --- a/src/test/java/org/orekit/models/earth/troposphere/EstimatedTroposphericModelTest.java +++ b/src/test/java/org/orekit/models/earth/troposphere/EstimatedTroposphericModelTest.java @@ -55,6 +55,7 @@ import java.util.List; +@Deprecated public class EstimatedTroposphericModelTest { @BeforeAll @@ -123,7 +124,8 @@ private void doTestDelayStateDerivatives(final MappingFunction func, final Geode final TopocentricFrame baseFrame = new TopocentricFrame(earth, point, "topo"); // Station - final GroundStation station = new GroundStation(baseFrame); + final GroundStation station = new GroundStation(baseFrame, + TroposphericModelUtils.STANDARD_ATMOSPHERE_PROVIDER); // Tropospheric model final DiscreteTroposphericModel model = new EstimatedTroposphericModel(func, 2.0); diff --git a/src/test/java/org/orekit/models/earth/troposphere/FieldChaoMappingFunctionTest.java b/src/test/java/org/orekit/models/earth/troposphere/FieldChaoMappingFunctionTest.java new file mode 100644 index 0000000000..7ea6a01a0c --- /dev/null +++ b/src/test/java/org/orekit/models/earth/troposphere/FieldChaoMappingFunctionTest.java @@ -0,0 +1,38 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.troposphere; + +import org.hipparchus.util.Binary64Field; +import org.junit.jupiter.api.Test; + +public class FieldChaoMappingFunctionTest extends AbstractFieldMappingFunctionTest { + + protected TroposphereMappingFunction buildMappingFunction() { + return new ChaoMappingFunction(); + } + + @Test + public void testMappingFactors() { + doTestMappingFactors(Binary64Field.getInstance(), 10.21, 11.05); + } + + @Test + public void testMFStateDerivatives() { + doTestMFStateDerivatives(2.2e-11, 9.2e-12); + } + +} diff --git a/src/test/java/org/orekit/models/earth/troposphere/FieldGlobalMappingFunctionModelTest.java b/src/test/java/org/orekit/models/earth/troposphere/FieldGlobalMappingFunctionModelTest.java index 5112f6b264..c2c422be4f 100644 --- a/src/test/java/org/orekit/models/earth/troposphere/FieldGlobalMappingFunctionModelTest.java +++ b/src/test/java/org/orekit/models/earth/troposphere/FieldGlobalMappingFunctionModelTest.java @@ -40,6 +40,7 @@ import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; import org.orekit.frames.TopocentricFrame; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; import org.orekit.orbits.FieldKeplerianOrbit; import org.orekit.orbits.FieldOrbit; import org.orekit.orbits.Orbit; @@ -52,7 +53,9 @@ import org.orekit.time.FieldAbsoluteDate; import org.orekit.time.TimeScalesFactory; import org.orekit.utils.Constants; +import org.orekit.utils.FieldTrackingCoordinates; import org.orekit.utils.IERSConventions; +import org.orekit.utils.TrackingCoordinates; public class FieldGlobalMappingFunctionModelTest { @@ -92,13 +95,19 @@ private > void doTestMappingFactors(final Fiel final double height = 844.715; final FieldGeodeticPoint point = new FieldGeodeticPoint(zero.add(latitude), zero.add(longitude), zero.add(height)); - final double elevation = 0.5 * FastMath.PI - 1.278564131; + final FieldTrackingCoordinates trackingCoordinates = + new FieldTrackingCoordinates<>(zero, + zero.newInstance(0.5 * FastMath.PI - 1.278564131), + zero); final double expectedHydro = 3.425246; final double expectedWet = 3.449589; - final MappingFunction model = new GlobalMappingFunctionModel(); + final TroposphereMappingFunction model = new GlobalMappingFunctionModel(); + final FieldPressureTemperatureHumidity weather = + new FieldPressureTemperatureHumidity<>(field, + TroposphericModelUtils.STANDARD_ATMOSPHERE); - final T[] computedMapping = model.mappingFactors(zero.add(elevation), point, date); + final T[] computedMapping = model.mappingFactors(trackingCoordinates, point, weather, date); Assertions.assertEquals(expectedHydro, computedMapping[0].getReal(), 1.0e-6); Assertions.assertEquals(expectedWet, computedMapping[1].getReal(), 1.0e-6); @@ -112,7 +121,10 @@ public void testFixedHeight() { private > void doTestFixedHeight(final Field field) { final T zero = field.getZero(); final FieldAbsoluteDate date = new FieldAbsoluteDate<>(field); - MappingFunction model = new GlobalMappingFunctionModel(); + TroposphereMappingFunction model = new GlobalMappingFunctionModel(); + final FieldPressureTemperatureHumidity weather = + new FieldPressureTemperatureHumidity<>(field, + TroposphericModelUtils.STANDARD_ATMOSPHERE); FieldGeodeticPoint point = new FieldGeodeticPoint(zero.add(FastMath.toRadians(45.0)), zero.add(FastMath.toRadians(45.0)), zero.add(350.0)); final T[] lastFactors = MathArrays.buildArray(field, 2); lastFactors[0] = zero.add(Double.MAX_VALUE); @@ -120,7 +132,10 @@ private > void doTestFixedHeight(final Field(zero, + zero.newInstance(FastMath.toRadians(elev)), + zero), + point, weather, date); Assertions.assertTrue(Precision.compareTo(factors[0].getReal(), lastFactors[0].getReal(), 1.0e-6) < 0); Assertions.assertTrue(Precision.compareTo(factors[1].getReal(), lastFactors[1].getReal(), 1.0e-6) < 0); lastFactors[0] = factors[0]; @@ -144,10 +159,11 @@ public void testMFStateDerivatives() { final TopocentricFrame baseFrame = new TopocentricFrame(earth, point, "topo"); // Station - final GroundStation station = new GroundStation(baseFrame); + final GroundStation station = new GroundStation(baseFrame, + TroposphericModelUtils.STANDARD_ATMOSPHERE_PROVIDER); // Mapping Function model - final MappingFunction model = new GlobalMappingFunctionModel(); + final TroposphereMappingFunction model = new GlobalMappingFunctionModel(); // Derivative Structure final DSFactory factory = new DSFactory(6, 1); @@ -160,6 +176,10 @@ public void testMFStateDerivatives() { final Field field = a0.getField(); final DerivativeStructure zero = field.getZero(); + final FieldPressureTemperatureHumidity weather = + new FieldPressureTemperatureHumidity<>(field, + TroposphericModelUtils.STANDARD_ATMOSPHERE); + // Field Date final FieldAbsoluteDate dsDate = new FieldAbsoluteDate<>(field); // Field Orbit @@ -172,11 +192,12 @@ public void testMFStateDerivatives() { // Initial satellite elevation final FieldVector3D position = dsState.getPosition(); - final DerivativeStructure dsElevation = baseFrame.getTrackingCoordinates(position, frame, dsDate).getElevation(); + final FieldTrackingCoordinates dsTrackingCoordinates = + baseFrame.getTrackingCoordinates(position, frame, dsDate); // Compute mapping factors state derivatives final FieldGeodeticPoint dsPoint = new FieldGeodeticPoint<>(zero.add(latitude), zero.add(longitude), zero.add(height)); - final DerivativeStructure[] factors = model.mappingFactors(dsElevation, dsPoint, dsDate); + final DerivativeStructure[] factors = model.mappingFactors(dsTrackingCoordinates, dsPoint, weather, dsDate); final double[] compMFH = factors[0].getAllDerivatives(); final double[] compMFW = factors[1].getAllDerivatives(); @@ -194,59 +215,67 @@ public void testMFStateDerivatives() { for (int i = 0; i < 6; i++) { SpacecraftState stateM4 = shiftState(state, orbitType, angleType, -4 * steps[i], i); final Vector3D positionM4 = stateM4.getPosition(); - final double elevationM4 = station.getBaseFrame(). - getTrackingCoordinates(positionM4, stateM4.getFrame(), stateM4.getDate()). - getElevation(); - double[] delayM4 = model.mappingFactors(elevationM4, point, stateM4.getDate()); + final TrackingCoordinates trackingCoordinatesM4 = station.getBaseFrame(). + getTrackingCoordinates(positionM4, stateM4.getFrame(), stateM4.getDate()); + double[] delayM4 = model.mappingFactors(trackingCoordinatesM4, point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + stateM4.getDate()); SpacecraftState stateM3 = shiftState(state, orbitType, angleType, -3 * steps[i], i); final Vector3D positionM3 = stateM3.getPosition(); - final double elevationM3 = station.getBaseFrame(). - getTrackingCoordinates(positionM3, stateM3.getFrame(), stateM3.getDate()). - getElevation(); - double[] delayM3 = model.mappingFactors(elevationM3, point, stateM3.getDate()); + final TrackingCoordinates trackingCoordinatesM3 = station.getBaseFrame(). + getTrackingCoordinates(positionM3, stateM3.getFrame(), stateM3.getDate()); + double[] delayM3 = model.mappingFactors(trackingCoordinatesM3, point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + stateM3.getDate()); SpacecraftState stateM2 = shiftState(state, orbitType, angleType, -2 * steps[i], i); final Vector3D positionM2 = stateM2.getPosition(); - final double elevationM2 = station.getBaseFrame(). - getTrackingCoordinates(positionM2, stateM2.getFrame(), stateM2.getDate()). - getElevation(); - double[] delayM2 = model.mappingFactors(elevationM2, point, stateM2.getDate()); + final TrackingCoordinates trackingCoordinatesM2 = station.getBaseFrame(). + getTrackingCoordinates(positionM2, stateM2.getFrame(), stateM2.getDate()); + double[] delayM2 = model.mappingFactors(trackingCoordinatesM2, point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + stateM2.getDate()); SpacecraftState stateM1 = shiftState(state, orbitType, angleType, -1 * steps[i], i); final Vector3D positionM1 = stateM1.getPosition(); - final double elevationM1 = station.getBaseFrame(). - getTrackingCoordinates(positionM1, stateM1.getFrame(), stateM1.getDate()). - getElevation(); - double[] delayM1 = model.mappingFactors(elevationM1, point, stateM1.getDate()); + final TrackingCoordinates trackingCoordinatesM1 = station.getBaseFrame(). + getTrackingCoordinates(positionM1, stateM1.getFrame(), stateM1.getDate()); + double[] delayM1 = model.mappingFactors(trackingCoordinatesM1, point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + stateM1.getDate()); SpacecraftState stateP1 = shiftState(state, orbitType, angleType, 1 * steps[i], i); final Vector3D positionP1 = stateP1.getPosition(); - final double elevationP1 = station.getBaseFrame(). - getTrackingCoordinates(positionP1, stateP1.getFrame(), stateP1.getDate()). - getElevation(); - double[] delayP1 = model.mappingFactors(elevationP1, point, stateP1.getDate()); + final TrackingCoordinates trackingCoordinatesP1 = station.getBaseFrame(). + getTrackingCoordinates(positionP1, stateP1.getFrame(), stateP1.getDate()); + double[] delayP1 = model.mappingFactors(trackingCoordinatesP1, point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + stateP1.getDate()); SpacecraftState stateP2 = shiftState(state, orbitType, angleType, 2 * steps[i], i); final Vector3D positionP2 = stateP2.getPosition(); - final double elevationP2 = station.getBaseFrame(). - getTrackingCoordinates(positionP2, stateP2.getFrame(), stateP2.getDate()). - getElevation(); - double[] delayP2 = model.mappingFactors(elevationP2, point, stateP2.getDate()); + final TrackingCoordinates trackingCoordinatesP2 = station.getBaseFrame(). + getTrackingCoordinates(positionP2, stateP2.getFrame(), stateP2.getDate()); + double[] delayP2 = model.mappingFactors(trackingCoordinatesP2, point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + stateP2.getDate()); SpacecraftState stateP3 = shiftState(state, orbitType, angleType, 3 * steps[i], i); final Vector3D positionP3 = stateP3.getPosition(); - final double elevationP3 = station.getBaseFrame(). - getTrackingCoordinates(positionP3, stateP3.getFrame(), stateP3.getDate()). - getElevation(); - double[] delayP3 = model.mappingFactors(elevationP3, point, stateP3.getDate()); + final TrackingCoordinates trackingCoordinatesP3 = station.getBaseFrame(). + getTrackingCoordinates(positionP3, stateP3.getFrame(), stateP3.getDate()); + double[] delayP3 = model.mappingFactors(trackingCoordinatesP3, point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + stateP3.getDate()); SpacecraftState stateP4 = shiftState(state, orbitType, angleType, 4 * steps[i], i); final Vector3D positionP4 = stateP4.getPosition(); - final double elevationP4 = station.getBaseFrame(). - getTrackingCoordinates(positionP4, stateP4.getFrame(), stateP4.getDate()). - getElevation(); - double[] delayP4 = model.mappingFactors(elevationP4, point, stateP4.getDate()); + final TrackingCoordinates trackingCoordinatesP4 = station.getBaseFrame(). + getTrackingCoordinates(positionP4, stateP4.getFrame(), stateP4.getDate()); + double[] delayP4 = model.mappingFactors(trackingCoordinatesP4, point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + stateP4.getDate()); fillJacobianColumn(refMF, i, orbitType, angleType, steps[i], delayM4, delayM3, delayM2, delayM1, diff --git a/src/test/java/org/orekit/models/earth/troposphere/FieldMendesPavlisModelTest.java b/src/test/java/org/orekit/models/earth/troposphere/FieldMendesPavlisModelTest.java index 723ed57127..c4b39f291b 100644 --- a/src/test/java/org/orekit/models/earth/troposphere/FieldMendesPavlisModelTest.java +++ b/src/test/java/org/orekit/models/earth/troposphere/FieldMendesPavlisModelTest.java @@ -39,6 +39,10 @@ import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; import org.orekit.frames.TopocentricFrame; +import org.orekit.models.earth.weather.ConstantPressureTemperatureHumidityProvider; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; +import org.orekit.models.earth.weather.PressureTemperatureHumidity; +import org.orekit.models.earth.weather.water.CIPM2007; import org.orekit.orbits.FieldKeplerianOrbit; import org.orekit.orbits.FieldOrbit; import org.orekit.orbits.Orbit; @@ -51,7 +55,9 @@ import org.orekit.time.FieldAbsoluteDate; import org.orekit.time.TimeScalesFactory; import org.orekit.utils.Constants; +import org.orekit.utils.FieldTrackingCoordinates; import org.orekit.utils.IERSConventions; +import org.orekit.utils.TrackingCoordinates; public class FieldMendesPavlisModelTest { @@ -90,9 +96,18 @@ private > void doTestZenithDelay(final Field point = new FieldGeodeticPoint<>(zero.add(latitude), zero.add(longitude), zero.add(height)); @@ -107,8 +122,8 @@ private > void doTestZenithDelay(final Field date = new FieldAbsoluteDate<>(field, 2009, 8, 12, TimeScalesFactory.getUTC()); - final MendesPavlisModel model = new MendesPavlisModel(temperature, pressure, - humidity, lambda); + final MendesPavlisModel model = new MendesPavlisModel(new ConstantPressureTemperatureHumidityProvider(pth), + lambda, TroposphericModelUtils.MICRO_M); final T[] computedDelay = model.computeZenithDelay(point, model.getParameters(field), date); @@ -144,21 +159,35 @@ private > void doTestMappingFactors(final Fiel final double latitude = FastMath.toRadians(30.67166667); final double longitude = FastMath.toRadians(-104.0250); final double height = 2075; - final double pressure = 798.4188; + final double pressure = TroposphericModelUtils.HECTO_PASCAL.toSI(798.4188); final double temperature = 300.15; final double humidity = 0.4; + final PressureTemperatureHumidity pth = new PressureTemperatureHumidity(height, + pressure, + temperature, + new CIPM2007(). + waterVaporPressure(pressure, + temperature, + humidity), + Double.NaN, + Double.NaN); final double lambda = 0.532; final FieldGeodeticPoint point = new FieldGeodeticPoint<>(zero.add(latitude), zero.add(longitude), zero.add(height)); - final double elevation = FastMath.toRadians(15.0); + final FieldTrackingCoordinates trackingCoordinates = new FieldTrackingCoordinates<>(zero, + zero.newInstance(FastMath.toRadians(15.0)), + zero); // Expected mapping factor: 3.80024367 (Ref) final double expectedMapping = 3.80024367; // Test for the second constructor - final MendesPavlisModel model = new MendesPavlisModel(temperature, pressure, - humidity, lambda); + final MendesPavlisModel model = new MendesPavlisModel(new ConstantPressureTemperatureHumidityProvider(pth), + lambda, TroposphericModelUtils.MICRO_M); - final T[] computedMapping = model.mappingFactors(zero.add(elevation), point, date); + final T[] computedMapping = model.mappingFactors(trackingCoordinates, point, + new FieldPressureTemperatureHumidity<>(field, + TroposphericModelUtils.STANDARD_ATMOSPHERE), + date); Assertions.assertEquals(expectedMapping, computedMapping[0].getReal(), 5.0e-8); Assertions.assertEquals(expectedMapping, computedMapping[1].getReal(), 5.0e-8); @@ -175,8 +204,13 @@ private > void doTestDelay(final Field fiel final double height = 100d; final FieldAbsoluteDate date = new FieldAbsoluteDate<>(field); final FieldGeodeticPoint point = new FieldGeodeticPoint<>(zero.add(FastMath.toRadians(45.0)), zero.add(FastMath.toRadians(45.0)), zero.add(height)); - MendesPavlisModel model = MendesPavlisModel.getStandardModel(0.6943); - final T path = model.pathDelay(zero.add(FastMath.toRadians(elevation)), point, model.getParameters(field), date); + MendesPavlisModel model = MendesPavlisModel.getStandardModel(0.6943, TroposphericModelUtils.MICRO_M); + final T path = model.pathDelay(new FieldTrackingCoordinates<>(zero, + zero.newInstance(FastMath.toRadians(elevation)), + zero), + point, + new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + model.getParameters(field), date).getDelay(); Assertions.assertTrue(Precision.compareTo(path.getReal(), 20d, epsilon) < 0); Assertions.assertTrue(Precision.compareTo(path.getReal(), 0d, epsilon) > 0); } @@ -190,11 +224,16 @@ private > void doTestFixedHeight(final Field date = new FieldAbsoluteDate<>(field); final FieldGeodeticPoint point = new FieldGeodeticPoint<>(zero.add(FastMath.toRadians(45.0)), zero.add(FastMath.toRadians(45.0)), zero.add(350.0)); - MendesPavlisModel model = MendesPavlisModel.getStandardModel(0.6943); + MendesPavlisModel model = MendesPavlisModel.getStandardModel(0.6943, TroposphericModelUtils.MICRO_M); T lastDelay = zero.add(Double.MAX_VALUE); // delay shall decline with increasing elevation angle for (double elev = 10d; elev < 90d; elev += 8d) { - final T delay = model.pathDelay(zero.add(FastMath.toRadians(elev)), point, model.getParameters(field), date); + final T delay = model.pathDelay(new FieldTrackingCoordinates<>(zero, + zero.newInstance(FastMath.toRadians(elev)), + zero), + point, + new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + model.getParameters(field), date).getDelay(); Assertions.assertTrue(Precision.compareTo(delay.getReal(), lastDelay.getReal(), epsilon) < 0); lastDelay = delay; } @@ -216,10 +255,11 @@ public void testDelayStateDerivatives() { final TopocentricFrame baseFrame = new TopocentricFrame(earth, point, "topo"); // Station - final GroundStation station = new GroundStation(baseFrame); + final GroundStation station = new GroundStation(baseFrame, + TroposphericModelUtils.STANDARD_ATMOSPHERE_PROVIDER); // Tropospheric model - final MendesPavlisModel model = MendesPavlisModel.getStandardModel(0.65); + final MendesPavlisModel model = MendesPavlisModel.getStandardModel(0.65, TroposphericModelUtils.MICRO_M); // Derivative Structure final DSFactory factory = new DSFactory(6, 1); @@ -244,11 +284,14 @@ public void testDelayStateDerivatives() { // Initial satellite elevation final FieldVector3D position = dsState.getPosition(); - final DerivativeStructure dsElevation = baseFrame.getTrackingCoordinates(position, frame, dsDate).getElevation(); + final FieldTrackingCoordinates dsTrackingCoordinates = + baseFrame.getTrackingCoordinates(position, frame, dsDate); // Compute Delay with state derivatives final FieldGeodeticPoint dsPoint = new FieldGeodeticPoint<>(zero.add(latitude), zero.add(longitude), zero.add(height)); - final DerivativeStructure delay = model.pathDelay(dsElevation, dsPoint, model.getParameters(field, dsDate), dsDate); + final DerivativeStructure delay = model.pathDelay(dsTrackingCoordinates, dsPoint, + new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + model.getParameters(field, dsDate), dsDate).getDelay(); final double[] compDeriv = delay.getAllDerivatives(); @@ -265,59 +308,59 @@ public void testDelayStateDerivatives() { for (int i = 0; i < 6; i++) { SpacecraftState stateM4 = shiftState(state, orbitType, angleType, -4 * steps[i], i); final Vector3D positionM4 = stateM4.getPosition(); - final double elevationM4 = station.getBaseFrame(). - getTrackingCoordinates(positionM4, stateM4.getFrame(), stateM4.getDate()). - getElevation(); - double delayM4 = model.pathDelay(elevationM4, point, model.getParameters(), stateM4.getDate()); + final TrackingCoordinates trackingCoordinatesM4 = station.getBaseFrame(). + getTrackingCoordinates(positionM4, stateM4.getFrame(), stateM4.getDate()); + double delayM4 = model.pathDelay(trackingCoordinatesM4, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateM4.getDate()).getDelay(); SpacecraftState stateM3 = shiftState(state, orbitType, angleType, -3 * steps[i], i); final Vector3D positionM3 = stateM3.getPosition(); - final double elevationM3 = station.getBaseFrame(). - getTrackingCoordinates(positionM3, stateM3.getFrame(), stateM3.getDate()). - getElevation(); - double delayM3 = model.pathDelay(elevationM3, point, model.getParameters(), stateM3.getDate()); + final TrackingCoordinates trackingCoordinatesM3 = station.getBaseFrame(). + getTrackingCoordinates(positionM3, stateM3.getFrame(), stateM3.getDate()); + double delayM3 = model.pathDelay(trackingCoordinatesM3, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateM3.getDate()).getDelay(); SpacecraftState stateM2 = shiftState(state, orbitType, angleType, -2 * steps[i], i); final Vector3D positionM2 = stateM2.getPosition(); - final double elevationM2 = station.getBaseFrame(). - getTrackingCoordinates(positionM2, stateM2.getFrame(), stateM2.getDate()). - getElevation(); - double delayM2 = model.pathDelay(elevationM2, point, model.getParameters(), stateM2.getDate()); + final TrackingCoordinates trackingCoordinatesM2 = station.getBaseFrame(). + getTrackingCoordinates(positionM2, stateM2.getFrame(), stateM2.getDate()); + double delayM2 = model.pathDelay(trackingCoordinatesM2, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateM2.getDate()).getDelay(); SpacecraftState stateM1 = shiftState(state, orbitType, angleType, -1 * steps[i], i); final Vector3D positionM1 = stateM1.getPosition(); - final double elevationM1 = station.getBaseFrame(). - getTrackingCoordinates(positionM1, stateM1.getFrame(), stateM1.getDate()). - getElevation(); - double delayM1 = model.pathDelay(elevationM1, point, model.getParameters(), stateM1.getDate()); + final TrackingCoordinates trackingCoordinatesM1 = station.getBaseFrame(). + getTrackingCoordinates(positionM1, stateM1.getFrame(), stateM1.getDate()); + double delayM1 = model.pathDelay(trackingCoordinatesM1, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateM1.getDate()).getDelay(); SpacecraftState stateP1 = shiftState(state, orbitType, angleType, 1 * steps[i], i); final Vector3D positionP1 = stateP1.getPosition(); - final double elevationP1 = station.getBaseFrame(). - getTrackingCoordinates(positionP1, stateP1.getFrame(), stateP1.getDate()). - getElevation(); - double delayP1 = model.pathDelay(elevationP1, point, model.getParameters(), stateP1.getDate()); + final TrackingCoordinates trackingCoordinatesP1 = station.getBaseFrame(). + getTrackingCoordinates(positionP1, stateP1.getFrame(), stateP1.getDate()); + double delayP1 = model.pathDelay(trackingCoordinatesP1, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateP1.getDate()).getDelay(); SpacecraftState stateP2 = shiftState(state, orbitType, angleType, 2 * steps[i], i); final Vector3D positionP2 = stateP2.getPosition(); - final double elevationP2 = station.getBaseFrame(). - getTrackingCoordinates(positionP2, stateP2.getFrame(), stateP2.getDate()). - getElevation(); - double delayP2 = model.pathDelay(elevationP2, point, model.getParameters(), stateP2.getDate()); + final TrackingCoordinates trackingCoordinatesP2 = station.getBaseFrame(). + getTrackingCoordinates(positionP2, stateP2.getFrame(), stateP2.getDate()); + double delayP2 = model.pathDelay(trackingCoordinatesP2, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateP2.getDate()).getDelay(); SpacecraftState stateP3 = shiftState(state, orbitType, angleType, 3 * steps[i], i); final Vector3D positionP3 = stateP3.getPosition(); - final double elevationP3 = station.getBaseFrame(). - getTrackingCoordinates(positionP3, stateP3.getFrame(), stateP3.getDate()). - getElevation(); - double delayP3 = model.pathDelay(elevationP3, point, model.getParameters(), stateP3.getDate()); + final TrackingCoordinates trackingCoordinatesP3 = station.getBaseFrame(). + getTrackingCoordinates(positionP3, stateP3.getFrame(), stateP3.getDate()); + double delayP3 = model.pathDelay(trackingCoordinatesP3, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateP3.getDate()).getDelay(); SpacecraftState stateP4 = shiftState(state, orbitType, angleType, 4 * steps[i], i); final Vector3D positionP4 = stateP4.getPosition(); - final double elevationP4 = station.getBaseFrame(). - getTrackingCoordinates(positionP4, stateP4.getFrame(), stateP4.getDate()). - getElevation(); - double delayP4 = model.pathDelay(elevationP4, point, model.getParameters(), stateP4.getDate()); + final TrackingCoordinates trackingCoordinatesP4 = station.getBaseFrame(). + getTrackingCoordinates(positionP4, stateP4.getFrame(), stateP4.getDate()); + double delayP4 = model.pathDelay(trackingCoordinatesP4, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateP4.getDate()).getDelay(); fillJacobianColumn(refDeriv, i, orbitType, angleType, steps[i], delayM4, delayM3, delayM2, delayM1, diff --git a/src/test/java/org/orekit/models/earth/troposphere/FieldNiellMappingFunctionModelTest.java b/src/test/java/org/orekit/models/earth/troposphere/FieldNiellMappingFunctionModelTest.java index c12948e8c1..f6884e8396 100644 --- a/src/test/java/org/orekit/models/earth/troposphere/FieldNiellMappingFunctionModelTest.java +++ b/src/test/java/org/orekit/models/earth/troposphere/FieldNiellMappingFunctionModelTest.java @@ -16,296 +16,23 @@ */ package org.orekit.models.earth.troposphere; -import org.hipparchus.CalculusFieldElement; -import org.hipparchus.Field; -import org.hipparchus.analysis.differentiation.DSFactory; -import org.hipparchus.analysis.differentiation.DerivativeStructure; -import org.hipparchus.geometry.euclidean.threed.FieldVector3D; -import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.Binary64Field; -import org.hipparchus.util.FastMath; -import org.hipparchus.util.MathArrays; -import org.hipparchus.util.Precision; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.orekit.Utils; -import org.orekit.attitudes.Attitude; -import org.orekit.bodies.FieldGeodeticPoint; -import org.orekit.bodies.GeodeticPoint; -import org.orekit.bodies.OneAxisEllipsoid; -import org.orekit.errors.OrekitException; -import org.orekit.estimation.measurements.GroundStation; -import org.orekit.frames.Frame; -import org.orekit.frames.FramesFactory; -import org.orekit.frames.TopocentricFrame; -import org.orekit.orbits.FieldKeplerianOrbit; -import org.orekit.orbits.FieldOrbit; -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.numerical.NumericalPropagator; -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; -public class FieldNiellMappingFunctionModelTest { +class FieldNiellMappingFunctionModelTest extends AbstractFieldMappingFunctionTest { - @BeforeAll - public static void setUpGlobal() { - Utils.setDataRoot("atmosphere"); - } - - @BeforeEach - public void setUp() throws OrekitException { - Utils.setDataRoot("regular-data:potential/shm-format"); + protected TroposphereMappingFunction buildMappingFunction() { + return new NiellMappingFunctionModel(); } @Test public void testMappingFactors() { - doTestMappingFactors(Binary64Field.getInstance()); - } - - private > void doTestMappingFactors(final Field field) { - - final T zero = field.getZero(); - - // Site (Le Mans, France): latitude: 48.0° - // longitude: 0.20° - // height: 68 m - // - // Date: 1st January 1994 at 0h UT - // - // Ref: Mercier F., Perosanz F., Mesures GNSS, Résolution des ambiguités. - // - // Expected mapping factors : hydrostatic -> 10.16 (Ref) - // wet -> 10.75 (Ref) - - final FieldAbsoluteDate date = new FieldAbsoluteDate<>(field, 1994, 1, 1, TimeScalesFactory.getUTC()); - - final double latitude = FastMath.toRadians(48.0); - final double longitude = FastMath.toRadians(0.20); - final double height = 68.0; - - final FieldGeodeticPoint point = new FieldGeodeticPoint<>(zero.add(latitude), zero.add(longitude), zero.add(height)); - - final double elevation = FastMath.toRadians(5.0); - final double expectedHydro = 10.16; - final double expectedWet = 10.75; - - final MappingFunction model = new NiellMappingFunctionModel(); - - final T[] computedMapping = model.mappingFactors(zero.add(elevation), point, date); - - Assertions.assertEquals(expectedHydro, computedMapping[0].getReal(), 1.0e-2); - Assertions.assertEquals(expectedWet, computedMapping[1].getReal(), 1.0e-2); - } - - @Test - public void testFixedHeight() { - doTestFixedHeight(Binary64Field.getInstance()); - } - - private > void doTestFixedHeight(final Field field) { - final T zero = field.getZero(); - final FieldAbsoluteDate date = new FieldAbsoluteDate<>(field); - final FieldGeodeticPoint point = new FieldGeodeticPoint<>(zero.add(FastMath.toRadians(45.0)), zero.add(FastMath.toRadians(45.0)), zero.add(350.0)); - MappingFunction model = new NiellMappingFunctionModel(); - T[] lastFactors = MathArrays.buildArray(field, 2); - lastFactors[0] = zero.add(Double.MAX_VALUE); - lastFactors[1] = zero.add(Double.MAX_VALUE); - // mapping functions shall decline with increasing elevation angle - for (double elev = 10d; elev < 90d; elev += 8d) { - final T[] factors = model.mappingFactors(zero.add(FastMath.toRadians(elev)), point, - date); - Assertions.assertTrue(Precision.compareTo(factors[0].getReal(), lastFactors[0].getReal(), 1.0e-6) < 0); - Assertions.assertTrue(Precision.compareTo(factors[1].getReal(), lastFactors[1].getReal(), 1.0e-6) < 0); - lastFactors[0] = factors[0]; - lastFactors[1] = factors[1]; - } + doTestMappingFactors(Binary64Field.getInstance(), 10.16, 10.75); } @Test public void testMFStateDerivatives() { - - // Geodetic point - final double latitude = FastMath.toRadians(45.0); - final double longitude = FastMath.toRadians(45.0); - final double height = 0.0; - final GeodeticPoint point = new GeodeticPoint(latitude, longitude, height); - // Body: earth - final OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, - Constants.WGS84_EARTH_FLATTENING, - FramesFactory.getITRF(IERSConventions.IERS_2010, true)); - // Topocentric frame - final TopocentricFrame baseFrame = new TopocentricFrame(earth, point, "topo"); - - // Station - final GroundStation station = new GroundStation(baseFrame); - - // Mapping Function model - final MappingFunction model = new NiellMappingFunctionModel(); - - // Derivative Structure - final DSFactory factory = new DSFactory(6, 1); - final DerivativeStructure a0 = factory.variable(0, 24464560.0); - final DerivativeStructure e0 = factory.variable(1, 0.05); - final DerivativeStructure i0 = factory.variable(2, 0.122138); - final DerivativeStructure pa0 = factory.variable(3, 3.10686); - final DerivativeStructure raan0 = factory.variable(4, 1.00681); - final DerivativeStructure anomaly0 = factory.variable(5, 0.048363); - final Field field = a0.getField(); - final DerivativeStructure zero = field.getZero(); - - // Field Date - final FieldAbsoluteDate dsDate = new FieldAbsoluteDate<>(field); - // Field Orbit - final Frame frame = FramesFactory.getEME2000(); - final FieldOrbit dsOrbit = new FieldKeplerianOrbit<>(a0, e0, i0, pa0, raan0, anomaly0, - PositionAngleType.MEAN, frame, - dsDate, zero.add(3.9860047e14)); - // Field State - final FieldSpacecraftState dsState = new FieldSpacecraftState<>(dsOrbit); - - // Initial satellite elevation - final FieldVector3D position = dsState.getPosition(); - final DerivativeStructure dsElevation = baseFrame.getTrackingCoordinates(position, frame, dsDate).getElevation(); - - // Compute mapping factors with state derivatives - final FieldGeodeticPoint dsPoint = new FieldGeodeticPoint<>(zero.add(latitude), zero.add(longitude), zero.add(height)); - final DerivativeStructure[] factors = model.mappingFactors(dsElevation, dsPoint, dsDate); - - final double[] compMFH = factors[0].getAllDerivatives(); - final double[] compMFW = factors[1].getAllDerivatives(); - - // Field -> non-field - final Orbit orbit = dsOrbit.toOrbit(); - final SpacecraftState state = dsState.toSpacecraftState(); - - // Finite differences for reference values - final double[][] refMF = new double[2][6]; - final OrbitType orbitType = OrbitType.KEPLERIAN; - final PositionAngleType angleType = PositionAngleType.MEAN; - double dP = 0.001; - double[] steps = NumericalPropagator.tolerances(1000000 * dP, orbit, orbitType)[0]; - for (int i = 0; i < 6; i++) { - SpacecraftState stateM4 = shiftState(state, orbitType, angleType, -4 * steps[i], i); - final Vector3D positionM4 = stateM4.getPosition(); - final double elevationM4 = station.getBaseFrame(). - getTrackingCoordinates(positionM4, stateM4.getFrame(), stateM4.getDate()). - getElevation(); - double[] delayM4 = model.mappingFactors(elevationM4, point, stateM4.getDate()); - - SpacecraftState stateM3 = shiftState(state, orbitType, angleType, -3 * steps[i], i); - final Vector3D positionM3 = stateM3.getPosition(); - final double elevationM3 = station.getBaseFrame(). - getTrackingCoordinates(positionM3, stateM3.getFrame(), stateM3.getDate()). - getElevation(); - double[] delayM3 = model.mappingFactors(elevationM3, point, stateM3.getDate()); - - SpacecraftState stateM2 = shiftState(state, orbitType, angleType, -2 * steps[i], i); - final Vector3D positionM2 = stateM2.getPosition(); - final double elevationM2 = station.getBaseFrame(). - getTrackingCoordinates(positionM2, stateM2.getFrame(), stateM2.getDate()). - getElevation(); - double[] delayM2 = model.mappingFactors(elevationM2, point, stateM2.getDate()); - - SpacecraftState stateM1 = shiftState(state, orbitType, angleType, -1 * steps[i], i); - final Vector3D positionM1 = stateM1.getPosition(); - final double elevationM1 = station.getBaseFrame(). - getTrackingCoordinates(positionM1, stateM1.getFrame(), stateM1.getDate()). - getElevation(); - double[] delayM1 = model.mappingFactors(elevationM1, point, stateM1.getDate()); - - SpacecraftState stateP1 = shiftState(state, orbitType, angleType, 1 * steps[i], i); - final Vector3D positionP1 = stateP1.getPosition(); - final double elevationP1 = station.getBaseFrame(). - getTrackingCoordinates(positionP1, stateP1.getFrame(), stateP1.getDate()). - getElevation(); - double[] delayP1 = model.mappingFactors(elevationP1, point, stateP1.getDate()); - - SpacecraftState stateP2 = shiftState(state, orbitType, angleType, 2 * steps[i], i); - final Vector3D positionP2 = stateP2.getPosition(); - final double elevationP2 = station.getBaseFrame(). - getTrackingCoordinates(positionP2, stateP2.getFrame(), stateP2.getDate()). - getElevation(); - double[] delayP2 = model.mappingFactors(elevationP2, point, stateP2.getDate()); - - SpacecraftState stateP3 = shiftState(state, orbitType, angleType, 3 * steps[i], i); - final Vector3D positionP3 = stateP3.getPosition(); - final double elevationP3 = station.getBaseFrame(). - getTrackingCoordinates(positionP3, stateP3.getFrame(), stateP3.getDate()). - getElevation(); - double[] delayP3 = model.mappingFactors(elevationP3, point, stateP3.getDate()); - - SpacecraftState stateP4 = shiftState(state, orbitType, angleType, 4 * steps[i], i); - final Vector3D positionP4 = stateP4.getPosition(); - final double elevationP4 = station.getBaseFrame(). - getTrackingCoordinates(positionP4, stateP4.getFrame(), stateP4.getDate()). - getElevation(); - double[] delayP4 = model.mappingFactors(elevationP4, point, stateP4.getDate()); - - fillJacobianColumn(refMF, i, orbitType, angleType, steps[i], - delayM4, delayM3, delayM2, delayM1, - delayP1, delayP2, delayP3, delayP4); - } - - // Tolerances - final double epsMFH = 6.506e-12; - final double epsMFW = 1.557e-11; - for (int i = 0; i < 6; i++) { - Assertions.assertEquals(0., FastMath.abs(compMFH[i + 1] - refMF[0][i]), epsMFH); - Assertions.assertEquals(0., FastMath.abs(compMFW[i + 1] - refMF[1][i]), epsMFW); - } - } - - private void fillJacobianColumn(double[][] jacobian, int column, - OrbitType orbitType, PositionAngleType angleType, double h, - double[] sM4h, double[] sM3h, - double[] sM2h, double[] sM1h, - double[] sP1h, double[] sP2h, - double[] sP3h, double[] sP4h) { - for (int i = 0; i < jacobian.length; ++i) { - jacobian[i][column] = ( -3 * (sP4h[i] - sM4h[i]) + - 32 * (sP3h[i] - sM3h[i]) - - 168 * (sP2h[i] - sM2h[i]) + - 672 * (sP1h[i] - sM1h[i])) / (840 * h); - } - } - - private SpacecraftState shiftState(SpacecraftState state, OrbitType orbitType, PositionAngleType angleType, - double delta, int column) { - - double[][] array = stateToArray(state, orbitType, angleType, true); - array[0][column] += delta; - - return arrayToState(array, orbitType, angleType, state.getFrame(), state.getDate(), - state.getMu(), state.getAttitude()); - - } - - private double[][] stateToArray(SpacecraftState state, OrbitType orbitType, PositionAngleType angleType, - boolean withMass) { - double[][] array = new double[2][withMass ? 7 : 6]; - orbitType.mapOrbitToArray(state.getOrbit(), angleType, array[0], array[1]); - if (withMass) { - array[0][6] = state.getMass(); - } - return array; - } - - private SpacecraftState arrayToState(double[][] array, OrbitType orbitType, PositionAngleType angleType, - Frame frame, AbsoluteDate date, double mu, - Attitude attitude) { - Orbit orbit = orbitType.mapArrayToOrbit(array[0], array[1], angleType, date, mu, frame); - return (array.length > 6) ? - new SpacecraftState(orbit, attitude) : - new SpacecraftState(orbit, attitude, array[0][6]); + doTestMFStateDerivatives(6.542e-12, 1.557e-11); } } diff --git a/src/test/java/org/orekit/models/earth/troposphere/FieldRevisedChaoMappingFunctionTest.java b/src/test/java/org/orekit/models/earth/troposphere/FieldRevisedChaoMappingFunctionTest.java new file mode 100644 index 0000000000..23411b5dd5 --- /dev/null +++ b/src/test/java/org/orekit/models/earth/troposphere/FieldRevisedChaoMappingFunctionTest.java @@ -0,0 +1,38 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.troposphere; + +import org.hipparchus.util.Binary64Field; +import org.junit.jupiter.api.Test; + +public class FieldRevisedChaoMappingFunctionTest extends AbstractFieldMappingFunctionTest { + + protected TroposphereMappingFunction buildMappingFunction() { + return new RevisedChaoMappingFunction(); + } + + @Test + public void testMappingFactors() { + doTestMappingFactors(Binary64Field.getInstance(), 10.13, 11.05); + } + + @Test + public void testMFStateDerivatives() { + doTestMFStateDerivatives(3.2e-11, 9.2e-12); + } + +} diff --git a/src/test/java/org/orekit/models/earth/troposphere/FieldViennaOneModelTest.java b/src/test/java/org/orekit/models/earth/troposphere/FieldViennaOneModelTest.java index cc49081e8f..2af7d910d1 100644 --- a/src/test/java/org/orekit/models/earth/troposphere/FieldViennaOneModelTest.java +++ b/src/test/java/org/orekit/models/earth/troposphere/FieldViennaOneModelTest.java @@ -39,6 +39,7 @@ import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; import org.orekit.frames.TopocentricFrame; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; import org.orekit.orbits.FieldKeplerianOrbit; import org.orekit.orbits.FieldOrbit; import org.orekit.orbits.Orbit; @@ -51,8 +52,11 @@ import org.orekit.time.FieldAbsoluteDate; import org.orekit.time.TimeScalesFactory; import org.orekit.utils.Constants; +import org.orekit.utils.FieldTrackingCoordinates; import org.orekit.utils.IERSConventions; +import org.orekit.utils.TrackingCoordinates; +@Deprecated public class FieldViennaOneModelTest { private static double epsilon = 1e-6; @@ -103,7 +107,10 @@ private > void doTestMappingFactors(final Fiel final double longitude = FastMath.toRadians(280.0); final double height = 824.17; - final double elevation = 0.5 * FastMath.PI - 1.278564131; + final FieldTrackingCoordinates trackingCoordinates = + new FieldTrackingCoordinates<>(zero, + zero.newInstance(0.5 * FastMath.PI - 1.278564131), + zero); final double expectedHydro = 3.425088; final double expectedWet = 3.448300; @@ -113,7 +120,10 @@ private > void doTestMappingFactors(final Fiel final FieldGeodeticPoint point = new FieldGeodeticPoint<>(zero.add(latitude), zero.add(longitude), zero.add(height)); final ViennaOneModel model = new ViennaOneModel(a, z); - final T[] computedMapping = model.mappingFactors(zero.add(elevation), point, date); + final T[] computedMapping = model.mappingFactors(trackingCoordinates, point, + new FieldPressureTemperatureHumidity<>(field, + TroposphericModelUtils.STANDARD_ATMOSPHERE), + date); Assertions.assertEquals(expectedHydro, computedMapping[0].getReal(), 4.1e-6); Assertions.assertEquals(expectedWet, computedMapping[1].getReal(), 1.0e-6); @@ -133,7 +143,12 @@ private > void doTestDelay(final Field fiel final double[] z = {2.0966, 0.2140}; final FieldGeodeticPoint point = new FieldGeodeticPoint<>(zero.add(FastMath.toRadians(45.0)), zero.add(FastMath.toRadians(45.0)), zero.add(height)); ViennaOneModel model = new ViennaOneModel(a, z); - final T path = model.pathDelay(zero.add(FastMath.toRadians(elevation)), point, model.getParameters(field), date); + final T path = model.pathDelay(new FieldTrackingCoordinates<>(zero, + zero.newInstance(FastMath.toRadians(elevation)), + zero), + point, + new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + model.getParameters(field), date).getDelay(); Assertions.assertTrue(Precision.compareTo(path.getReal(), 20d, epsilon) < 0); Assertions.assertTrue(Precision.compareTo(path.getReal(), 0d, epsilon) > 0); } @@ -153,7 +168,12 @@ private > void doTestFixedHeight(final Field(zero, + zero.newInstance(FastMath.toRadians(elev)), + zero), + point, + new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + model.getParameters(field), date).getDelay(); Assertions.assertTrue(Precision.compareTo(delay.getReal(), lastDelay.getReal(), epsilon) < 0); lastDelay = delay; } @@ -175,12 +195,13 @@ public void testDelayStateDerivatives() { final TopocentricFrame baseFrame = new TopocentricFrame(earth, point, "topo"); // Station - final GroundStation station = new GroundStation(baseFrame); + final GroundStation station = new GroundStation(baseFrame, + TroposphericModelUtils.STANDARD_ATMOSPHERE_PROVIDER); // Tropospheric model final double[] a = { 0.00127683, 0.00060955 }; final double[] z = {2.0966, 0.2140}; - final DiscreteTroposphericModel model = new ViennaOneModel(a, z); + final TroposphericModel model = new ViennaOneModel(a, z); // Derivative Structure final DSFactory factory = new DSFactory(6, 1); @@ -206,11 +227,14 @@ public void testDelayStateDerivatives() { // Initial satellite elevation final FieldVector3D position = dsState.getPosition(); - final DerivativeStructure dsElevation = baseFrame.getTrackingCoordinates(position, frame, dsDate).getElevation(); + final FieldTrackingCoordinates dsTrackingCoordinates = + baseFrame.getTrackingCoordinates(position, frame, dsDate); // Compute delay state derivatives final FieldGeodeticPoint dsPoint = new FieldGeodeticPoint<>(zero.add(latitude), zero.add(longitude), zero.add(height)); - final DerivativeStructure delay = model.pathDelay(dsElevation, dsPoint, model.getParameters(field), dsDate); + final DerivativeStructure delay = model.pathDelay(dsTrackingCoordinates, dsPoint, + new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + model.getParameters(field), dsDate).getDelay(); final double[] compDelay = delay.getAllDerivatives(); @@ -227,59 +251,59 @@ public void testDelayStateDerivatives() { for (int i = 0; i < 6; i++) { SpacecraftState stateM4 = shiftState(state, orbitType, angleType, -4 * steps[i], i); final Vector3D positionM4 = stateM4.getPosition(); - final double elevationM4 = station.getBaseFrame(). - getTrackingCoordinates(positionM4, stateM4.getFrame(), stateM4.getDate()). - getElevation(); - double delayM4 = model.pathDelay(elevationM4, point, model.getParameters(), stateM4.getDate()); + final TrackingCoordinates trackingCoordinatesM4 = station.getBaseFrame(). + getTrackingCoordinates(positionM4, stateM4.getFrame(), stateM4.getDate()); + double delayM4 = model.pathDelay(trackingCoordinatesM4, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateM4.getDate()).getDelay(); SpacecraftState stateM3 = shiftState(state, orbitType, angleType, -3 * steps[i], i); final Vector3D positionM3 = stateM3.getPosition(); - final double elevationM3 = station.getBaseFrame(). - getTrackingCoordinates(positionM3, stateM3.getFrame(), stateM3.getDate()). - getElevation(); - double delayM3 = model.pathDelay(elevationM3, point, model.getParameters(), stateM3.getDate()); + final TrackingCoordinates trackingCoordinatesM3 = station.getBaseFrame(). + getTrackingCoordinates(positionM3, stateM3.getFrame(), stateM3.getDate()); + double delayM3 = model.pathDelay(trackingCoordinatesM3, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateM3.getDate()).getDelay(); SpacecraftState stateM2 = shiftState(state, orbitType, angleType, -2 * steps[i], i); final Vector3D positionM2 = stateM2.getPosition(); - final double elevationM2 = station.getBaseFrame(). - getTrackingCoordinates(positionM2, stateM2.getFrame(), stateM2.getDate()). - getElevation(); - double delayM2 = model.pathDelay(elevationM2, point, model.getParameters(), stateM2.getDate()); + final TrackingCoordinates trackingCoordinatesM2 = station.getBaseFrame(). + getTrackingCoordinates(positionM2, stateM2.getFrame(), stateM2.getDate()); + double delayM2 = model.pathDelay(trackingCoordinatesM2, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateM2.getDate()).getDelay(); SpacecraftState stateM1 = shiftState(state, orbitType, angleType, -1 * steps[i], i); final Vector3D positionM1 = stateM1.getPosition(); - final double elevationM1 = station.getBaseFrame(). - getTrackingCoordinates(positionM1, stateM1.getFrame(), stateM1.getDate()). - getElevation(); - double delayM1 = model.pathDelay(elevationM1, point, model.getParameters(), stateM1.getDate()); + final TrackingCoordinates trackingCoordinatesM1 = station.getBaseFrame(). + getTrackingCoordinates(positionM1, stateM1.getFrame(), stateM1.getDate()); + double delayM1 = model.pathDelay(trackingCoordinatesM1, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateM1.getDate()).getDelay(); SpacecraftState stateP1 = shiftState(state, orbitType, angleType, 1 * steps[i], i); final Vector3D positionP1 = stateP1.getPosition(); - final double elevationP1 = station.getBaseFrame(). - getTrackingCoordinates(positionP1, stateP1.getFrame(), stateP1.getDate()). - getElevation(); - double delayP1 = model.pathDelay(elevationP1, point, model.getParameters(), stateP1.getDate()); + final TrackingCoordinates trackingCoordinatesP1 = station.getBaseFrame(). + getTrackingCoordinates(positionP1, stateP1.getFrame(), stateP1.getDate()); + double delayP1 = model.pathDelay(trackingCoordinatesP1, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateP1.getDate()).getDelay(); SpacecraftState stateP2 = shiftState(state, orbitType, angleType, 2 * steps[i], i); final Vector3D positionP2 = stateP2.getPosition(); - final double elevationP2 = station.getBaseFrame(). - getTrackingCoordinates(positionP2, stateP2.getFrame(), stateP2.getDate()). - getElevation(); - double delayP2 = model.pathDelay(elevationP2, point, model.getParameters(), stateP2.getDate()); + final TrackingCoordinates trackingCoordinatesP2 = station.getBaseFrame(). + getTrackingCoordinates(positionP2, stateP2.getFrame(), stateP2.getDate()); + double delayP2 = model.pathDelay(trackingCoordinatesP2, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateP2.getDate()).getDelay(); SpacecraftState stateP3 = shiftState(state, orbitType, angleType, 3 * steps[i], i); final Vector3D positionP3 = stateP3.getPosition(); - final double elevationP3 = station.getBaseFrame(). - getTrackingCoordinates(positionP3, stateP3.getFrame(), stateP3.getDate()). - getElevation(); - double delayP3 = model.pathDelay(elevationP3, point, model.getParameters(), stateP3.getDate()); + final TrackingCoordinates trackingCoordinatesP3 = station.getBaseFrame(). + getTrackingCoordinates(positionP3, stateP3.getFrame(), stateP3.getDate()); + double delayP3 = model.pathDelay(trackingCoordinatesP3, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateP3.getDate()).getDelay(); SpacecraftState stateP4 = shiftState(state, orbitType, angleType, 4 * steps[i], i); final Vector3D positionP4 = stateP4.getPosition(); - final double elevationP4 = station.getBaseFrame(). - getTrackingCoordinates(positionP4, stateP4.getFrame(), stateP4.getDate()). - getElevation(); - double delayP4 = model.pathDelay(elevationP4, point, model.getParameters(), stateP4.getDate()); + final TrackingCoordinates trackingCoordinatesP4 = station.getBaseFrame(). + getTrackingCoordinates(positionP4, stateP4.getFrame(), stateP4.getDate()); + double delayP4 = model.pathDelay(trackingCoordinatesP4, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateP4.getDate()).getDelay(); fillJacobianColumn(refDeriv, i, orbitType, angleType, steps[i], delayM4, delayM3, delayM2, delayM1, diff --git a/src/test/java/org/orekit/models/earth/troposphere/FieldViennaOneTest.java b/src/test/java/org/orekit/models/earth/troposphere/FieldViennaOneTest.java new file mode 100644 index 0000000000..bfc056f692 --- /dev/null +++ b/src/test/java/org/orekit/models/earth/troposphere/FieldViennaOneTest.java @@ -0,0 +1,364 @@ +/* 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.models.earth.troposphere; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.analysis.differentiation.DSFactory; +import org.hipparchus.analysis.differentiation.DerivativeStructure; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.util.Binary64Field; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.Precision; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.orekit.Utils; +import org.orekit.attitudes.Attitude; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.bodies.OneAxisEllipsoid; +import org.orekit.errors.OrekitException; +import org.orekit.estimation.measurements.GroundStation; +import org.orekit.frames.Frame; +import org.orekit.frames.FramesFactory; +import org.orekit.frames.TopocentricFrame; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; +import org.orekit.orbits.FieldKeplerianOrbit; +import org.orekit.orbits.FieldOrbit; +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.numerical.NumericalPropagator; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.TimeScalesFactory; +import org.orekit.utils.Constants; +import org.orekit.utils.FieldTrackingCoordinates; +import org.orekit.utils.IERSConventions; +import org.orekit.utils.TrackingCoordinates; + +public class FieldViennaOneTest { + + private static double epsilon = 1e-6; + + @BeforeAll + public static void setUpGlobal() { + Utils.setDataRoot("atmosphere"); + } + + @BeforeEach + public void setUp() throws OrekitException { + Utils.setDataRoot("regular-data:potential/shm-format"); + } + + @Test + public void testMappingFactors() { + doTestMappingFactors(Binary64Field.getInstance()); + } + + private > void doTestMappingFactors(final Field field) { + final T zero = field.getZero(); + // Site (NRAO, Green Bank, WV): latitude: 38° + // longitude: 280° + // height: 824.17 m + // + // Date: MJD 55055 -> 12 August 2009 at 0h UT + // + // Ref for the inputs: Petit, G. and Luzum, B. (eds.), IERS Conventions (2010), + // IERS Technical Note No. 36, BKG (2010) + // + // Values: ah = 0.00127683 + // aw = 0.00060955 + // zhd = 2.0966 m + // zwd = 0.2140 m + // + // Values taken from: http://vmf.geo.tuwien.ac.at/trop_products/GRID/2.5x2/VMF1/VMF1_OP/2009/VMFG_20090812.H00 + // + // Expected mapping factors : hydrostatic -> 3.425088 + // wet -> 3.448300 + // + // Expected outputs are obtained by performing the Matlab script vmf1_ht.m provided by TU WIEN: + // http://vmf.geo.tuwien.ac.at/codes/ + // + + final FieldAbsoluteDate date = FieldAbsoluteDate.createMJDDate(55055, zero, TimeScalesFactory.getUTC()); + + final double latitude = FastMath.toRadians(38.0); + final double longitude = FastMath.toRadians(280.0); + final double height = 824.17; + + final FieldTrackingCoordinates trackingCoordinates = + new FieldTrackingCoordinates<>(zero, + zero.newInstance(0.5 * FastMath.PI - 1.278564131), + zero); + final double expectedHydro = 3.425088; + final double expectedWet = 3.448300; + + final FieldGeodeticPoint point = new FieldGeodeticPoint<>(zero.add(latitude), zero.add(longitude), zero.add(height)); + final ViennaOne model = new ViennaOne(new ConstantViennaAProvider(new ViennaACoefficients(0.00127683, 0.00060955)), + new ConstantAzimuthalGradientProvider(null), + new ConstantTroposphericModel(new TroposphericDelay(2.0966, 0.2140, 0, 0)), + TimeScalesFactory.getUTC()); + + final T[] computedMapping = model.mappingFactors(trackingCoordinates, point, + new FieldPressureTemperatureHumidity<>(field, + TroposphericModelUtils.STANDARD_ATMOSPHERE), + date); + + Assertions.assertEquals(expectedHydro, computedMapping[0].getReal(), 4.1e-6); + Assertions.assertEquals(expectedWet, computedMapping[1].getReal(), 1.0e-6); + } + + @Test + public void testDelay() { + doTestDelay(Binary64Field.getInstance()); + } + + private > void doTestDelay(final Field field) { + final T zero = field.getZero(); + final double elevation = 10d; + final double height = 100d; + final FieldAbsoluteDate date = new FieldAbsoluteDate<>(field); + final FieldGeodeticPoint point = new FieldGeodeticPoint<>(zero.add(FastMath.toRadians(45.0)), zero.add(FastMath.toRadians(45.0)), zero.add(height)); + ViennaOne model = new ViennaOne(new ConstantViennaAProvider(new ViennaACoefficients(0.00127683, 0.00060955)), + new ConstantAzimuthalGradientProvider(null), + new ConstantTroposphericModel(new TroposphericDelay(2.0966, 0.2140, 0, 0)), + TimeScalesFactory.getUTC()); + final T path = model.pathDelay(new FieldTrackingCoordinates<>(zero, + zero.newInstance(FastMath.toRadians(elevation)), + zero), + point, + new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + model.getParameters(field), date).getDelay(); + Assertions.assertTrue(Precision.compareTo(path.getReal(), 20d, epsilon) < 0); + Assertions.assertTrue(Precision.compareTo(path.getReal(), 0d, epsilon) > 0); + } + + @Test + public void testFixedHeight() { + doTestFixedHeight(Binary64Field.getInstance()); + } + + private > void doTestFixedHeight(final Field field) { + final T zero = field.getZero(); + final FieldAbsoluteDate date = new FieldAbsoluteDate<>(field); + final FieldGeodeticPoint point = new FieldGeodeticPoint<>(zero.add(FastMath.toRadians(45.0)), zero.add(FastMath.toRadians(45.0)), zero.add(350.0)); + ViennaOne model = new ViennaOne(new ConstantViennaAProvider(new ViennaACoefficients(0.00127683, 0.00060955)), + new ConstantAzimuthalGradientProvider(null), + new ConstantTroposphericModel(new TroposphericDelay(2.0966, 0.2140, 0, 0)), + TimeScalesFactory.getUTC()); + T lastDelay = zero.add(Double.MAX_VALUE); + // delay shall decline with increasing elevation angle + for (double elev = 10d; elev < 90d; elev += 8d) { + final T delay = model.pathDelay(new FieldTrackingCoordinates<>(zero, + zero.newInstance(FastMath.toRadians(elev)), + zero), + point, + new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + model.getParameters(field), date).getDelay(); + Assertions.assertTrue(Precision.compareTo(delay.getReal(), lastDelay.getReal(), epsilon) < 0); + lastDelay = delay; + } + } + + @Test + public void testDelayStateDerivatives() { + + // Geodetic point + final double latitude = FastMath.toRadians(45.0); + final double longitude = FastMath.toRadians(45.0); + final double height = 0.0; + final GeodeticPoint point = new GeodeticPoint(latitude, longitude, height); + // Body: earth + final OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, + Constants.WGS84_EARTH_FLATTENING, + FramesFactory.getITRF(IERSConventions.IERS_2010, true)); + // Topocentric frame + final TopocentricFrame baseFrame = new TopocentricFrame(earth, point, "topo"); + + // Station + final GroundStation station = new GroundStation(baseFrame, + TroposphericModelUtils.STANDARD_ATMOSPHERE_PROVIDER); + + // Tropospheric model + final TroposphericModel model = new ViennaOne(new ConstantViennaAProvider(new ViennaACoefficients(0.00127683, 0.00060955)), + new ConstantAzimuthalGradientProvider(null), + new ConstantTroposphericModel(new TroposphericDelay(2.0966, 0.2140, 0, 0)), + TimeScalesFactory.getUTC()); + + // Derivative Structure + final DSFactory factory = new DSFactory(6, 1); + final DerivativeStructure a0 = factory.variable(0, 24464560.0); + final DerivativeStructure e0 = factory.variable(1, 0.05); + final DerivativeStructure i0 = factory.variable(2, 0.122138); + final DerivativeStructure pa0 = factory.variable(3, 3.10686); + final DerivativeStructure raan0 = factory.variable(4, 1.00681); + final DerivativeStructure anomaly0 = factory.variable(5, 0.048363); + final Field field = a0.getField(); + final DerivativeStructure zero = field.getZero(); + + // Field Date + final FieldAbsoluteDate dsDate = new FieldAbsoluteDate<>(field, 2018, 11, 19, 18, 0, 0.0, + TimeScalesFactory.getUTC()); + // Field Orbit + final Frame frame = FramesFactory.getEME2000(); + final FieldOrbit dsOrbit = new FieldKeplerianOrbit<>(a0, e0, i0, pa0, raan0, anomaly0, + PositionAngleType.MEAN, frame, + dsDate, zero.add(3.9860047e14)); + // Field State + final FieldSpacecraftState dsState = new FieldSpacecraftState<>(dsOrbit); + + // Initial satellite elevation + final FieldVector3D position = dsState.getPosition(); + final FieldTrackingCoordinates dsTrackingCoordinates = + baseFrame.getTrackingCoordinates(position, frame, dsDate); + + // Compute delay state derivatives + final FieldGeodeticPoint dsPoint = new FieldGeodeticPoint<>(zero.add(latitude), zero.add(longitude), zero.add(height)); + final DerivativeStructure delay = model.pathDelay(dsTrackingCoordinates, dsPoint, + new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + model.getParameters(field), dsDate).getDelay(); + + final double[] compDelay = delay.getAllDerivatives(); + + // Field -> non-field + final Orbit orbit = dsOrbit.toOrbit(); + final SpacecraftState state = dsState.toSpacecraftState(); + + // Finite differences for reference values + final double[][] refDeriv = new double[1][6]; + final OrbitType orbitType = OrbitType.KEPLERIAN; + final PositionAngleType angleType = PositionAngleType.MEAN; + double dP = 0.001; + double[] steps = NumericalPropagator.tolerances(1000000 * dP, orbit, orbitType)[0]; + for (int i = 0; i < 6; i++) { + SpacecraftState stateM4 = shiftState(state, orbitType, angleType, -4 * steps[i], i); + final Vector3D positionM4 = stateM4.getPosition(); + final TrackingCoordinates trackingCoordinatesM4 = station.getBaseFrame(). + getTrackingCoordinates(positionM4, stateM4.getFrame(), stateM4.getDate()); + double delayM4 = model.pathDelay(trackingCoordinatesM4, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateM4.getDate()).getDelay(); + + SpacecraftState stateM3 = shiftState(state, orbitType, angleType, -3 * steps[i], i); + final Vector3D positionM3 = stateM3.getPosition(); + final TrackingCoordinates trackingCoordinatesM3 = station.getBaseFrame(). + getTrackingCoordinates(positionM3, stateM3.getFrame(), stateM3.getDate()); + double delayM3 = model.pathDelay(trackingCoordinatesM3, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateM3.getDate()).getDelay(); + + SpacecraftState stateM2 = shiftState(state, orbitType, angleType, -2 * steps[i], i); + final Vector3D positionM2 = stateM2.getPosition(); + final TrackingCoordinates trackingCoordinatesM2 = station.getBaseFrame(). + getTrackingCoordinates(positionM2, stateM2.getFrame(), stateM2.getDate()); + double delayM2 = model.pathDelay(trackingCoordinatesM2, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateM2.getDate()).getDelay(); + + SpacecraftState stateM1 = shiftState(state, orbitType, angleType, -1 * steps[i], i); + final Vector3D positionM1 = stateM1.getPosition(); + final TrackingCoordinates trackingCoordinatesM1 = station.getBaseFrame(). + getTrackingCoordinates(positionM1, stateM1.getFrame(), stateM1.getDate()); + double delayM1 = model.pathDelay(trackingCoordinatesM1, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateM1.getDate()).getDelay(); + + SpacecraftState stateP1 = shiftState(state, orbitType, angleType, 1 * steps[i], i); + final Vector3D positionP1 = stateP1.getPosition(); + final TrackingCoordinates trackingCoordinatesP1 = station.getBaseFrame(). + getTrackingCoordinates(positionP1, stateP1.getFrame(), stateP1.getDate()); + double delayP1 = model.pathDelay(trackingCoordinatesP1, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateP1.getDate()).getDelay(); + + SpacecraftState stateP2 = shiftState(state, orbitType, angleType, 2 * steps[i], i); + final Vector3D positionP2 = stateP2.getPosition(); + final TrackingCoordinates trackingCoordinatesP2 = station.getBaseFrame(). + getTrackingCoordinates(positionP2, stateP2.getFrame(), stateP2.getDate()); + double delayP2 = model.pathDelay(trackingCoordinatesP2, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateP2.getDate()).getDelay(); + + SpacecraftState stateP3 = shiftState(state, orbitType, angleType, 3 * steps[i], i); + final Vector3D positionP3 = stateP3.getPosition(); + final TrackingCoordinates trackingCoordinatesP3 = station.getBaseFrame(). + getTrackingCoordinates(positionP3, stateP3.getFrame(), stateP3.getDate()); + double delayP3 = model.pathDelay(trackingCoordinatesP3, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateP3.getDate()).getDelay(); + + SpacecraftState stateP4 = shiftState(state, orbitType, angleType, 4 * steps[i], i); + final Vector3D positionP4 = stateP4.getPosition(); + final TrackingCoordinates trackingCoordinatesP4 = station.getBaseFrame(). + getTrackingCoordinates(positionP4, stateP4.getFrame(), stateP4.getDate()); + double delayP4 = model.pathDelay(trackingCoordinatesP4, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateP4.getDate()).getDelay(); + + fillJacobianColumn(refDeriv, i, orbitType, angleType, steps[i], + delayM4, delayM3, delayM2, delayM1, + delayP1, delayP2, delayP3, delayP4); + } + + for (int i = 0; i < 6; i++) { + Assertions.assertEquals(compDelay[i + 1], refDeriv[0][i], 3.0e-11); + } + } + + private void fillJacobianColumn(double[][] jacobian, int column, + OrbitType orbitType, PositionAngleType angleType, double h, + double sM4h, double sM3h, + double sM2h, double sM1h, + double sP1h, double sP2h, + double sP3h, double sP4h) { + for (int i = 0; i < jacobian.length; ++i) { + jacobian[i][column] = ( -3 * (sP4h - sM4h) + + 32 * (sP3h - sM3h) - + 168 * (sP2h - sM2h) + + 672 * (sP1h - sM1h)) / (840 * h); + } + } + + private SpacecraftState shiftState(SpacecraftState state, OrbitType orbitType, PositionAngleType angleType, + double delta, int column) { + + double[][] array = stateToArray(state, orbitType, angleType, true); + array[0][column] += delta; + + return arrayToState(array, orbitType, angleType, state.getFrame(), state.getDate(), + state.getMu(), state.getAttitude()); + + } + + private double[][] stateToArray(SpacecraftState state, OrbitType orbitType, PositionAngleType angleType, + boolean withMass) { + double[][] array = new double[2][withMass ? 7 : 6]; + orbitType.mapOrbitToArray(state.getOrbit(), angleType, array[0], array[1]); + if (withMass) { + array[0][6] = state.getMass(); + } + return array; + } + + private SpacecraftState arrayToState(double[][] array, OrbitType orbitType, PositionAngleType angleType, + Frame frame, AbsoluteDate date, double mu, + Attitude attitude) { + Orbit orbit = orbitType.mapArrayToOrbit(array[0], array[1], angleType, date, mu, frame); + return (array.length > 6) ? + new SpacecraftState(orbit, attitude) : + new SpacecraftState(orbit, attitude, array[0][6]); + } + +} diff --git a/src/test/java/org/orekit/models/earth/troposphere/FieldViennaThreeModelTest.java b/src/test/java/org/orekit/models/earth/troposphere/FieldViennaThreeModelTest.java index 5566ee9308..6b5ac24062 100644 --- a/src/test/java/org/orekit/models/earth/troposphere/FieldViennaThreeModelTest.java +++ b/src/test/java/org/orekit/models/earth/troposphere/FieldViennaThreeModelTest.java @@ -39,6 +39,7 @@ import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; import org.orekit.frames.TopocentricFrame; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; import org.orekit.orbits.FieldKeplerianOrbit; import org.orekit.orbits.FieldOrbit; import org.orekit.orbits.Orbit; @@ -51,8 +52,11 @@ import org.orekit.time.FieldAbsoluteDate; import org.orekit.time.TimeScalesFactory; import org.orekit.utils.Constants; +import org.orekit.utils.FieldTrackingCoordinates; import org.orekit.utils.IERSConventions; +import org.orekit.utils.TrackingCoordinates; +@Deprecated public class FieldViennaThreeModelTest { private static double epsilon = 1e-6; @@ -102,7 +106,8 @@ private > void doTestMappingFactors(final Fiel final double longitude = FastMath.toRadians(277.5); final double height = 824.0; - final double elevation = FastMath.toRadians(38.0); + final FieldTrackingCoordinates trackingCoordinates = + new FieldTrackingCoordinates(zero, zero.newInstance(FastMath.toRadians(38.0)), zero); final double expectedHydro = 1.621024; final double expectedWet = 1.623023; @@ -113,7 +118,9 @@ private > void doTestMappingFactors(final Fiel final ViennaThreeModel model = new ViennaThreeModel(a, z); - final T[] computedMapping = model.mappingFactors(zero.add(elevation), point, + final T[] computedMapping = model.mappingFactors(trackingCoordinates, point, + new FieldPressureTemperatureHumidity<>(field, + TroposphericModelUtils.STANDARD_ATMOSPHERE), date); Assertions.assertEquals(expectedHydro, computedMapping[0].getReal(), epsilon); @@ -166,7 +173,10 @@ private > void doTestLowElevation(final Field< final ViennaThreeModel model = new ViennaThreeModel(a, z); - final T[] computedMapping = model.mappingFactors(zero.add(elevation), point, + final T[] computedMapping = model.mappingFactors(new FieldTrackingCoordinates(zero, zero.newInstance(elevation), zero), + point, + new FieldPressureTemperatureHumidity<>(field, + TroposphericModelUtils.STANDARD_ATMOSPHERE), date); Assertions.assertEquals(expectedHydro, computedMapping[0].getReal(), epsilon); @@ -219,7 +229,10 @@ private > void doTestHightElevation(final Fiel final ViennaThreeModel model = new ViennaThreeModel(a, z); - final T[] computedMapping = model.mappingFactors(zero.add(elevation), point, + final T[] computedMapping = model.mappingFactors(new FieldTrackingCoordinates(zero, zero.newInstance(elevation), zero), + point, + new FieldPressureTemperatureHumidity<>(field, + TroposphericModelUtils.STANDARD_ATMOSPHERE), date); Assertions.assertEquals(expectedHydro, computedMapping[0].getReal(), epsilon); @@ -240,10 +253,15 @@ private > void doTestDelay(final Field fiel final double[] z = {2.1993, 0.0690}; final FieldGeodeticPoint point = new FieldGeodeticPoint<>(zero.add(FastMath.toRadians(37.5)), zero.add(FastMath.toRadians(277.5)), zero.add(height)); ViennaThreeModel model = new ViennaThreeModel(a, z); - final T path = model.pathDelay(zero.add(FastMath.toRadians(elevation)), point, - model.getParameters(field), date); - Assertions.assertTrue(Precision.compareTo(path.getReal(), 20d, epsilon) < 0); - Assertions.assertTrue(Precision.compareTo(path.getReal(), 0d, epsilon) > 0); + final FieldTroposphericDelay delay = model.pathDelay(new FieldTrackingCoordinates(zero, zero.newInstance(FastMath.toRadians(elevation)), zero), + point, + new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + model.getParameters(field), date); + Assertions.assertEquals( 2.1993, delay.getZh().getReal(), 1.0e-4); + Assertions.assertEquals( 0.069, delay.getZw().getReal(), 1.0e-4); + Assertions.assertEquals(12.2124, delay.getSh().getReal(), 1.0e-4); + Assertions.assertEquals( 0.3916, delay.getSw().getReal(), 1.0e-4); + Assertions.assertEquals(12.6041, delay.getDelay().getReal(), 1.0e-4); } @Test @@ -261,8 +279,10 @@ private > void doTestFixedHeight(final Field(zero, zero.newInstance(FastMath.toRadians(elev)), zero), + point, + new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + model.getParameters(field), date).getDelay(); Assertions.assertTrue(Precision.compareTo(delay.getReal(), lastDelay.getReal(), epsilon) < 0); lastDelay = delay; } @@ -284,12 +304,13 @@ public void testDelayStateDerivatives() { final TopocentricFrame baseFrame = new TopocentricFrame(earth, point, "topo"); // Station - final GroundStation station = new GroundStation(baseFrame); + final GroundStation station = new GroundStation(baseFrame, + TroposphericModelUtils.STANDARD_ATMOSPHERE_PROVIDER); // Tropospheric model final double[] a = { 0.00127683, 0.00060955 }; final double[] z = {2.0966, 0.2140}; - final DiscreteTroposphericModel model = new ViennaThreeModel(a, z); + final TroposphericModel model = new ViennaThreeModel(a, z); // Derivative Structure final DSFactory factory = new DSFactory(6, 1); @@ -315,11 +336,14 @@ public void testDelayStateDerivatives() { // Initial satellite elevation final FieldVector3D position = dsState.getPosition(); - final DerivativeStructure dsElevation = baseFrame.getTrackingCoordinates(position, frame, dsDate).getElevation(); + final FieldTrackingCoordinates dsTrackingCoordinates = + baseFrame.getTrackingCoordinates(position, frame, dsDate); // Compute delay state derivatives final FieldGeodeticPoint dsPoint = new FieldGeodeticPoint<>(zero.add(latitude), zero.add(longitude), zero.add(height)); - final DerivativeStructure delay = model.pathDelay(dsElevation, dsPoint, model.getParameters(field), dsDate); + final DerivativeStructure delay = model.pathDelay(dsTrackingCoordinates, dsPoint, + new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + model.getParameters(field), dsDate).getDelay(); final double[] compDelay = delay.getAllDerivatives(); @@ -336,59 +360,59 @@ public void testDelayStateDerivatives() { for (int i = 0; i < 6; i++) { SpacecraftState stateM4 = shiftState(state, orbitType, angleType, -4 * steps[i], i); final Vector3D positionM4 = stateM4.getPosition(); - final double elevationM4 = station.getBaseFrame(). - getTrackingCoordinates(positionM4, stateM4.getFrame(), stateM4.getDate()). - getElevation(); - double delayM4 = model.pathDelay(elevationM4, point, model.getParameters(), stateM4.getDate()); + final TrackingCoordinates trackingCoordinatesM4 = station.getBaseFrame(). + getTrackingCoordinates(positionM4, stateM4.getFrame(), stateM4.getDate()); + double delayM4 = model.pathDelay(trackingCoordinatesM4, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateM4.getDate()).getDelay(); SpacecraftState stateM3 = shiftState(state, orbitType, angleType, -3 * steps[i], i); final Vector3D positionM3 = stateM3.getPosition(); - final double elevationM3 = station.getBaseFrame(). - getTrackingCoordinates(positionM3, stateM3.getFrame(), stateM3.getDate()). - getElevation(); - double delayM3 = model.pathDelay(elevationM3, point, model.getParameters(), stateM3.getDate()); + final TrackingCoordinates trackingCoordinatesM3 = station.getBaseFrame(). + getTrackingCoordinates(positionM3, stateM3.getFrame(), stateM3.getDate()); + double delayM3 = model.pathDelay(trackingCoordinatesM3, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateM3.getDate()).getDelay(); SpacecraftState stateM2 = shiftState(state, orbitType, angleType, -2 * steps[i], i); final Vector3D positionM2 = stateM2.getPosition(); - final double elevationM2 = station.getBaseFrame(). - getTrackingCoordinates(positionM2, stateM2.getFrame(), stateM2.getDate()). - getElevation(); - double delayM2 = model.pathDelay(elevationM2, point, model.getParameters(), stateM2.getDate()); + final TrackingCoordinates trackingCoordinatesM2 = station.getBaseFrame(). + getTrackingCoordinates(positionM2, stateM2.getFrame(), stateM2.getDate()); + double delayM2 = model.pathDelay(trackingCoordinatesM2, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateM2.getDate()).getDelay(); SpacecraftState stateM1 = shiftState(state, orbitType, angleType, -1 * steps[i], i); final Vector3D positionM1 = stateM1.getPosition(); - final double elevationM1 = station.getBaseFrame(). - getTrackingCoordinates(positionM1, stateM1.getFrame(), stateM1.getDate()). - getElevation(); - double delayM1 = model.pathDelay(elevationM1, point, model.getParameters(), stateM1.getDate()); + final TrackingCoordinates trackingCoordinatesM1 = station.getBaseFrame(). + getTrackingCoordinates(positionM1, stateM1.getFrame(), stateM1.getDate()); + double delayM1 = model.pathDelay(trackingCoordinatesM1, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateM1.getDate()).getDelay(); SpacecraftState stateP1 = shiftState(state, orbitType, angleType, 1 * steps[i], i); final Vector3D positionP1 = stateP1.getPosition(); - final double elevationP1 = station.getBaseFrame(). - getTrackingCoordinates(positionP1, stateP1.getFrame(), stateP1.getDate()). - getElevation(); - double delayP1 = model.pathDelay(elevationP1, point, model.getParameters(), stateP1.getDate()); + final TrackingCoordinates trackingCoordinatesP1 = station.getBaseFrame(). + getTrackingCoordinates(positionP1, stateP1.getFrame(), stateP1.getDate()); + double delayP1 = model.pathDelay(trackingCoordinatesP1, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateP1.getDate()).getDelay(); SpacecraftState stateP2 = shiftState(state, orbitType, angleType, 2 * steps[i], i); final Vector3D positionP2 = stateP2.getPosition(); - final double elevationP2 = station.getBaseFrame(). - getTrackingCoordinates(positionP2, stateP2.getFrame(), stateP2.getDate()). - getElevation(); - double delayP2 = model.pathDelay(elevationP2, point, model.getParameters(), stateP2.getDate()); + final TrackingCoordinates trackingCoordinatesP2 = station.getBaseFrame(). + getTrackingCoordinates(positionP2, stateP2.getFrame(), stateP2.getDate()); + double delayP2 = model.pathDelay(trackingCoordinatesP2, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateP2.getDate()).getDelay(); SpacecraftState stateP3 = shiftState(state, orbitType, angleType, 3 * steps[i], i); final Vector3D positionP3 = stateP3.getPosition(); - final double elevationP3 = station.getBaseFrame(). - getTrackingCoordinates(positionP3, stateP3.getFrame(), stateP3.getDate()). - getElevation(); - double delayP3 = model.pathDelay(elevationP3, point, model.getParameters(), stateP3.getDate()); + final TrackingCoordinates trackingCoordinatesP3 = station.getBaseFrame(). + getTrackingCoordinates(positionP3, stateP3.getFrame(), stateP3.getDate()); + double delayP3 = model.pathDelay(trackingCoordinatesP3, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateP3.getDate()).getDelay(); SpacecraftState stateP4 = shiftState(state, orbitType, angleType, 4 * steps[i], i); final Vector3D positionP4 = stateP4.getPosition(); - final double elevationP4 = station.getBaseFrame(). - getTrackingCoordinates(positionP4, stateP4.getFrame(), stateP4.getDate()). - getElevation(); - double delayP4 = model.pathDelay(elevationP4, point, model.getParameters(), stateP4.getDate()); + final TrackingCoordinates trackingCoordinatesP4 = station.getBaseFrame(). + getTrackingCoordinates(positionP4, stateP4.getFrame(), stateP4.getDate()); + double delayP4 = model.pathDelay(trackingCoordinatesP4, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateP4.getDate()).getDelay(); fillJacobianColumn(refDeriv, i, orbitType, angleType, steps[i], delayM4, delayM3, delayM2, delayM1, diff --git a/src/test/java/org/orekit/models/earth/troposphere/FieldViennaThreeTest.java b/src/test/java/org/orekit/models/earth/troposphere/FieldViennaThreeTest.java new file mode 100644 index 0000000000..e25ead53b4 --- /dev/null +++ b/src/test/java/org/orekit/models/earth/troposphere/FieldViennaThreeTest.java @@ -0,0 +1,503 @@ +/* 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.models.earth.troposphere; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.analysis.differentiation.DSFactory; +import org.hipparchus.analysis.differentiation.DerivativeStructure; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.util.Binary64Field; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.Precision; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.orekit.Utils; +import org.orekit.attitudes.Attitude; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.bodies.OneAxisEllipsoid; +import org.orekit.errors.OrekitException; +import org.orekit.estimation.measurements.GroundStation; +import org.orekit.frames.Frame; +import org.orekit.frames.FramesFactory; +import org.orekit.frames.TopocentricFrame; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; +import org.orekit.orbits.FieldKeplerianOrbit; +import org.orekit.orbits.FieldOrbit; +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.numerical.NumericalPropagator; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.TimeScalesFactory; +import org.orekit.utils.Constants; +import org.orekit.utils.FieldTrackingCoordinates; +import org.orekit.utils.IERSConventions; +import org.orekit.utils.TrackingCoordinates; + +public class FieldViennaThreeTest { + + private static double epsilon = 1e-6; + + @BeforeAll + public static void setUpGlobal() { + Utils.setDataRoot("atmosphere"); + } + + @BeforeEach + public void setUp() throws OrekitException { + Utils.setDataRoot("regular-data:potential/shm-format"); + } + + @Test + public void testMappingFactors() { + doTestMappingFactors(Binary64Field.getInstance()); + } + + private > void doTestMappingFactors(final Field field) { + + final T zero = field.getZero(); + + // Site: latitude: 37.5° + // longitude: 277.5° + // height: 824 m + // + // Date: 25 November 2018 at 0h UT + // + // Values: ah = 0.00123462 + // aw = 0.00047101 + // zhd = 2.1993 m + // zwd = 0.0690 m + // + // Values taken from: http://vmf.geo.tuwien.ac.at/trop_products/GRID/5x5/VMF3/VMF3_OP/2018/VMF3_20181125.H00 + // + // Expected mapping factors : hydrostatic -> 1.621024 + // wet -> 1.623023 + // + // Expected outputs are obtained by performing the Matlab script vmf3.m provided by TU WIEN: + // http://vmf.geo.tuwien.ac.at/codes/ + // + + final FieldAbsoluteDate date = new FieldAbsoluteDate<>(field, 2018, 11, 25, TimeScalesFactory.getUTC()); + + final double latitude = FastMath.toRadians(37.5); + final double longitude = FastMath.toRadians(277.5); + final double height = 824.0; + + final FieldTrackingCoordinates trackingCoordinates = + new FieldTrackingCoordinates(zero, zero.newInstance(FastMath.toRadians(38.0)), zero); + final double expectedHydro = 1.621024; + final double expectedWet = 1.623023; + + final FieldGeodeticPoint point = new FieldGeodeticPoint<>(zero.add(latitude), zero.add(longitude), zero.add(height)); + + final ViennaThree model = new ViennaThree(new ConstantViennaAProvider(new ViennaACoefficients(0.00123462, 0.00047101)), + new ConstantAzimuthalGradientProvider(null), + new ConstantTroposphericModel(new TroposphericDelay(2.1993, 0.0690, 0, 0)), + TimeScalesFactory.getUTC()); + + final T[] computedMapping = model.mappingFactors(trackingCoordinates, point, + new FieldPressureTemperatureHumidity<>(field, + TroposphericModelUtils.STANDARD_ATMOSPHERE), + date); + + Assertions.assertEquals(expectedHydro, computedMapping[0].getReal(), epsilon); + Assertions.assertEquals(expectedWet, computedMapping[1].getReal(), epsilon); + } + + @Test + public void testLowElevation() { + doTestLowElevation(Binary64Field.getInstance()); + } + + private > void doTestLowElevation(final Field field) { + + final T zero = field.getZero(); + + // Site: latitude: 37.5° + // longitude: 277.5° + // height: 824 m + // + // Date: 25 November 2018 at 0h UT + // + // Values: ah = 0.00123462 + // aw = 0.00047101 + // zhd = 2.1993 m + // zwd = 0.0690 m + // + // Values taken from: http://vmf.geo.tuwien.ac.at/trop_products/GRID/5x5/VMF3/VMF3_OP/2018/VMF3_20181125.H00 + // + // Expected mapping factors : hydrostatic -> 10.132802 + // wet -> 10.879154 + // + // Expected outputs are obtained by performing the Matlab script vmf3.m provided by TU WIEN: + // http://vmf.geo.tuwien.ac.at/codes/ + // + + final FieldAbsoluteDate date = new FieldAbsoluteDate<>(field, 2018, 11, 25, TimeScalesFactory.getUTC()); + + final double latitude = FastMath.toRadians(37.5); + final double longitude = FastMath.toRadians(277.5); + final double height = 824.0; + + final double elevation = FastMath.toRadians(5.0); + final double expectedHydro = 10.132802; + final double expectedWet = 10.879154; + + final FieldGeodeticPoint point = new FieldGeodeticPoint<>(zero.add(latitude), zero.add(longitude), zero.add(height)); + + final ViennaThree model = new ViennaThree(new ConstantViennaAProvider(new ViennaACoefficients(0.00123462, 0.00047101)), + new ConstantAzimuthalGradientProvider(null), + new ConstantTroposphericModel(new TroposphericDelay(2.1993, 0.0690, 0, 0)), + TimeScalesFactory.getUTC()); + + final T[] computedMapping = model.mappingFactors(new FieldTrackingCoordinates(zero, zero.newInstance(elevation), zero), + point, + new FieldPressureTemperatureHumidity<>(field, + TroposphericModelUtils.STANDARD_ATMOSPHERE), + date); + + Assertions.assertEquals(expectedHydro, computedMapping[0].getReal(), epsilon); + Assertions.assertEquals(expectedWet, computedMapping[1].getReal(), epsilon); + } + + @Test + public void testHightElevation() { + doTestHightElevation(Binary64Field.getInstance()); + } + + private > void doTestHightElevation(final Field field) { + + final T zero = field.getZero(); + + // Site: latitude: 37.5° + // longitude: 277.5° + // height: 824 m + // + // Date: 25 November 2018 at 0h UT + // + // Values: ah = 0.00123462 + // aw = 0.00047101 + // zhd = 2.1993 m + // zwd = 0.0690 m + // + // Values taken from: http://vmf.geo.tuwien.ac.at/trop_products/GRID/5x5/VMF3/VMF3_OP/2018/VMF3_20181125.H00 + // + // Expected mapping factors : hydrostatic -> 1.003810 + // wet -> 1.003816 + // + // Expected outputs are obtained by performing the Matlab script vmf3.m provided by TU WIEN: + // http://vmf.geo.tuwien.ac.at/codes/ + // + + final FieldAbsoluteDate date = new FieldAbsoluteDate<>(field, 2018, 11, 25, TimeScalesFactory.getUTC()); + + final double latitude = FastMath.toRadians(37.5); + final double longitude = FastMath.toRadians(277.5); + final double height = 824.0; + + final double elevation = FastMath.toRadians(85.0); + final double expectedHydro = 1.003810; + final double expectedWet = 1.003816; + + final FieldGeodeticPoint point = new FieldGeodeticPoint<>(zero.add(latitude), zero.add(longitude), zero.add(height)); + + final ViennaThree model = new ViennaThree(new ConstantViennaAProvider(new ViennaACoefficients(0.00123462, 0.00047101)), + new ConstantAzimuthalGradientProvider(null), + new ConstantTroposphericModel(new TroposphericDelay(2.1993, 0.0690, 0, 0)), + TimeScalesFactory.getUTC()); + + final T[] computedMapping = model.mappingFactors(new FieldTrackingCoordinates(zero, zero.newInstance(elevation), zero), + point, + new FieldPressureTemperatureHumidity<>(field, + TroposphericModelUtils.STANDARD_ATMOSPHERE), + date); + + Assertions.assertEquals(expectedHydro, computedMapping[0].getReal(), epsilon); + Assertions.assertEquals(expectedWet, computedMapping[1].getReal(), epsilon); + } + + @Test + public void testDelay() { + doTestDelay(Binary64Field.getInstance()); + } + + private > void doTestDelay(final Field field) { + final T zero = field.getZero(); + final double elevation = 10d; + final double height = 100d; + final FieldAbsoluteDate date = new FieldAbsoluteDate<>(field); + final FieldGeodeticPoint point = new FieldGeodeticPoint<>(zero.add(FastMath.toRadians(37.5)), zero.add(FastMath.toRadians(277.5)), zero.add(height)); + ViennaThree model = new ViennaThree(new ConstantViennaAProvider(new ViennaACoefficients(0.00123462, 0.00047101)), + new ConstantAzimuthalGradientProvider(null), + new ConstantTroposphericModel(new TroposphericDelay(2.1993, 0.0690, 0, 0)), + TimeScalesFactory.getUTC()); + final FieldTroposphericDelay delay = model.pathDelay(new FieldTrackingCoordinates<>(zero, zero.newInstance(FastMath.toRadians(elevation)), zero), + point, + new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + model.getParameters(field), date); + Assertions.assertEquals( 2.1993, delay.getZh().getReal(), 1.0e-4); + Assertions.assertEquals( 0.069, delay.getZw().getReal(), 1.0e-4); + Assertions.assertEquals(12.2124, delay.getSh().getReal(), 1.0e-4); + Assertions.assertEquals( 0.3916, delay.getSw().getReal(), 1.0e-4); + Assertions.assertEquals(12.6041, delay.getDelay().getReal(), 1.0e-4); + } + + @Test + public void testDelayWithAzimuthalAsymmetry() { + doTestDelayWithAzimuthalAsymmetry(Binary64Field.getInstance()); + } + + private > void doTestDelayWithAzimuthalAsymmetry(final Field field) { + final T zero = field.getZero(); + final double azimuth = 30.0; + final double elevation = 10.0; + final double height = 100.0; + final FieldAbsoluteDate date = new FieldAbsoluteDate<>(field); + final FieldGeodeticPoint point = new FieldGeodeticPoint<>(zero.add(FastMath.toRadians(37.5)), zero.add(FastMath.toRadians(277.5)), zero.add(height)); + ViennaThree model = new ViennaThree(new ConstantViennaAProvider(new ViennaACoefficients(0.00123462, 0.00047101)), + new ConstantAzimuthalGradientProvider(new AzimuthalGradientCoefficients(12.0, 4.5, + 0.8, 1.25)), + new ConstantTroposphericModel(new TroposphericDelay(2.1993, 0.0690, 0, 0)), + TimeScalesFactory.getUTC()); + final FieldTroposphericDelay delay = model.pathDelay(new FieldTrackingCoordinates<>(zero.newInstance(FastMath.toRadians(azimuth)), + zero.newInstance(FastMath.toRadians(elevation)), + zero), + point, + new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + model.getParameters(field), date); + Assertions.assertEquals( 2.1993, delay.getZh().getReal(), 1.0e-4); + Assertions.assertEquals( 0.069, delay.getZw().getReal(), 1.0e-4); + Assertions.assertEquals(12.2124 + 373.8241, delay.getSh().getReal(), 1.0e-4); // second term is due to azimuthal gradient + Assertions.assertEquals( 0.3916 + 38.9670, delay.getSw().getReal(), 1.0e-4); // second term is due to azimuthal gradient + Assertions.assertEquals(12.6041 + 373.8241 + 38.9670, delay.getDelay().getReal(), 1.0e-4); + } + + @Test + public void testFixedHeight() { + doTestFixedHeight(Binary64Field.getInstance()); + } + + private > void doTestFixedHeight(final Field field) { + final T zero = field.getZero(); + final FieldAbsoluteDate date = new FieldAbsoluteDate<>(field); + final FieldGeodeticPoint point = new FieldGeodeticPoint<>(zero.add(FastMath.toRadians(37.5)), zero.add(FastMath.toRadians(277.5)), zero.add(350.0)); + ViennaThree model = new ViennaThree(new ConstantViennaAProvider(new ViennaACoefficients(0.00123462, 0.00047101)), + new ConstantAzimuthalGradientProvider(null), + new ConstantTroposphericModel(new TroposphericDelay(2.1993, 0.0690, 0, 0)), + TimeScalesFactory.getUTC()); + T lastDelay = zero.add(Double.MAX_VALUE); + // delay shall decline with increasing elevation angle + for (double elev = 10d; elev < 90d; elev += 8d) { + final T delay = model.pathDelay(new FieldTrackingCoordinates(zero, zero.newInstance(FastMath.toRadians(elev)), zero), + point, + new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + model.getParameters(field), date).getDelay(); + Assertions.assertTrue(Precision.compareTo(delay.getReal(), lastDelay.getReal(), epsilon) < 0); + lastDelay = delay; + } + } + + @Test + public void testDelayStateDerivatives() { + + // Geodetic point + final double latitude = FastMath.toRadians(45.0); + final double longitude = FastMath.toRadians(45.0); + final double height = 0.0; + final GeodeticPoint point = new GeodeticPoint(latitude, longitude, height); + // Body: earth + final OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, + Constants.WGS84_EARTH_FLATTENING, + FramesFactory.getITRF(IERSConventions.IERS_2010, true)); + // Topocentric frame + final TopocentricFrame baseFrame = new TopocentricFrame(earth, point, "topo"); + + // Station + final GroundStation station = new GroundStation(baseFrame, + TroposphericModelUtils.STANDARD_ATMOSPHERE_PROVIDER); + + // Tropospheric model + final TroposphericModel model = new ViennaThree(new ConstantViennaAProvider(new ViennaACoefficients(00.00127683, 0.00060955)), + new ConstantAzimuthalGradientProvider(null), + new ConstantTroposphericModel(new TroposphericDelay(2.0966, 0.2140, 0, 0)), + TimeScalesFactory.getUTC()); + + // Derivative Structure + final DSFactory factory = new DSFactory(6, 1); + final DerivativeStructure a0 = factory.variable(0, 24464560.0); + final DerivativeStructure e0 = factory.variable(1, 0.05); + final DerivativeStructure i0 = factory.variable(2, 0.122138); + final DerivativeStructure pa0 = factory.variable(3, 3.10686); + final DerivativeStructure raan0 = factory.variable(4, 1.00681); + final DerivativeStructure anomaly0 = factory.variable(5, 0.048363); + final Field field = a0.getField(); + final DerivativeStructure zero = field.getZero(); + + // Field Date + final FieldAbsoluteDate dsDate = new FieldAbsoluteDate<>(field, 2018, 11, 19, 18, 0, 0.0, + TimeScalesFactory.getUTC()); + // Field Orbit + final Frame frame = FramesFactory.getEME2000(); + final FieldOrbit dsOrbit = new FieldKeplerianOrbit<>(a0, e0, i0, pa0, raan0, anomaly0, + PositionAngleType.MEAN, frame, + dsDate, zero.add(3.9860047e14)); + // Field State + final FieldSpacecraftState dsState = new FieldSpacecraftState<>(dsOrbit); + + // Initial satellite elevation + final FieldVector3D position = dsState.getPosition(); + final FieldTrackingCoordinates dsTrackingCoordinates = + baseFrame.getTrackingCoordinates(position, frame, dsDate); + + // Compute delay state derivatives + final FieldGeodeticPoint dsPoint = new FieldGeodeticPoint<>(zero.add(latitude), zero.add(longitude), zero.add(height)); + final DerivativeStructure delay = model.pathDelay(dsTrackingCoordinates, dsPoint, + new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + model.getParameters(field), dsDate).getDelay(); + + final double[] compDelay = delay.getAllDerivatives(); + + // Field -> non-field + final Orbit orbit = dsOrbit.toOrbit(); + final SpacecraftState state = dsState.toSpacecraftState(); + + // Finite differences for reference values + final double[][] refDeriv = new double[1][6]; + final OrbitType orbitType = OrbitType.KEPLERIAN; + final PositionAngleType angleType = PositionAngleType.MEAN; + double dP = 0.001; + double[] steps = NumericalPropagator.tolerances(1000000 * dP, orbit, orbitType)[0]; + for (int i = 0; i < 6; i++) { + SpacecraftState stateM4 = shiftState(state, orbitType, angleType, -4 * steps[i], i); + final Vector3D positionM4 = stateM4.getPosition(); + final TrackingCoordinates trackingCoordinatesM4 = station.getBaseFrame(). + getTrackingCoordinates(positionM4, stateM4.getFrame(), stateM4.getDate()); + double delayM4 = model.pathDelay(trackingCoordinatesM4, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateM4.getDate()).getDelay(); + + SpacecraftState stateM3 = shiftState(state, orbitType, angleType, -3 * steps[i], i); + final Vector3D positionM3 = stateM3.getPosition(); + final TrackingCoordinates trackingCoordinatesM3 = station.getBaseFrame(). + getTrackingCoordinates(positionM3, stateM3.getFrame(), stateM3.getDate()); + double delayM3 = model.pathDelay(trackingCoordinatesM3, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateM3.getDate()).getDelay(); + + SpacecraftState stateM2 = shiftState(state, orbitType, angleType, -2 * steps[i], i); + final Vector3D positionM2 = stateM2.getPosition(); + final TrackingCoordinates trackingCoordinatesM2 = station.getBaseFrame(). + getTrackingCoordinates(positionM2, stateM2.getFrame(), stateM2.getDate()); + double delayM2 = model.pathDelay(trackingCoordinatesM2, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateM2.getDate()).getDelay(); + + SpacecraftState stateM1 = shiftState(state, orbitType, angleType, -1 * steps[i], i); + final Vector3D positionM1 = stateM1.getPosition(); + final TrackingCoordinates trackingCoordinatesM1 = station.getBaseFrame(). + getTrackingCoordinates(positionM1, stateM1.getFrame(), stateM1.getDate()); + double delayM1 = model.pathDelay(trackingCoordinatesM1, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateM1.getDate()).getDelay(); + + SpacecraftState stateP1 = shiftState(state, orbitType, angleType, 1 * steps[i], i); + final Vector3D positionP1 = stateP1.getPosition(); + final TrackingCoordinates trackingCoordinatesP1 = station.getBaseFrame(). + getTrackingCoordinates(positionP1, stateP1.getFrame(), stateP1.getDate()); + double delayP1 = model.pathDelay(trackingCoordinatesP1, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateP1.getDate()).getDelay(); + + SpacecraftState stateP2 = shiftState(state, orbitType, angleType, 2 * steps[i], i); + final Vector3D positionP2 = stateP2.getPosition(); + final TrackingCoordinates trackingCoordinatesP2 = station.getBaseFrame(). + getTrackingCoordinates(positionP2, stateP2.getFrame(), stateP2.getDate()); + double delayP2 = model.pathDelay(trackingCoordinatesP2, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateP2.getDate()).getDelay(); + + SpacecraftState stateP3 = shiftState(state, orbitType, angleType, 3 * steps[i], i); + final Vector3D positionP3 = stateP3.getPosition(); + final TrackingCoordinates trackingCoordinatesP3 = station.getBaseFrame(). + getTrackingCoordinates(positionP3, stateP3.getFrame(), stateP3.getDate()); + double delayP3 = model.pathDelay(trackingCoordinatesP3, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateP3.getDate()).getDelay(); + + SpacecraftState stateP4 = shiftState(state, orbitType, angleType, 4 * steps[i], i); + final Vector3D positionP4 = stateP4.getPosition(); + final TrackingCoordinates trackingCoordinatesP4 = station.getBaseFrame(). + getTrackingCoordinates(positionP4, stateP4.getFrame(), stateP4.getDate()); + double delayP4 = model.pathDelay(trackingCoordinatesP4, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), stateP4.getDate()).getDelay(); + + fillJacobianColumn(refDeriv, i, orbitType, angleType, steps[i], + delayM4, delayM3, delayM2, delayM1, + delayP1, delayP2, delayP3, delayP4); + } + + for (int i = 0; i < 6; i++) { + Assertions.assertEquals(compDelay[i + 1], refDeriv[0][i], 6.2e-12); + } + } + + private void fillJacobianColumn(double[][] jacobian, int column, + OrbitType orbitType, PositionAngleType angleType, double h, + double sM4h, double sM3h, + double sM2h, double sM1h, + double sP1h, double sP2h, + double sP3h, double sP4h) { + for (int i = 0; i < jacobian.length; ++i) { + jacobian[i][column] = ( -3 * (sP4h - sM4h) + + 32 * (sP3h - sM3h) - + 168 * (sP2h - sM2h) + + 672 * (sP1h - sM1h)) / (840 * h); + } + } + + private SpacecraftState shiftState(SpacecraftState state, OrbitType orbitType, PositionAngleType angleType, + double delta, int column) { + + double[][] array = stateToArray(state, orbitType, angleType, true); + array[0][column] += delta; + + return arrayToState(array, orbitType, angleType, state.getFrame(), state.getDate(), + state.getMu(), state.getAttitude()); + + } + + private double[][] stateToArray(SpacecraftState state, OrbitType orbitType, PositionAngleType angleType, + boolean withMass) { + double[][] array = new double[2][withMass ? 7 : 6]; + orbitType.mapOrbitToArray(state.getOrbit(), angleType, array[0], array[1]); + if (withMass) { + array[0][6] = state.getMass(); + } + return array; + } + + private SpacecraftState arrayToState(double[][] array, OrbitType orbitType, PositionAngleType angleType, + Frame frame, AbsoluteDate date, double mu, + Attitude attitude) { + Orbit orbit = orbitType.mapArrayToOrbit(array[0], array[1], angleType, date, mu, frame); + return (array.length > 6) ? + new SpacecraftState(orbit, attitude) : + new SpacecraftState(orbit, attitude, array[0][6]); + } + +} diff --git a/src/test/java/org/orekit/models/earth/troposphere/FixedTroposphericModelTest.java b/src/test/java/org/orekit/models/earth/troposphere/FixedTroposphericModelTest.java index 356a897209..6d4ffe1262 100644 --- a/src/test/java/org/orekit/models/earth/troposphere/FixedTroposphericModelTest.java +++ b/src/test/java/org/orekit/models/earth/troposphere/FixedTroposphericModelTest.java @@ -28,32 +28,68 @@ import org.orekit.Utils; import org.orekit.bodies.FieldGeodeticPoint; import org.orekit.bodies.GeodeticPoint; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldTrackingCoordinates; +import org.orekit.utils.TrackingCoordinates; public class FixedTroposphericModelTest { private static double epsilon = 1e-6; - private DiscreteTroposphericModel model; + private TroposphericModel model; @Test public void testModel() { // check with (artificial) test values from tropospheric-delay.txt - Assertions.assertEquals(2.5d, model.pathDelay(FastMath.toRadians(90d), new GeodeticPoint(0., 0., 0.), null, AbsoluteDate.J2000_EPOCH), epsilon); - Assertions.assertEquals(20.8d, model.pathDelay(FastMath.toRadians(0d), new GeodeticPoint(0., 0., 0.), null, AbsoluteDate.J2000_EPOCH), epsilon); - - Assertions.assertEquals(12.1d, model.pathDelay(FastMath.toRadians(0d), new GeodeticPoint(0., 0., 5000.), null, AbsoluteDate.J2000_EPOCH), epsilon); - Assertions.assertEquals(2.5d, model.pathDelay(FastMath.toRadians(90d), new GeodeticPoint(0., 0., 5000.), null, AbsoluteDate.J2000_EPOCH), epsilon); + Assertions.assertEquals(2.5d, + model.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(90d), 0.0), + new GeodeticPoint(0., 0., 0.), + TroposphericModelUtils.STANDARD_ATMOSPHERE, + null, AbsoluteDate.J2000_EPOCH).getDelay(), + epsilon); + Assertions.assertEquals(20.8d, + model.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(0d), 0.0), + new GeodeticPoint(0., 0., 0.), + TroposphericModelUtils.STANDARD_ATMOSPHERE, + null, AbsoluteDate.J2000_EPOCH).getDelay(), + epsilon); + + Assertions.assertEquals(12.1d, + model.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(0d), 0.0), + new GeodeticPoint(0., 0., 5000.), + TroposphericModelUtils.STANDARD_ATMOSPHERE, + null, AbsoluteDate.J2000_EPOCH).getDelay(), + epsilon); + Assertions.assertEquals(2.5d, + model.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(90d), 0.0), + new GeodeticPoint(0., 0., 5000.), + TroposphericModelUtils.STANDARD_ATMOSPHERE, + null, AbsoluteDate.J2000_EPOCH).getDelay(), + epsilon); // interpolation between two elevation angles in the table - final double delay = model.pathDelay(FastMath.toRadians(35d), new GeodeticPoint(0., 0., 1200.), null, AbsoluteDate.J2000_EPOCH); + final double delay = model.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(35d), 0.0), + new GeodeticPoint(0., 0., 1200.), + TroposphericModelUtils.STANDARD_ATMOSPHERE, + null, AbsoluteDate.J2000_EPOCH).getDelay(); Assertions.assertTrue(Precision.compareTo(delay, 6.4d, epsilon) < 0); Assertions.assertTrue(Precision.compareTo(delay, 3.2d, epsilon) > 0); // sanity checks - Assertions.assertEquals(12.1d, model.pathDelay(FastMath.toRadians(-20d), new GeodeticPoint(0., 0., 5000.), null, AbsoluteDate.J2000_EPOCH), epsilon); - Assertions.assertEquals(2.5d, model.pathDelay(FastMath.toRadians(90d), new GeodeticPoint(0., 0., 100000.), null, AbsoluteDate.J2000_EPOCH), epsilon); + Assertions.assertEquals(12.1d, + model.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(-20d), 0.0), + new GeodeticPoint(0., 0., 5000.), + TroposphericModelUtils.STANDARD_ATMOSPHERE, + null, AbsoluteDate.J2000_EPOCH).getDelay(), + epsilon); + Assertions.assertEquals(2.5d, + model.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(90d),0.0), + new GeodeticPoint(0., 0., 100000.), + TroposphericModelUtils.STANDARD_ATMOSPHERE, + null, AbsoluteDate.J2000_EPOCH).getDelay(), + epsilon); } @Test @@ -64,27 +100,64 @@ public void testFieldModel() { private > void doTestFieldModel(final Field field) { final T zero = field.getZero(); // check with (artificial) test values from tropospheric-delay.txt - Assertions.assertEquals(2.5d, model.pathDelay(zero.add(FastMath.toRadians(90d)), new FieldGeodeticPoint(zero, zero, zero), null, FieldAbsoluteDate.getJ2000Epoch(field)).getReal(), epsilon); - Assertions.assertEquals(20.8d, model.pathDelay(zero.add(FastMath.toRadians(0d)), new FieldGeodeticPoint(zero, zero, zero), null, FieldAbsoluteDate.getJ2000Epoch(field)).getReal(), epsilon); - - Assertions.assertEquals(12.1d, model.pathDelay(zero.add(FastMath.toRadians(0d)), new FieldGeodeticPoint(zero, zero, zero.add(5000.0)), null, FieldAbsoluteDate.getJ2000Epoch(field)).getReal(), epsilon); - Assertions.assertEquals(2.5d, model.pathDelay(zero.add(FastMath.toRadians(90d)), new FieldGeodeticPoint(zero, zero, zero.add(5000.0)), null, FieldAbsoluteDate.getJ2000Epoch(field)).getReal(), epsilon); + Assertions.assertEquals(2.5d, + model.pathDelay(new FieldTrackingCoordinates<>(zero, zero.newInstance(FastMath.toRadians(90d)), zero), + new FieldGeodeticPoint(zero, zero, zero), + new FieldPressureTemperatureHumidity(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + null, FieldAbsoluteDate.getJ2000Epoch(field)).getDelay().getReal(), + epsilon); + Assertions.assertEquals(20.8d, + model.pathDelay(new FieldTrackingCoordinates<>(zero, zero.newInstance(FastMath.toRadians(0d)), zero), + new FieldGeodeticPoint(zero, zero, zero), + new FieldPressureTemperatureHumidity(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + null, FieldAbsoluteDate.getJ2000Epoch(field)).getDelay().getReal(), + epsilon); + + Assertions.assertEquals(12.1d, + model.pathDelay(new FieldTrackingCoordinates<>(zero, zero.newInstance(FastMath.toRadians(0d)), zero), + new FieldGeodeticPoint(zero, zero, zero.add(5000.0)), + new FieldPressureTemperatureHumidity(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + null, FieldAbsoluteDate.getJ2000Epoch(field)).getDelay().getReal(), + epsilon); + Assertions.assertEquals(2.5d, + model.pathDelay(new FieldTrackingCoordinates<>(zero, zero.newInstance(FastMath.toRadians(90d)), zero), + new FieldGeodeticPoint(zero, zero, zero.add(5000.0)), + new FieldPressureTemperatureHumidity(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + null, FieldAbsoluteDate.getJ2000Epoch(field)).getDelay().getReal(), + epsilon); // interpolation between two elevation angles in the table - final double delay = model.pathDelay(zero.add(FastMath.toRadians(35d)), new FieldGeodeticPoint(zero, zero, zero.add(1200.0)), null, FieldAbsoluteDate.getJ2000Epoch(field)).getReal(); + final double delay = model.pathDelay(new FieldTrackingCoordinates<>(zero, zero.newInstance(FastMath.toRadians(35d)), zero), + new FieldGeodeticPoint(zero, zero, zero.add(1200.0)), + new FieldPressureTemperatureHumidity(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + null, FieldAbsoluteDate.getJ2000Epoch(field)).getDelay().getReal(); Assertions.assertTrue(Precision.compareTo(delay, 6.4d, epsilon) < 0); Assertions.assertTrue(Precision.compareTo(delay, 3.2d, epsilon) > 0); // sanity checks - Assertions.assertEquals(12.1d, model.pathDelay(zero.add(FastMath.toRadians(-20d)), new FieldGeodeticPoint(zero, zero, zero.add(5000.0)), null, FieldAbsoluteDate.getJ2000Epoch(field)).getReal(), epsilon); - Assertions.assertEquals(2.5d, model.pathDelay(zero.add(FastMath.toRadians(90d)), new FieldGeodeticPoint(zero, zero, zero.add(100000.0)), null, FieldAbsoluteDate.getJ2000Epoch(field)).getReal(), epsilon); + Assertions.assertEquals(12.1d, + model.pathDelay(new FieldTrackingCoordinates<>(zero, zero.newInstance(FastMath.toRadians(-20d)), zero), + new FieldGeodeticPoint(zero, zero, zero.add(5000.0)), + new FieldPressureTemperatureHumidity(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + null, FieldAbsoluteDate.getJ2000Epoch(field)).getDelay().getReal(), + epsilon); + Assertions.assertEquals(2.5d, + model.pathDelay(new FieldTrackingCoordinates<>(zero, zero.newInstance(FastMath.toRadians(90d)), zero), + new FieldGeodeticPoint(zero, zero, zero.add(100000.0)), + new FieldPressureTemperatureHumidity(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + null, FieldAbsoluteDate.getJ2000Epoch(field)).getDelay().getReal(), + epsilon); } @Test public void testSymmetry() { for (int elevation = 0; elevation < 90; elevation += 10) { - final double delay1 = model.pathDelay(FastMath.toRadians(elevation), new GeodeticPoint(0., 0., 100.), null, AbsoluteDate.J2000_EPOCH); - final double delay2 = model.pathDelay(FastMath.toRadians(180 - elevation), new GeodeticPoint(0., 0., 100.), null, AbsoluteDate.J2000_EPOCH); + final double delay1 = model.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(elevation), 0.0), + new GeodeticPoint(0., 0., 100.), + TroposphericModelUtils.STANDARD_ATMOSPHERE, null, AbsoluteDate.J2000_EPOCH).getDelay(); + final double delay2 = model.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(180 - elevation), 0.0), + new GeodeticPoint(0., 0., 100.), + TroposphericModelUtils.STANDARD_ATMOSPHERE, null, AbsoluteDate.J2000_EPOCH).getDelay(); Assertions.assertEquals(delay1, delay2, epsilon); } @@ -98,14 +171,16 @@ public void testFieldSymmetry() { private > void doTestFieldSymmetry(final Field field) { final T zero = field.getZero(); for (int elevation = 0; elevation < 90; elevation += 10) { - final T delay1 = model.pathDelay(zero.add(FastMath.toRadians(elevation)), + final T delay1 = model.pathDelay(new FieldTrackingCoordinates<>(zero, zero.newInstance(FastMath.toRadians(elevation)), zero), new FieldGeodeticPoint(zero, zero, zero.add(100.)), + new FieldPressureTemperatureHumidity(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), null, - FieldAbsoluteDate.getJ2000Epoch(field)); - final T delay2 = model.pathDelay(zero.add(FastMath.toRadians(180 - elevation)), + FieldAbsoluteDate.getJ2000Epoch(field)).getDelay(); + final T delay2 = model.pathDelay(new FieldTrackingCoordinates<>(zero, zero.newInstance(FastMath.toRadians(180 - elevation)), zero), new FieldGeodeticPoint(zero, zero, zero.add(100.)), - null, - FieldAbsoluteDate.getJ2000Epoch(field)); + new FieldPressureTemperatureHumidity(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + null, + FieldAbsoluteDate.getJ2000Epoch(field)).getDelay(); Assertions.assertEquals(delay1.getReal(), delay2.getReal(), epsilon); } diff --git a/src/test/java/org/orekit/models/earth/troposphere/GlobalMappingFunctionModelTest.java b/src/test/java/org/orekit/models/earth/troposphere/GlobalMappingFunctionModelTest.java index 277b43085e..2079699b65 100644 --- a/src/test/java/org/orekit/models/earth/troposphere/GlobalMappingFunctionModelTest.java +++ b/src/test/java/org/orekit/models/earth/troposphere/GlobalMappingFunctionModelTest.java @@ -27,6 +27,7 @@ import org.orekit.errors.OrekitException; import org.orekit.time.AbsoluteDate; import org.orekit.time.TimeScalesFactory; +import org.orekit.utils.TrackingCoordinates; public class GlobalMappingFunctionModelTest { @@ -62,13 +63,15 @@ public void testMappingFactors() { final double height = 844.715; final GeodeticPoint point = new GeodeticPoint(latitude, longitude, height); - final double elevation = 0.5 * FastMath.PI - 1.278564131; + final TrackingCoordinates trackingCoordinates = new TrackingCoordinates(0.0, 0.5 * FastMath.PI - 1.278564131, 0.0); final double expectedHydro = 3.425246; final double expectedWet = 3.449589; - final MappingFunction model = new GlobalMappingFunctionModel(); + final TroposphereMappingFunction model = new GlobalMappingFunctionModel(); - final double[] computedMapping = model.mappingFactors(elevation, point, date); + final double[] computedMapping = model.mappingFactors(trackingCoordinates, point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + date); Assertions.assertEquals(expectedHydro, computedMapping[0], 1.0e-6); Assertions.assertEquals(expectedWet, computedMapping[1], 1.0e-6); @@ -77,7 +80,7 @@ public void testMappingFactors() { @Test public void testFixedHeight() { final AbsoluteDate date = new AbsoluteDate(); - MappingFunction model = new GlobalMappingFunctionModel(); + TroposphereMappingFunction model = new GlobalMappingFunctionModel(); double[] lastFactors = new double[] { Double.MAX_VALUE, Double.MAX_VALUE @@ -85,7 +88,10 @@ public void testFixedHeight() { GeodeticPoint point = new GeodeticPoint(FastMath.toRadians(45.0), FastMath.toRadians(45.0), 350.0); // mapping functions shall decline with increasing elevation angle for (double elev = 10d; elev < 90d; elev += 8d) { - final double[] factors = model.mappingFactors(FastMath.toRadians(elev), point, date); + final double[] factors = model.mappingFactors(new TrackingCoordinates(0.0, FastMath.toRadians(elev), 0.0), + point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + date); Assertions.assertTrue(Precision.compareTo(factors[0], lastFactors[0], 1.0e-6) < 0); Assertions.assertTrue(Precision.compareTo(factors[1], lastFactors[1], 1.0e-6) < 0); lastFactors[0] = factors[0]; diff --git a/src/test/java/org/orekit/models/earth/troposphere/MariniMurrayModelTest.java b/src/test/java/org/orekit/models/earth/troposphere/MariniMurrayModelTest.java index 07cd3151c3..9153374aab 100644 --- a/src/test/java/org/orekit/models/earth/troposphere/MariniMurrayModelTest.java +++ b/src/test/java/org/orekit/models/earth/troposphere/MariniMurrayModelTest.java @@ -28,15 +28,17 @@ import org.orekit.Utils; import org.orekit.bodies.FieldGeodeticPoint; import org.orekit.bodies.GeodeticPoint; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldTrackingCoordinates; +import org.orekit.utils.TrackingCoordinates; +@Deprecated public class MariniMurrayModelTest { private static double epsilon = 1e-6; - private DiscreteTroposphericModel model; - private double latitude; private double longitude; @@ -48,8 +50,6 @@ public static void setUpGlobal() { @BeforeEach public void setUp() throws Exception { - // ruby laser with wavelength 694.3 nm - model = MariniMurrayModel.getStandardModel(694.3); latitude = FastMath.toRadians(45.0); longitude = FastMath.toRadians(45.0); } @@ -59,7 +59,43 @@ public void testDelay() { final double elevation = 10d; final double height = 100d; - final double path = model.pathDelay(FastMath.toRadians(elevation), new GeodeticPoint(latitude, longitude, height), null, AbsoluteDate.J2000_EPOCH); + // ruby laser with wavelength 694.3 nm + TroposphericModel model = MariniMurrayModel.getStandardModel(694.3); + final double path = model.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(elevation), 0.0), + new GeodeticPoint(latitude, longitude, height), + TroposphericModelUtils.STANDARD_ATMOSPHERE, null, AbsoluteDate.J2000_EPOCH).getDelay(); + + Assertions.assertTrue(Precision.compareTo(path, 20d, epsilon) < 0); + Assertions.assertTrue(Precision.compareTo(path, 0d, epsilon) > 0); + } + + @Deprecated + @Test + public void testDeprecatedConstructor1() { + final double elevation = 10d; + final double height = 100d; + + // ruby laser with wavelength 694.3 nm + TroposphericModel model = MariniMurrayModel.getStandardModel(694.3); + final double path = model.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(elevation), 0.0), + new GeodeticPoint(latitude, longitude, height), + TroposphericModelUtils.STANDARD_ATMOSPHERE, null, AbsoluteDate.J2000_EPOCH).getDelay(); + + Assertions.assertTrue(Precision.compareTo(path, 20d, epsilon) < 0); + Assertions.assertTrue(Precision.compareTo(path, 0d, epsilon) > 0); + } + + @Deprecated + @Test + public void testDeprecatedConstructor2() { + final double elevation = 10d; + final double height = 100d; + + // ruby laser with wavelength 694.3 nm + TroposphericModel model = new MariniMurrayModel(273.15 + 20, 1013.25, 0.5, 694.3); + final double path = model.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(elevation), 0.0), + new GeodeticPoint(latitude, longitude, height), + TroposphericModelUtils.STANDARD_ATMOSPHERE, null, AbsoluteDate.J2000_EPOCH).getDelay(); Assertions.assertTrue(Precision.compareTo(path, 20d, epsilon) < 0); Assertions.assertTrue(Precision.compareTo(path, 0d, epsilon) > 0); @@ -72,10 +108,17 @@ public void testFieldDelay() { private > void doTestFieldDelay(final Field field) { final T zero = field.getZero(); - final T elevation = zero.add(FastMath.toRadians(10d)); + final FieldTrackingCoordinates trackingCoordinates = + new FieldTrackingCoordinates<>(zero, + zero.newInstance(FastMath.toRadians(10d)), + zero); final T height = zero.add(100d); - final T path = model.pathDelay(elevation, new FieldGeodeticPoint<>(zero.add(latitude), zero.add(longitude), height), null, FieldAbsoluteDate.getJ2000Epoch(field)); + // ruby laser with wavelength 694.3 nm + TroposphericModel model = MariniMurrayModel.getStandardModel(694.3); + final T path = model.pathDelay(trackingCoordinates, new FieldGeodeticPoint<>(zero.add(latitude), zero.add(longitude), height), + new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + null, FieldAbsoluteDate.getJ2000Epoch(field)).getDelay(); Assertions.assertTrue(Precision.compareTo(path.getReal(), 20d, epsilon) < 0); Assertions.assertTrue(Precision.compareTo(path.getReal(), 0d, epsilon) > 0); @@ -83,10 +126,14 @@ private > void doTestFieldDelay(final Field @Test public void testFixedHeight() { + // ruby laser with wavelength 694.3 nm + TroposphericModel model = MariniMurrayModel.getStandardModel(694.3); double lastDelay = Double.MAX_VALUE; // delay shall decline with increasing elevation angle for (double elev = 10d; elev < 90d; elev += 8d) { - final double delay = model.pathDelay(FastMath.toRadians(elev), new GeodeticPoint(latitude, longitude, 350.0), null, AbsoluteDate.J2000_EPOCH); + final double delay = model.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(elev), 0.0), + new GeodeticPoint(latitude, longitude, 350.0), + TroposphericModelUtils.STANDARD_ATMOSPHERE, null, AbsoluteDate.J2000_EPOCH).getDelay(); Assertions.assertTrue(Precision.compareTo(delay, lastDelay, epsilon) < 0); lastDelay = delay; } @@ -98,11 +145,16 @@ public void testFieldFixedHeight() { } private > void doTestFieldFixedHeight(final Field field) { + // ruby laser with wavelength 694.3 nm + TroposphericModel model = MariniMurrayModel.getStandardModel(694.3); final T zero = field.getZero(); T lastDelay = zero.add(Double.MAX_VALUE); // delay shall decline with increasing elevation angle for (double elev = 10d; elev < 90d; elev += 8d) { - final T delay = model.pathDelay(zero.add(FastMath.toRadians(elev)), new FieldGeodeticPoint<>(zero.add(latitude), zero.add(longitude), zero.add(350.0)), null, FieldAbsoluteDate.getJ2000Epoch(field)); + final T delay = model.pathDelay(new FieldTrackingCoordinates<>(zero, zero.newInstance(FastMath.toRadians(elev)), zero), + new FieldGeodeticPoint<>(zero.add(latitude), zero.add(longitude), zero.add(350.0)), + new FieldPressureTemperatureHumidity(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + null, FieldAbsoluteDate.getJ2000Epoch(field)).getDelay(); Assertions.assertTrue(Precision.compareTo(delay.getReal(), lastDelay.getReal(), epsilon) < 0); lastDelay = delay; } @@ -111,10 +163,15 @@ private > void doTestFieldFixedHeight(final Fi @Test public void compareExpectedValues() { + // ruby laser with wavelength 694.3 nm + TroposphericModel model = MariniMurrayModel.getStandardModel(694.3); + double height = 0; double elevation = 10; double expectedValue = 13.26069; - double actualValue = model.pathDelay(FastMath.toRadians(elevation), new GeodeticPoint(latitude, longitude, height), null, AbsoluteDate.J2000_EPOCH); + double actualValue = model.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(elevation), 0.0), + new GeodeticPoint(latitude, longitude, height), + TroposphericModelUtils.STANDARD_ATMOSPHERE, null, AbsoluteDate.J2000_EPOCH).getDelay(); Assertions.assertEquals(expectedValue, actualValue, 1.0e-5); } @@ -126,11 +183,17 @@ public void compareFieldExpectedValue() { private > void doCompareFieldExpectedValues(final Field field) { + // ruby laser with wavelength 694.3 nm + TroposphericModel model = MariniMurrayModel.getStandardModel(694.3); + T zero = field.getZero(); T height = zero; - T elevation = zero.add(FastMath.toRadians(10)); + T elevation = zero.newInstance(FastMath.toRadians(10)); double expectedValue = 13.26069; - T actualValue = model.pathDelay(elevation, new FieldGeodeticPoint<>(zero.add(latitude), zero.add(longitude), height), null, FieldAbsoluteDate.getJ2000Epoch(field)); + T actualValue = model.pathDelay(new FieldTrackingCoordinates<>(zero, elevation, zero), + new FieldGeodeticPoint<>(zero.add(latitude), zero.add(longitude), height), + new FieldPressureTemperatureHumidity(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + null, FieldAbsoluteDate.getJ2000Epoch(field)).getDelay(); Assertions.assertEquals(expectedValue, actualValue.getReal(), 1.0e-5); } diff --git a/src/test/java/org/orekit/models/earth/troposphere/MariniMurrayTest.java b/src/test/java/org/orekit/models/earth/troposphere/MariniMurrayTest.java new file mode 100644 index 0000000000..4a98599b81 --- /dev/null +++ b/src/test/java/org/orekit/models/earth/troposphere/MariniMurrayTest.java @@ -0,0 +1,168 @@ +/* Copyright 2011-2012 Space Applications Services + * Licensed to CS Communication & Systèmes (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.models.earth.troposphere; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.util.Binary64Field; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.Precision; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.orekit.Utils; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldTrackingCoordinates; +import org.orekit.utils.TrackingCoordinates; + +public class MariniMurrayTest { + + private static double epsilon = 1e-6; + + private double latitude; + + private double longitude; + + @BeforeAll + public static void setUpGlobal() { + Utils.setDataRoot("atmosphere"); + } + + @BeforeEach + public void setUp() throws Exception { + latitude = FastMath.toRadians(45.0); + longitude = FastMath.toRadians(45.0); + } + + @Test + public void testDelay() { + final double elevation = 10d; + final double height = 100d; + + // ruby laser with wavelength 694.3 nm + TroposphericModel model = new MariniMurray(694.3, TroposphericModelUtils.NANO_M); + final double path = model.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(elevation), 0.0), + new GeodeticPoint(latitude, longitude, height), + TroposphericModelUtils.STANDARD_ATMOSPHERE, null, AbsoluteDate.J2000_EPOCH).getDelay(); + + Assertions.assertTrue(Precision.compareTo(path, 20d, epsilon) < 0); + Assertions.assertTrue(Precision.compareTo(path, 0d, epsilon) > 0); + } + + @Test + public void testFieldDelay() { + doTestFieldDelay(Binary64Field.getInstance()); + } + + private > void doTestFieldDelay(final Field field) { + final T zero = field.getZero(); + final FieldTrackingCoordinates trackingCoordinates = + new FieldTrackingCoordinates<>(zero, + zero.newInstance(FastMath.toRadians(10d)), + zero); + final T height = zero.add(100d); + + // ruby laser with wavelength 694.3 nm + TroposphericModel model = new MariniMurray(694.3, TroposphericModelUtils.NANO_M); + final T path = model.pathDelay(trackingCoordinates, new FieldGeodeticPoint<>(zero.add(latitude), zero.add(longitude), height), + new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + null, FieldAbsoluteDate.getJ2000Epoch(field)).getDelay(); + + Assertions.assertTrue(Precision.compareTo(path.getReal(), 20d, epsilon) < 0); + Assertions.assertTrue(Precision.compareTo(path.getReal(), 0d, epsilon) > 0); + } + + @Test + public void testFixedHeight() { + // ruby laser with wavelength 694.3 nm + TroposphericModel model = new MariniMurray(694.3, TroposphericModelUtils.NANO_M); + double lastDelay = Double.MAX_VALUE; + // delay shall decline with increasing elevation angle + for (double elev = 10d; elev < 90d; elev += 8d) { + final double delay = model.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(elev), 0.0), + new GeodeticPoint(latitude, longitude, 350.0), + TroposphericModelUtils.STANDARD_ATMOSPHERE, null, AbsoluteDate.J2000_EPOCH).getDelay(); + Assertions.assertTrue(Precision.compareTo(delay, lastDelay, epsilon) < 0); + lastDelay = delay; + } + } + + @Test + public void testFieldFixedHeight() { + doTestFieldFixedHeight(Binary64Field.getInstance()); + } + + private > void doTestFieldFixedHeight(final Field field) { + // ruby laser with wavelength 694.3 nm + TroposphericModel model = new MariniMurray(694.3, TroposphericModelUtils.NANO_M); + final T zero = field.getZero(); + T lastDelay = zero.add(Double.MAX_VALUE); + // delay shall decline with increasing elevation angle + for (double elev = 10d; elev < 90d; elev += 8d) { + final T delay = model.pathDelay(new FieldTrackingCoordinates<>(zero, zero.newInstance(FastMath.toRadians(elev)), zero), + new FieldGeodeticPoint<>(zero.add(latitude), zero.add(longitude), zero.add(350.0)), + new FieldPressureTemperatureHumidity(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + null, FieldAbsoluteDate.getJ2000Epoch(field)).getDelay(); + Assertions.assertTrue(Precision.compareTo(delay.getReal(), lastDelay.getReal(), epsilon) < 0); + lastDelay = delay; + } + } + + @Test + public void compareExpectedValues() { + + // ruby laser with wavelength 694.3 nm + TroposphericModel model = new MariniMurray(694.3, TroposphericModelUtils.NANO_M); + + double height = 0; + double elevation = 10; + double expectedValue = 13.26069; + double actualValue = model.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(elevation), 0.0), + new GeodeticPoint(latitude, longitude, height), + TroposphericModelUtils.STANDARD_ATMOSPHERE, null, AbsoluteDate.J2000_EPOCH).getDelay(); + + Assertions.assertEquals(expectedValue, actualValue, 1.0e-5); + } + + @Test + public void compareFieldExpectedValue() { + doCompareFieldExpectedValues(Binary64Field.getInstance()); + } + + private > void doCompareFieldExpectedValues(final Field field) { + + // ruby laser with wavelength 694.3 nm + TroposphericModel model = new MariniMurray(694.3, TroposphericModelUtils.NANO_M); + + T zero = field.getZero(); + T height = zero; + T elevation = zero.newInstance(FastMath.toRadians(10)); + double expectedValue = 13.26069; + T actualValue = model.pathDelay(new FieldTrackingCoordinates<>(zero, elevation, zero), + new FieldGeodeticPoint<>(zero.add(latitude), zero.add(longitude), height), + new FieldPressureTemperatureHumidity(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + null, FieldAbsoluteDate.getJ2000Epoch(field)).getDelay(); + + Assertions.assertEquals(expectedValue, actualValue.getReal(), 1.0e-5); + } + +} diff --git a/src/test/java/org/orekit/models/earth/troposphere/MendesPavlisModelTest.java b/src/test/java/org/orekit/models/earth/troposphere/MendesPavlisModelTest.java index 2914e1a37b..5be89ef067 100644 --- a/src/test/java/org/orekit/models/earth/troposphere/MendesPavlisModelTest.java +++ b/src/test/java/org/orekit/models/earth/troposphere/MendesPavlisModelTest.java @@ -25,8 +25,12 @@ import org.orekit.Utils; import org.orekit.bodies.GeodeticPoint; import org.orekit.errors.OrekitException; +import org.orekit.models.earth.weather.ConstantPressureTemperatureHumidityProvider; +import org.orekit.models.earth.weather.PressureTemperatureHumidity; +import org.orekit.models.earth.weather.water.CIPM2007; import org.orekit.time.AbsoluteDate; import org.orekit.time.TimeScalesFactory; +import org.orekit.utils.TrackingCoordinates; ; @@ -63,9 +67,18 @@ public void testZenithDelay() { final double latitude = FastMath.toRadians(30.67166667); final double longitude = FastMath.toRadians(-104.0250); final double height = 2010.344; - final double pressure = 798.4188; + final double pressure = TroposphericModelUtils.HECTO_PASCAL.toSI(798.4188); final double temperature = 300.15; final double humidity = 0.4; + final PressureTemperatureHumidity pth = new PressureTemperatureHumidity(height, + pressure, + temperature, + new CIPM2007(). + waterVaporPressure(pressure, + temperature, + humidity), + Double.NaN, + Double.NaN); final double lambda = 0.532; final GeodeticPoint point = new GeodeticPoint(latitude, longitude, height); @@ -80,8 +93,8 @@ public void testZenithDelay() { final AbsoluteDate date = new AbsoluteDate(2009, 8, 12, TimeScalesFactory.getUTC()); - final MendesPavlisModel model = new MendesPavlisModel(temperature, pressure, - humidity, lambda); + final MendesPavlisModel model = new MendesPavlisModel(new ConstantPressureTemperatureHumidityProvider(pth), + lambda, TroposphericModelUtils.MICRO_M); final double[] computedDelay = model.computeZenithDelay(point, model.getParameters(), date); @@ -112,21 +125,32 @@ public void testMappingFactors() { final double latitude = FastMath.toRadians(30.67166667); final double longitude = FastMath.toRadians(-104.0250); final double height = 2075; - final double pressure = 798.4188; + final double pressure = TroposphericModelUtils.HECTO_PASCAL.toSI(798.4188); final double temperature = 300.15; final double humidity = 0.4; + final PressureTemperatureHumidity pth = new PressureTemperatureHumidity(height, + pressure, + temperature, + new CIPM2007(). + waterVaporPressure(pressure, + temperature, + humidity), + Double.NaN, + Double.NaN); final double lambda = 0.532; final GeodeticPoint point = new GeodeticPoint(latitude, longitude, height); - final double elevation = FastMath.toRadians(15.0); + final TrackingCoordinates trackingCoordinates = new TrackingCoordinates(0.0, FastMath.toRadians(15.0), 0.0); // Expected mapping factor: 3.80024367 (Ref) final double expectedMapping = 3.80024367; // Test for the second constructor - final MendesPavlisModel model = new MendesPavlisModel(temperature, pressure, - humidity, lambda); + final MendesPavlisModel model = new MendesPavlisModel(new ConstantPressureTemperatureHumidityProvider(pth), + lambda, TroposphericModelUtils.MICRO_M); - final double[] computedMapping = model.mappingFactors(elevation, point, date); + final double[] computedMapping = model.mappingFactors(trackingCoordinates, point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + date); Assertions.assertEquals(expectedMapping, computedMapping[0], 5.0e-8); Assertions.assertEquals(expectedMapping, computedMapping[1], 5.0e-8); @@ -138,8 +162,11 @@ public void testDelay() { final double height = 100d; final AbsoluteDate date = new AbsoluteDate(); final GeodeticPoint point = new GeodeticPoint(FastMath.toRadians(45.0), FastMath.toRadians(45.0), height); - MendesPavlisModel model = MendesPavlisModel.getStandardModel( 0.6943); - final double path = model.pathDelay(FastMath.toRadians(elevation), point, model.getParameters(), date); + MendesPavlisModel model = MendesPavlisModel.getStandardModel( 0.6943, TroposphericModelUtils.MICRO_M); + final double path = model.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(elevation), 0.0), + point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), date).getDelay(); Assertions.assertTrue(Precision.compareTo(path, 20d, epsilon) < 0); Assertions.assertTrue(Precision.compareTo(path, 0d, epsilon) > 0); } @@ -148,14 +175,77 @@ public void testDelay() { public void testFixedHeight() { final AbsoluteDate date = new AbsoluteDate(); final GeodeticPoint point = new GeodeticPoint(FastMath.toRadians(45.0), FastMath.toRadians(45.0), 350.0); - MendesPavlisModel model = MendesPavlisModel.getStandardModel(0.6943); + MendesPavlisModel model = MendesPavlisModel.getStandardModel(0.6943, TroposphericModelUtils.MICRO_M); double lastDelay = Double.MAX_VALUE; // delay shall decline with increasing elevation angle for (double elev = 10d; elev < 90d; elev += 8d) { - final double delay = model.pathDelay(FastMath.toRadians(elev), point, model.getParameters(), date); + final double delay = model.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(elev), 0.0), + point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), date).getDelay(); Assertions.assertTrue(Precision.compareTo(delay, lastDelay, epsilon) < 0); lastDelay = delay; } } + @Deprecated + @Test + public void testDeprecatedConstructor1() { + + // Site: McDonald Observatory + // latitude: 30.67166667 ° + // longitude: -104.0250 ° + // height: 2010.344 m + // + // Meteo: pressure: 798.4188 hPa + // water vapor presure: 14.322 hPa + // temperature: 300.15 K + // humidity: 40 % + // + // Ref: Petit, G. and Luzum, B. (eds.), IERS Conventions (2010), + // IERS Technical Note No. 36, BKG (2010) + + final double latitude = FastMath.toRadians(30.67166667); + final double longitude = FastMath.toRadians(-104.0250); + final double height = 2010.344; + final double pressure = 798.4188; + final double temperature = 300.15; + final double humidity = 0.4; + final double lambda = 0.532; + final GeodeticPoint point = new GeodeticPoint(latitude, longitude, height); + + // Expected zenith hydrostatic delay: 1.932992 m (Ref) + final double expectedHydroDelay = 1.932992; + // Expected zenith wet delay: 0.223375*10-2 m (Ref) + final double expectedWetDelay = 0.223375e-2; + // Expected total zenith delay: 1.935226 m (Ref) + final double expectedDelay = 1.935226; + + final double precision = 4.0e-6; + + final AbsoluteDate date = new AbsoluteDate(2009, 8, 12, TimeScalesFactory.getUTC()); + + final MendesPavlisModel model = new MendesPavlisModel(temperature, pressure, humidity, lambda); + + final double[] computedDelay = model.computeZenithDelay(point, model.getParameters(), date); + + Assertions.assertEquals(expectedHydroDelay, computedDelay[0], precision); + Assertions.assertEquals(expectedWetDelay, computedDelay[1], precision); + Assertions.assertEquals(expectedDelay, computedDelay[0] + computedDelay[1], precision); + + } + + @Deprecated + @Test + public void testDeprecatedConstructor2() { + final double elevation = 10d; + final double height = 100d; + final AbsoluteDate date = new AbsoluteDate(); + final GeodeticPoint point = new GeodeticPoint(FastMath.toRadians(45.0), FastMath.toRadians(45.0), height); + MendesPavlisModel model = MendesPavlisModel.getStandardModel( 0.6943); + final double path = model.pathDelay(FastMath.toRadians(elevation), point, model.getParameters(), date); + Assertions.assertTrue(Precision.compareTo(path, 20d, epsilon) < 0); + Assertions.assertTrue(Precision.compareTo(path, 0d, epsilon) > 0); + } + } diff --git a/src/test/java/org/orekit/models/earth/troposphere/ModifiedHopfieldModelTest.java b/src/test/java/org/orekit/models/earth/troposphere/ModifiedHopfieldModelTest.java new file mode 100644 index 0000000000..3f0897dc58 --- /dev/null +++ b/src/test/java/org/orekit/models/earth/troposphere/ModifiedHopfieldModelTest.java @@ -0,0 +1,377 @@ +/* Copyright 2002-2024 Thales Alenia Space + * Licensed to CS Communication & Systèmes (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.models.earth.troposphere; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.util.Binary64Field; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.Precision; +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.FieldGeodeticPoint; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; +import org.orekit.models.earth.weather.HeightDependentPressureTemperatureHumidityConverter; +import org.orekit.models.earth.weather.water.CIPM2007; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldTrackingCoordinates; +import org.orekit.utils.TrackingCoordinates; + + +public class ModifiedHopfieldModelTest { + + private static double epsilon = 1e-6; + + private double[][] expectedValues; + + private double[] elevations; + + private double[] heights; + + @Test + public void testFixedElevation() { + Utils.setDataRoot("atmosphere"); + ModifiedHopfieldModel model = new ModifiedHopfieldModel(); + HeightDependentPressureTemperatureHumidityConverter converter = + new HeightDependentPressureTemperatureHumidityConverter(new CIPM2007()); + double lastDelay = Double.MAX_VALUE; + // delay shall decline with increasing height of the station + for (double height = 0; height < 5000; height += 100) { + final double delay = model.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(5), 0.0), + new GeodeticPoint(0.0, 0.0, height), + converter.convert(TroposphericModelUtils.STANDARD_ATMOSPHERE, height), + null, AbsoluteDate.J2000_EPOCH).getDelay(); + Assertions.assertTrue(Precision.compareTo(delay, lastDelay, epsilon) < 0); + lastDelay = delay; + } + } + + @Test + public void testFieldFixedElevation() { + doTestFieldFixedElevation(Binary64Field.getInstance()); + } + + private > void doTestFieldFixedElevation(final Field field) { + final T zero = field.getZero(); + Utils.setDataRoot("atmosphere"); + ModifiedHopfieldModel model = new ModifiedHopfieldModel(); + HeightDependentPressureTemperatureHumidityConverter converter = + new HeightDependentPressureTemperatureHumidityConverter(new CIPM2007()); + T lastDelay = zero.newInstance(Double.MAX_VALUE); + // delay shall decline with increasing height of the station + for (double height = 0; height < 5000; height += 100) { + final T delay = model.pathDelay(new FieldTrackingCoordinates<>(zero, + zero.newInstance(FastMath.toRadians(5)), + zero), + new FieldGeodeticPoint<>(zero, zero, zero.newInstance(height)), + converter.convert(new FieldPressureTemperatureHumidity<>(field, + TroposphericModelUtils.STANDARD_ATMOSPHERE), + zero.newInstance(height)), + null, FieldAbsoluteDate.getJ2000Epoch(field)).getDelay(); + Assertions.assertTrue(Precision.compareTo(delay.getReal(), lastDelay.getReal(), epsilon) < 0); + lastDelay = delay; + } + } + + @Test + public void testFixedHeight() { + Utils.setDataRoot("atmosphere"); + ModifiedHopfieldModel model = new ModifiedHopfieldModel(); + double lastDelay = Double.MAX_VALUE; + // delay shall decline with increasing elevation angle + for (double elev = 10d; elev < 90d; elev += 8d) { + final double delay = model.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(elev), 0.0), + new GeodeticPoint(0.0, 0.0, 350.0), + TroposphericModelUtils.STANDARD_ATMOSPHERE, + null, AbsoluteDate.J2000_EPOCH).getDelay(); + Assertions.assertTrue(Precision.compareTo(delay, lastDelay, epsilon) < 0); + lastDelay = delay; + } + } + + @Test + public void testFieldFixedHeight() { + doTestFieldFixedHeight(Binary64Field.getInstance()); + } + + private > void doTestFieldFixedHeight(final Field field) { + final T zero = field.getZero(); + Utils.setDataRoot("atmosphere"); + ModifiedHopfieldModel model = new ModifiedHopfieldModel(); + T lastDelay = zero.newInstance(Double.MAX_VALUE); + // delay shall decline with increasing elevation angle + for (double elev = 10d; elev < 90d; elev += 8d) { + final T delay = model.pathDelay(new FieldTrackingCoordinates<>(zero, + zero.newInstance(FastMath.toRadians(elev)), + zero), + new FieldGeodeticPoint<>(zero, zero, zero.newInstance(350.0)), + new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + null, FieldAbsoluteDate.getJ2000Epoch(field)).getDelay(); + Assertions.assertTrue(Precision.compareTo(delay.getReal(), lastDelay.getReal(), epsilon) < 0); + lastDelay = delay; + } + } + + @Test + public void testFieldVsNative() { + doTestFieldVsNative(Binary64Field.getInstance()); + } + + private > void doTestFieldVsNative(final Field field) { + final T zero = field.getZero(); + Utils.setDataRoot("atmosphere"); + ModifiedHopfieldModel model = new ModifiedHopfieldModel(); + HeightDependentPressureTemperatureHumidityConverter converter = + new HeightDependentPressureTemperatureHumidityConverter(new CIPM2007()); + for (int h = 0; h < 5000.0; h += 100) { + for (int e = 0; e < 90; e += 1.0) { + final double delayN = model.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(e), 0.0), + new GeodeticPoint(0, 0, h), + converter.convert(TroposphericModelUtils.STANDARD_ATMOSPHERE, h), + null, AbsoluteDate.J2000_EPOCH).getDelay(); + final T delayT = model.pathDelay(new FieldTrackingCoordinates<>(zero, + zero.newInstance(FastMath.toRadians(e)), + zero), + new FieldGeodeticPoint<>(zero, zero, zero.newInstance(h)), + converter.convert(new FieldPressureTemperatureHumidity<>(field, + TroposphericModelUtils.STANDARD_ATMOSPHERE), + zero.newInstance(h)), + null, FieldAbsoluteDate.getJ2000Epoch(field)).getDelay(); + Assertions.assertEquals(delayN, delayT.getReal(), epsilon); + } + } + } + + @Test + public void testNegativeHeight() { + Utils.setDataRoot("atmosphere"); + ModifiedHopfieldModel model = new ModifiedHopfieldModel(); + final double height = -500.0; + for (double elevation = 0; elevation < FastMath.PI; elevation += 0.1) { + Assertions.assertEquals(model.pathDelay(new TrackingCoordinates(0.0, elevation, 0.0), + new GeodeticPoint(0.0, 0.0, 0.0), + TroposphericModelUtils.STANDARD_ATMOSPHERE, + null, AbsoluteDate.J2000_EPOCH).getDelay(), + model.pathDelay(new TrackingCoordinates(0.0, elevation, 0.0), + new GeodeticPoint(0.0, 0.0, height), + TroposphericModelUtils.STANDARD_ATMOSPHERE, + null, AbsoluteDate.J2000_EPOCH).getDelay(), + 1.e-10); + } + } + + @Test + public void testFieldNegativeHeight() { + doTestFieldNegativeHeight(Binary64Field.getInstance()); + } + + private > void doTestFieldNegativeHeight(final Field field) { + final T zero = field.getZero(); + Utils.setDataRoot("atmosphere"); + ModifiedHopfieldModel model = new ModifiedHopfieldModel(); + final T height = zero.subtract(500.0); + for (double elevation = 0; elevation < FastMath.PI; elevation += 0.1) { + Assertions.assertEquals(model.pathDelay(new FieldTrackingCoordinates<>(zero, + zero.newInstance(elevation), + zero), + new FieldGeodeticPoint<>(zero, zero, zero), + new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + null, FieldAbsoluteDate.getJ2000Epoch(field)).getDelay().getReal(), + model.pathDelay(new FieldTrackingCoordinates<>(zero, + zero.newInstance(elevation), + zero), + new FieldGeodeticPoint<>(zero, zero, height), + new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + null, FieldAbsoluteDate.getJ2000Epoch(field)).getDelay().getReal(), + 1.e-10); + } + } + + @Test + public void compareExpectedValues() { + Utils.setDataRoot("atmosphere"); + // the reference values have been computed by our own implementation, + // but checked against the plot in Guochang Xu, GPS - Theory, Algorithms and Applications, Springer, 2007 + // by making a screenshot of figure 5.4 and using it as the background of a gnuplot plot, + // twicking the scales and offset to ensure the elevation scales at 0° and 90° line up + // as well as the delay scale at 0m and 40m + ModifiedHopfieldModel model = new ModifiedHopfieldModel(); + HeightDependentPressureTemperatureHumidityConverter converter = + new HeightDependentPressureTemperatureHumidityConverter(new CIPM2007()); + + for (int h = 0; h < heights.length; h++) { + for (int e = 0; e < elevations.length; e++) { + double height = heights[h]; + double elevation = elevations[e]; + double expectedValue = expectedValues[h][e]; + final GeodeticPoint location = new GeodeticPoint(0.0, 0.0, height); + final AbsoluteDate date = AbsoluteDate.J2000_EPOCH; + double actualValue = model.pathDelay(new TrackingCoordinates(0.0, elevation, 0.0), + location, + converter.convert(TroposphericModelUtils.STANDARD_ATMOSPHERE, height), + null, date).getDelay(); + Assertions.assertEquals(expectedValue, actualValue, epsilon, "For height=" + height + " elevation = " + + FastMath.toDegrees(elevation) + " precision not met"); + } + } + } + + @Test + public void compareFieldExpectedValues() { + doCompareFieldExpectedValues(Binary64Field.getInstance()); + } + + private > void doCompareFieldExpectedValues(final Field field) { + final T zero = field.getZero(); + Utils.setDataRoot("atmosphere"); + // the reference values have been computed by our own implementation, + // but checked against the plot in Guochang Xu, GPS - Theory, Algorithms and Applications, Springer, 2007 + // by making a screenshot of figure 5.4 and using it as the background of a gnuplot plot, + // twicking the scales and offset to ensure the elevation scales at 0° and 90° line up + // as well as the delay scale at 0m and 40m + ModifiedHopfieldModel model = new ModifiedHopfieldModel(); + HeightDependentPressureTemperatureHumidityConverter converter = + new HeightDependentPressureTemperatureHumidityConverter(new CIPM2007()); + + for (int h = 0; h < heights.length; h++) { + for (int e = 0; e < elevations.length; e++) { + T height = zero.newInstance(heights[h]); + T elevation = zero.newInstance(elevations[e]); + double expectedValue = expectedValues[h][e]; + FieldGeodeticPoint location = new FieldGeodeticPoint<>(zero, zero, height); + FieldAbsoluteDate date = FieldAbsoluteDate.getJ2000Epoch(field); + T actualValue = model.pathDelay(new FieldTrackingCoordinates<>(zero, elevation, zero), + location, + converter.convert(new FieldPressureTemperatureHumidity<>(field, + TroposphericModelUtils.STANDARD_ATMOSPHERE), + height), + null, date).getDelay(); + Assertions.assertEquals(expectedValue, actualValue.getReal(), epsilon, "For height=" + height + " elevation = " + + FastMath.toDegrees(elevation.getReal()) + " precision not met"); + } + } + } + + @BeforeEach + public void setUp() throws Exception { + heights = new double[] { + 0.0, 250.0, 500.0, 750.0, 1000.0, 1250.0, 1500.0, 1750.0, 2000.0, 2250.0, 2500.0, 2750.0, 3000.0, 3250.0, + 3500.0, 3750.0, 4000.0, 4250.0, 4500.0, 4750.0, 5000.0 + }; + + elevations = new double[] { + FastMath.toRadians(10.0), FastMath.toRadians(15.0), FastMath.toRadians(20.0), + FastMath.toRadians(25.0), FastMath.toRadians(30.0), FastMath.toRadians(35.0), + FastMath.toRadians(40.0), FastMath.toRadians(45.0), FastMath.toRadians(50.0), + FastMath.toRadians(55.0), FastMath.toRadians(60.0), FastMath.toRadians(65.0), + FastMath.toRadians(70.0), FastMath.toRadians(75.0), FastMath.toRadians(80.0), + FastMath.toRadians(85.0), FastMath.toRadians(90.0) + }; + + expectedValues = new double[][] { + { 12.934879975754736, 8.84084160477251, 6.736022203196621, 5.46898001730666, 4.630745784092203, + 4.041035315294115, 3.608411951861711, 3.2817269682153354, 3.030241489495224, 2.8344622441649374, + 2.6815195803527967, 2.56266799580085, 2.4718587456496492, 2.4048902896027595, 2.358888630010781, + 2.3319868666687267, 2.3231329225099335 }, + { 12.561159287653568, 8.5845772339206, 6.540524901381437, 5.310160285259242, 4.496223800666109, + 3.9236205924564462, 3.503553629933834, 3.1863534038999624, 2.942171043169153, 2.7520781316145233, + 2.6035781540118803, 2.488179277049209, 2.4000082578504416, 2.334985490083572, 2.29032039704188, + 2.2642002736876043, 2.255603586487864 }, + { 12.196271109246876, 8.334396708338184, 6.349676953558442, 5.1551208397950665, 4.364905266082483, + 3.8090027338029726, 3.401193538602676, 3.0932523859733427, 2.8561993139217607, 2.671657358487666, + 2.5274942798666755, 2.415465885996259, 2.3298702638610886, 2.2667468414729446, 2.223386478825016, + 2.1980293767510024, 2.189683818548383 }, + { 11.840056851277243, 8.090190002026562, 6.1633940364070305, 5.003793024325134, 4.236731955221294, + 3.697130880280773, 3.301286235350814, 3.0023825684445966, 2.7722881127132357, 2.593164195299694, + 2.4532341509182074, 2.344495510225485, 2.2614135935595785, 2.2001440162361927, 2.158057126701398, + 2.1334447657599136, 2.1253443200250293 }, + { 11.492359892250795, 7.85184847179859, 5.981592891895236, 4.856109051837414, 4.111646381469352, + 3.5879548184769834, 3.203786854867139, 2.9137031306992824, 2.6903997358985885, 2.516563366789527, + 2.3807643900137903, 2.275236248208002, 2.1946074732175376, 2.135147072538955, 2.09430297036501, + 2.0704174046704082, 2.062556164891846 }, + { 11.153025565763668, 7.619264848064081, 5.804191320080653, 4.7120019989793684, 3.9895917916751586, + 3.4814249761974767, 3.1086511050880046, 2.8271737738921936, 2.6104969618924763, 2.4418200487968793, + 2.310052046890637, 2.2076566064757457, 2.1294215227726028, 2.0717264515927147, 2.0320950152583372, + 2.0089186289188268, 2.0012907971982328 }, + { 10.821901147844857, 7.392333225629629, 5.631108171923848, 4.57140580015122, 3.870512161113126, + 3.377492418053854, 3.0158352632458802, 2.74275471734699, 2.5325430478403774, 2.3688998651454702, + 2.2410645952252195, 2.1417254968001207, 2.0658257531059148, 2.0098529750053538, 1.9714046399731924, + 1.9489201428519454, 1.9415200285087917 }, + { 10.498835844315545, 7.1709490545137635, 5.462263342114509, 4.434255241609992, 3.7543521884571724, + 3.2761088410589285, 2.925296171925621, 2.6604066949628806, 2.456501726296164, 2.2977688845324415, + 2.173769929688361, 2.0774122333761604, 2.0037905633251, 1.9494978421369638, 1.9122035936566382, + 1.8903940171622173, 1.8832160353482224 }, + { 10.183680778166208, 6.955009130777359, 5.297577761910658, 4.30048595558501, 3.641057290764192, + 3.1772265702307525, 2.8369912351285764, 2.5800909516284145, 2.3823372019064784, 2.2283936174238486, + 2.108136363006301, 2.0146865300124164, 1.9432867380526482, 1.8906326274610628, 1.8544639934221097, + 1.8333126863281592, 1.8263513566513527 }, + { 9.8762889769513, 6.744411587369776, 5.136973391991192, 4.17003441440436, 3.5305735984673015, + 3.080798554205539, 2.750878414344576, 2.501769239642544, 2.3100141481017182, 2.1607410129566422, + 2.0441326230277275, 1.9535184973264808, 1.8842854447198623, 1.8332292779310844, 1.798158321765943, + 1.7776489460600666, 1.770898891218422 }, + { 9.576515360202594, 6.539055884991308, 4.980373215321884, 4.042837924633299, 3.42284795037922, + 2.9867783608594287, 2.666916224632045, 2.425403815143106, 2.2394977037939823, 2.0947784558469777, + 1.9817278497969246, 1.89387863994656, 1.8267582308665125, 1.777260110352495, 1.743259423989196, + 1.7233759507509971, 1.7168318951757036 }, + { 9.284216726861493, 6.338842802972065, 4.8277012300356, 3.9188346212243026, 3.317827888706016, + 2.8951201729394875, 2.585063730706338, 2.350957434542875, 2.1707534700819777, 2.0304737633052126, + 1.9208915926332406, 1.835737853718858, 1.7706770214462317, 1.7226978087605191, 1.6897405056251327, + 1.6704672109333, 1.664123979441577 }, + { 8.999251742730987, 6.143674430167964, 4.6788824423264685, 3.797963461679377, 3.2154616540713117, + 2.8057787837040173, 2.505280543036565, 2.278393350973342, 2.103747506963166, 1.9677951819575752, + 1.861593807216871, 1.7790674229211891, 1.7160141161378446, 1.6695154218036077, 1.6375751298722576, + 1.6188965907406763, 1.6127491071981621 }, + { 8.721480927948047, 5.95345415587411, 4.533842859358816, 3.680164220224668, 3.115698180551306, + 2.7187095925724325, 2.4275268139509656, 2.2076753107363376, 2.0384463300531492, 1.9067113847747645, + 1.8038048526812085, 1.723839017482765, 1.6627421866627106, 1.6176863601327756, 1.586737215033144, + 1.568638305375983, 1.5626815913686267 }, + { 8.450766644477026, 5.768086660756171, 4.392509482191019, 3.5653774819978135, 3.0184870907207904, + 2.6338686007848495, 2.3517632337511234, 2.1387675497637897, 1.9748169073126105, 1.8471914680074653, + 1.7474954887117944, 1.6700246902104292, 1.6108342741082338, 1.5671843937969427, 1.5372010319591705, + 1.5196669185848175, 1.513896092100304 }, + { 8.18697308362473, 5.587477907800089, 4.25481029871462, 3.4535446372483163, 2.923778690710489, + 2.5512124070717315, 2.277951026835166, 2.0716347900856587, 1.9128266557818219, 1.789204948129171, + 1.6926368726521528, 1.6175968740213034, 1.5602637862576487, 1.5179836496443941, 1.4889412015012302, + 1.4719573401350756, 1.4663676142537276 }, + { 7.929966253578143, 5.411535133280728, 4.120674276609269, 3.344607875551149, 2.8315239652758417, + 2.470698203333716, 2.206051947830202, 2.006242236306332, 1.8524434383230521, 1.732721758786236, + 1.6392005566165015, 1.5665283791822082, 1.511004494926278, 1.470058608730549, 1.4419326919666187, + 1.4254848233025974, 1.4200715048977355 }, + { 7.6796139669652295, 5.240166837750182, 3.9900313563137013, 3.2385101800340683, 2.7416745728777223, + 2.3922837703318565, 2.136028277734145, 1.94255557208964, 1.7936355603709304, 1.677712247755512, + 1.587158484609674, 1.51679239055582, 1.4630305333043425, 1.4233841037320645, 1.3961508165822356, + 1.3802249623630023, 1.374983450810773 }, + { 7.435785828440213, 5.073282777045855, 3.862812444013299, 3.135195321618908, 2.6541828407752037, + 2.315927473388706, 2.0678428200672396, 1.8805409566527334, 1.7363717666909935, 1.6241471739096365, + 1.536482989654237, 1.4683624648538693, 1.416316393306523, 1.3779353163675905, 1.3515712309641663, + 1.3361536900899693, 1.3310794759885491 }, + { 7.198353222293529, 4.910793953319438, 3.7389494046447243, 3.034607853277241, 2.5690017601307655, + 2.2415882581002715, 2.0014588970334697, 1.820165021268985, 1.6806212381467098, 1.5719977041902182, + 1.4871467909251757, 1.4212125278974417, 1.3708369229284505, 1.3336877748252136, 1.3081699305939638, + 1.2932472752599524, 1.2883359391581959 }, + { 6.967189300087932, 4.75261260608708, 3.618375054918015, 2.936693104300784, 2.486084981128291, + 2.1692256460593353, 1.9368403456921452, 1.7613948657802356, 1.6263535884750422, 1.5212354105891261, + 1.4391229908922085, 1.3753168718846802, 1.3265673236102633, 1.2906173511967862, 1.2659232483016118, + 1.2514823201637046, 1.246729531299119 }, + }; + } + +} diff --git a/src/test/java/org/orekit/models/earth/troposphere/ModifiedSaastamoinenModelTest.java b/src/test/java/org/orekit/models/earth/troposphere/ModifiedSaastamoinenModelTest.java index 02c2008ba0..efc2812bf2 100644 --- a/src/test/java/org/orekit/models/earth/troposphere/ModifiedSaastamoinenModelTest.java +++ b/src/test/java/org/orekit/models/earth/troposphere/ModifiedSaastamoinenModelTest.java @@ -29,8 +29,14 @@ import org.orekit.bodies.GeodeticPoint; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; +import org.orekit.models.earth.weather.ConstantPressureTemperatureHumidityProvider; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; +import org.orekit.models.earth.weather.PressureTemperatureHumidity; +import org.orekit.models.earth.weather.PressureTemperatureHumidityProvider; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldTrackingCoordinates; +import org.orekit.utils.TrackingCoordinates; public class ModifiedSaastamoinenModelTest { @@ -45,13 +51,20 @@ public class ModifiedSaastamoinenModelTest { @Test public void testIssue1078() { + Utils.setDataRoot("atmosphere"); try { - new ModifiedSaastamoinenModel(273.16 + 18, 1013.25, 50.0); - } catch (OrekitException oe) { - Assertions.assertEquals(OrekitMessages.INVALID_PARAMETER_RANGE, oe.getSpecifier()); - } - try { - new ModifiedSaastamoinenModel(273.16 + 18, 1013.25, -50.0); + final double altitude = 0.0; + final double temperature = 273.15 + 18; + final double pressure = TroposphericModelUtils.HECTO_PASCAL.toSI(1013.25); + final double humidity = 50; + final double waterPressure = ModifiedSaastamoinenModel.WATER.waterVaporPressure(pressure, + temperature, + humidity); + final PressureTemperatureHumidity pth = new PressureTemperatureHumidity(altitude, pressure, temperature, waterPressure, + Double.NaN, + Double.NaN); + final PressureTemperatureHumidityProvider pthProvider = new ConstantPressureTemperatureHumidityProvider(pth); + new ModifiedSaastamoinenModel(pthProvider, null); } catch (OrekitException oe) { Assertions.assertEquals(OrekitMessages.INVALID_PARAMETER_RANGE, oe.getSpecifier()); } @@ -64,7 +77,10 @@ public void testFixedElevation() { double lastDelay = Double.MAX_VALUE; // delay shall decline with increasing height of the station for (double height = 0; height < 5000; height += 100) { - final double delay = model.pathDelay(FastMath.toRadians(5), new GeodeticPoint(0.0, 0.0, height), null, AbsoluteDate.J2000_EPOCH); + final double delay = model.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(5), 0.0), + new GeodeticPoint(0.0, 0.0, height), + TroposphericModelUtils.STANDARD_ATMOSPHERE, + null, AbsoluteDate.J2000_EPOCH).getDelay(); Assertions.assertTrue(Precision.compareTo(delay, lastDelay, epsilon) < 0); lastDelay = delay; } @@ -82,7 +98,12 @@ private > void doTestFieldFixedElevation(final T lastDelay = zero.add(Double.MAX_VALUE); // delay shall decline with increasing height of the station for (double height = 0; height < 5000; height += 100) { - final T delay = model.pathDelay(zero.add(FastMath.toRadians(5)), new FieldGeodeticPoint<>(zero, zero, zero.add(height)), null, FieldAbsoluteDate.getJ2000Epoch(field)); + final T delay = model.pathDelay(new FieldTrackingCoordinates<>(zero, + zero.newInstance(FastMath.toRadians(5)), + zero), + new FieldGeodeticPoint<>(zero, zero, zero.add(height)), + new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + null, FieldAbsoluteDate.getJ2000Epoch(field)).getDelay(); Assertions.assertTrue(Precision.compareTo(delay.getReal(), lastDelay.getReal(), epsilon) < 0); lastDelay = delay; } @@ -95,7 +116,10 @@ public void testFixedHeight() { double lastDelay = Double.MAX_VALUE; // delay shall decline with increasing elevation angle for (double elev = 10d; elev < 90d; elev += 8d) { - final double delay = model.pathDelay(FastMath.toRadians(elev), new GeodeticPoint(0.0, 0.0, 350.0), null, AbsoluteDate.J2000_EPOCH); + final double delay = model.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(elev), 0.0), + new GeodeticPoint(0.0, 0.0, 350.0), + TroposphericModelUtils.STANDARD_ATMOSPHERE, + null, AbsoluteDate.J2000_EPOCH).getDelay(); Assertions.assertTrue(Precision.compareTo(delay, lastDelay, epsilon) < 0); lastDelay = delay; } @@ -113,7 +137,12 @@ private > void doTestFieldFixedHeight(final Fi T lastDelay = zero.add(Double.MAX_VALUE); // delay shall decline with increasing elevation angle for (double elev = 10d; elev < 90d; elev += 8d) { - final T delay = model.pathDelay(zero.add(FastMath.toRadians(elev)), new FieldGeodeticPoint<>(zero, zero, zero.add(350.0)), null, FieldAbsoluteDate.getJ2000Epoch(field)); + final T delay = model.pathDelay(new FieldTrackingCoordinates<>(zero, + zero.newInstance(FastMath.toRadians(elev)), + zero), + new FieldGeodeticPoint<>(zero, zero, zero.add(350.0)), + new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + null, FieldAbsoluteDate.getJ2000Epoch(field)).getDelay(); Assertions.assertTrue(Precision.compareTo(delay.getReal(), lastDelay.getReal(), epsilon) < 0); lastDelay = delay; } @@ -123,7 +152,18 @@ private > void doTestFieldFixedHeight(final Fi public void NoFile() { Utils.setDataRoot("atmosphere"); try { - new ModifiedSaastamoinenModel(273.16 + 18, 1013.25, 0.5, "^non-existent-file$"); + final double altitude = 0.0; + final double temperature = 273.15 + 18; + final double pressure = TroposphericModelUtils.HECTO_PASCAL.toSI(1013.25); + final double humidity = 0.5; + final double waterPressure = ModifiedSaastamoinenModel.WATER.waterVaporPressure(pressure, + temperature, + humidity); + final PressureTemperatureHumidity pth = new PressureTemperatureHumidity(altitude, pressure, temperature, waterPressure, + Double.NaN, + Double.NaN); + final PressureTemperatureHumidityProvider pthProvider = new ConstantPressureTemperatureHumidityProvider(pth); + new ModifiedSaastamoinenModel(pthProvider, "^non-existent-file$"); Assertions.fail("an exception should have been thrown"); } catch (OrekitException oe) { Assertions.assertEquals(OrekitMessages.UNABLE_TO_FIND_FILE, oe.getSpecifier()); @@ -134,8 +174,19 @@ public void NoFile() { @Test public void compareDefaultAndLoaded() { Utils.setDataRoot("atmosphere"); - ModifiedSaastamoinenModel defaultModel = new ModifiedSaastamoinenModel(273.16 + 18, 1013.25, 0.5, null); - ModifiedSaastamoinenModel loadedModel = new ModifiedSaastamoinenModel(273.16 + 18, 1013.25, 0.5, ModifiedSaastamoinenModel.DELTA_R_FILE_NAME); + final double altitude = 0.0; + final double temperature = 273.15 + 18; + final double pressure = TroposphericModelUtils.HECTO_PASCAL.toSI(1013.25); + final double humidity = 0.5; + final double waterPressure = ModifiedSaastamoinenModel.WATER.waterVaporPressure(pressure, + temperature, + humidity); + final PressureTemperatureHumidity pth = new PressureTemperatureHumidity(altitude, pressure, temperature, waterPressure, + Double.NaN, + Double.NaN); + final PressureTemperatureHumidityProvider pthProvider = new ConstantPressureTemperatureHumidityProvider(pth); + ModifiedSaastamoinenModel defaultModel = new ModifiedSaastamoinenModel(pthProvider, null); + ModifiedSaastamoinenModel loadedModel = new ModifiedSaastamoinenModel(pthProvider, ModifiedSaastamoinenModel.DELTA_R_FILE_NAME); double[] heights = new double[] { 0.0, 250.0, 500.0, 750.0, 1000.0, 1250.0, 1500.0, 1750.0, 2000.0, 2250.0, 2500.0, 2750.0, 3000.0, 3250.0, 3500.0, 3750.0, 4000.0, 4250.0, 4500.0, 4750.0, 5000.0 @@ -148,14 +199,18 @@ public void compareDefaultAndLoaded() { FastMath.toRadians(70.0), FastMath.toRadians(75.0), FastMath.toRadians(80.0), FastMath.toRadians(85.0), FastMath.toRadians(90.0) }; - for (int h = 0; h < heights.length; h++) { - for (int e = 0; e < elevations.length; e++) { - double height = heights[h]; - double elevation = elevations[e]; - double expectedValue = defaultModel.pathDelay(elevation, new GeodeticPoint(0.0, 0.0, height), null, AbsoluteDate.J2000_EPOCH); - double actualValue = loadedModel.pathDelay(elevation, new GeodeticPoint(0.0, 0.0, height), null, AbsoluteDate.J2000_EPOCH); - Assertions.assertEquals(expectedValue, actualValue, epsilon, "For height=" + height + " elevation = " + - FastMath.toDegrees(elevation) + " precision not met"); + for (double v : heights) { + for (double value : elevations) { + double expectedValue = defaultModel.pathDelay(new TrackingCoordinates(0.0, value, 0.0), + new GeodeticPoint(0.0, 0.0, v), + TroposphericModelUtils.STANDARD_ATMOSPHERE, + null, AbsoluteDate.J2000_EPOCH).getDelay(); + double actualValue = loadedModel.pathDelay(new TrackingCoordinates(0.0, value, 0.0), + new GeodeticPoint(0.0, 0.0, v), + TroposphericModelUtils.STANDARD_ATMOSPHERE, + null, AbsoluteDate.J2000_EPOCH).getDelay(); + Assertions.assertEquals(expectedValue, actualValue, epsilon, "For height=" + v + " elevation = " + + FastMath.toDegrees(value) + " precision not met"); } } } @@ -166,8 +221,14 @@ public void testNegativeHeight() { ModifiedSaastamoinenModel model = ModifiedSaastamoinenModel.getStandardModel(); final double height = -500.0; for (double elevation = 0; elevation < FastMath.PI; elevation += 0.1) { - Assertions.assertEquals(model.pathDelay(elevation, new GeodeticPoint(0.0, 0.0, 0.0), null, AbsoluteDate.J2000_EPOCH), - model.pathDelay(elevation, new GeodeticPoint(0.0, 0.0, height), null, AbsoluteDate.J2000_EPOCH), + Assertions.assertEquals(model.pathDelay(new TrackingCoordinates(0.0, elevation, 0.0), + new GeodeticPoint(0.0, 0.0, 0.0), + TroposphericModelUtils.STANDARD_ATMOSPHERE, + null, AbsoluteDate.J2000_EPOCH).getDelay(), + model.pathDelay(new TrackingCoordinates(0.0, elevation, 0.0), + new GeodeticPoint(0.0, 0.0, height), + TroposphericModelUtils.STANDARD_ATMOSPHERE, + null, AbsoluteDate.J2000_EPOCH).getDelay(), 1.e-10); } } @@ -183,8 +244,18 @@ private > void doTestFieldNegativeHeight(final ModifiedSaastamoinenModel model = ModifiedSaastamoinenModel.getStandardModel(); final T height = zero.subtract(500.0); for (double elevation = 0; elevation < FastMath.PI; elevation += 0.1) { - Assertions.assertEquals(model.pathDelay(zero.add(elevation), new FieldGeodeticPoint<>(zero, zero, zero), null, FieldAbsoluteDate.getJ2000Epoch(field)).getReal(), - model.pathDelay(zero.add(elevation), new FieldGeodeticPoint<>(zero, zero, zero.add(height)), null, FieldAbsoluteDate.getJ2000Epoch(field)).getReal(), + Assertions.assertEquals(model.pathDelay(new FieldTrackingCoordinates<>(zero, + zero.newInstance(elevation), + zero), + new FieldGeodeticPoint<>(zero, zero, zero), + new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + null, FieldAbsoluteDate.getJ2000Epoch(field)).getDelay().getReal(), + model.pathDelay(new FieldTrackingCoordinates<>(zero, + zero.newInstance(elevation), + zero), + new FieldGeodeticPoint<>(zero, zero, zero.add(height)), + new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + null, FieldAbsoluteDate.getJ2000Epoch(field)).getDelay().getReal(), 1.e-10); } } @@ -200,10 +271,17 @@ public void testIssue654LowElevation() { // Reset to default value model.setLowElevationThreshold(ModifiedSaastamoinenModel.DEFAULT_LOW_ELEVATION_THRESHOLD); - double lowElevationPathDelay = model.pathDelay(0.001, new GeodeticPoint(0.0, 0.0, 0.0), null, AbsoluteDate.J2000_EPOCH); + double lowElevationPathDelay = model.pathDelay(new TrackingCoordinates(0.0, 0.001, 0.0), + new GeodeticPoint(0.0, 0.0, 0.0), + TroposphericModelUtils.STANDARD_ATMOSPHERE, + null, AbsoluteDate.J2000_EPOCH).getDelay(); Assertions.assertTrue(lowElevationPathDelay > 0.); - Assertions.assertEquals(model.pathDelay(model.getLowElevationThreshold(), new GeodeticPoint(0.0, 0.0, 0.0), null, AbsoluteDate.J2000_EPOCH), - lowElevationPathDelay, 1.e-10); + Assertions.assertEquals(model.pathDelay(new TrackingCoordinates(0.0, model.getLowElevationThreshold(), 0.0), + new GeodeticPoint(0.0, 0.0, 0.0), + TroposphericModelUtils.STANDARD_ATMOSPHERE, + null, AbsoluteDate.J2000_EPOCH).getDelay(), + lowElevationPathDelay, + 1.e-10); } @Test @@ -214,10 +292,16 @@ private > void doTestFieldLowElevation(final F Utils.setDataRoot("atmosphere"); ModifiedSaastamoinenModel model = ModifiedSaastamoinenModel.getStandardModel(); final T elevation = zero.add(0.001); - double lowElevationPathDelay = model.pathDelay(zero.add(elevation), new FieldGeodeticPoint<>(zero, zero, zero), null, - FieldAbsoluteDate.getJ2000Epoch(field)).getReal(); - double thresholdElevationPathDelay = model.pathDelay(zero.add(model.getLowElevationThreshold()), new FieldGeodeticPoint<>(zero, zero, zero), - null, FieldAbsoluteDate.getJ2000Epoch(field)).getReal(); + double lowElevationPathDelay = model.pathDelay(new FieldTrackingCoordinates<>(zero, elevation, zero), + new FieldGeodeticPoint<>(zero, zero, zero), + new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + null, FieldAbsoluteDate.getJ2000Epoch(field)).getDelay().getReal(); + double thresholdElevationPathDelay = model.pathDelay(new FieldTrackingCoordinates<>(zero, + zero.newInstance(model.getLowElevationThreshold()), + zero), + new FieldGeodeticPoint<>(zero, zero, zero), + new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + null, FieldAbsoluteDate.getJ2000Epoch(field)).getDelay().getReal(); Assertions.assertTrue(lowElevationPathDelay > 0.); Assertions.assertEquals(thresholdElevationPathDelay, lowElevationPathDelay, 1.e-10); } @@ -225,14 +309,37 @@ private > void doTestFieldLowElevation(final F @Test public void compareExpectedValues() { Utils.setDataRoot("atmosphere"); - ModifiedSaastamoinenModel model = ModifiedSaastamoinenModel.getStandardModel(); + final double pressure = TroposphericModelUtils.HECTO_PASCAL.toSI(1013.25); + // it seems the reference values for the test have been computed using a wrong conversion + // between Celsius and Kelvin (273.16 offset instead of 273.15) + // which is probably due to a similar error in equation 5.97 of + // Guochang Xu, GPS - Theory, Algorithms and Applications, Springer, 2007 + // so for the sake of the test, we use a temperature of 18.01°C and not the standard atmosphere at 18.00°C + final double altitude = 0.0; + final double temperature = 273.15 + 18.01; + final double humidity = 0.5; + final PressureTemperatureHumidity pth = new PressureTemperatureHumidity(altitude, + pressure, + temperature, + ModifiedSaastamoinenModel.WATER.waterVaporPressure(pressure, + temperature, + humidity), + Double.NaN, + Double.NaN); + final PressureTemperatureHumidityProvider pth0Provider = new ConstantPressureTemperatureHumidityProvider(pth); + ModifiedSaastamoinenModel model = new ModifiedSaastamoinenModel(pth0Provider); for (int h = 0; h < heights.length; h++) { for (int e = 0; e < elevations.length; e++) { double height = heights[h]; double elevation = elevations[e]; double expectedValue = expectedValues[h][e]; - double actualValue = model.pathDelay(elevation, new GeodeticPoint(0.0, 0.0, height), null, AbsoluteDate.J2000_EPOCH); + final GeodeticPoint location = new GeodeticPoint(0.0, 0.0, height); + final AbsoluteDate date = AbsoluteDate.J2000_EPOCH; + double actualValue = model.pathDelay(new TrackingCoordinates(0.0, elevation, 0.0), + location, + pth0Provider.getWeatherParamerers(location, date), + null, date).getDelay(); Assertions.assertEquals(expectedValue, actualValue, epsilon, "For height=" + height + " elevation = " + FastMath.toDegrees(elevation) + " precision not met"); } @@ -247,14 +354,35 @@ public void compareFieldExpectedValues() { private > void doCompareFieldExpectedValues(final Field field) { final T zero = field.getZero(); Utils.setDataRoot("atmosphere"); - ModifiedSaastamoinenModel model = ModifiedSaastamoinenModel.getStandardModel(); + final double pressure = TroposphericModelUtils.HECTO_PASCAL.toSI(1013.25); + // it seems the reference values for the test have been computed using a wrong conversion + // between Celsius and Kelvin (273.16 offset instead of 273.15) + // so for the sake of the test, we use a temperature of 18.01°C and not the standard atmosphere at 18.00°C + final double altitude = 0.0; + final double temperature = 273.15 + 18.01; + final double humidity = 0.5; + final PressureTemperatureHumidity pth = new PressureTemperatureHumidity(altitude, + pressure, + temperature, + ModifiedSaastamoinenModel.WATER.waterVaporPressure(pressure, + temperature, + humidity), + Double.NaN, + Double.NaN); + final PressureTemperatureHumidityProvider pth0Provider = new ConstantPressureTemperatureHumidityProvider(pth); + ModifiedSaastamoinenModel model = new ModifiedSaastamoinenModel(pth0Provider); for (int h = 0; h < heights.length; h++) { for (int e = 0; e < elevations.length; e++) { T height = zero.add(heights[h]); T elevation = zero.add(elevations[e]); double expectedValue = expectedValues[h][e]; - T actualValue = model.pathDelay(elevation, new FieldGeodeticPoint<>(zero, zero, zero.add(height)), null, FieldAbsoluteDate.getJ2000Epoch(field)); + FieldGeodeticPoint location = new FieldGeodeticPoint<>(zero, zero, zero.add(height)); + FieldAbsoluteDate date = FieldAbsoluteDate.getJ2000Epoch(field); + T actualValue = model.pathDelay(new FieldTrackingCoordinates<>(zero, elevation, zero), + location, + pth0Provider.getWeatherParamerers(location, date), + null, date).getDelay(); Assertions.assertEquals(expectedValue, actualValue.getReal(), epsilon, "For height=" + height + " elevation = " + FastMath.toDegrees(elevation.getReal()) + " precision not met"); } @@ -267,7 +395,15 @@ public void testIssue572() { ModifiedSaastamoinenModel model = ModifiedSaastamoinenModel.getStandardModel(); final double height = 6000.0; for (double elevation = 0; elevation < FastMath.PI; elevation += 0.1) { - Assertions.assertEquals(model.pathDelay(elevation, new GeodeticPoint(0.0, 0.0, 5000.0), null, AbsoluteDate.J2000_EPOCH), model.pathDelay(elevation, new GeodeticPoint(0.0, 0.0, height), null, AbsoluteDate.J2000_EPOCH), 1.e-10); + Assertions.assertEquals(model.pathDelay(new TrackingCoordinates(0.0, elevation, 0.0), + new GeodeticPoint(0.0, 0.0, 5000.0), + TroposphericModelUtils.STANDARD_ATMOSPHERE, + null, AbsoluteDate.J2000_EPOCH).getDelay(), + model.pathDelay(new TrackingCoordinates(0.0, elevation, 0.0), + new GeodeticPoint(0.0, 0.0, height), + TroposphericModelUtils.STANDARD_ATMOSPHERE, + null, AbsoluteDate.J2000_EPOCH).getDelay(), + 1.e-10); } } @@ -282,9 +418,19 @@ private > void doTestFieldIssue572(final Field ModifiedSaastamoinenModel model = ModifiedSaastamoinenModel.getStandardModel(); final T height = zero.add(6000.0); for (double elevation = 0; elevation < FastMath.PI; elevation += 0.1) { - Assertions.assertEquals(model.pathDelay(zero.add(elevation),new FieldGeodeticPoint<>(zero, zero, zero.add(5000.0)), null, FieldAbsoluteDate.getJ2000Epoch(field)).getReal(), - model.pathDelay(zero.add(elevation), new FieldGeodeticPoint<>(zero, zero, zero.add(height)), null, FieldAbsoluteDate.getJ2000Epoch(field)).getReal(), - 1.e-10); + Assertions.assertEquals(model.pathDelay(new FieldTrackingCoordinates<>(zero, + zero.newInstance(elevation), + zero), + new FieldGeodeticPoint<>(zero, zero, zero.add(5000.0)), + new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + null, FieldAbsoluteDate.getJ2000Epoch(field)).getDelay().getReal(), + model.pathDelay(new FieldTrackingCoordinates<>(zero, + zero.newInstance(elevation), + zero), + new FieldGeodeticPoint<>(zero, zero, zero.add(height)), + new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + null, FieldAbsoluteDate.getJ2000Epoch(field)).getDelay().getReal(), + 1.e-10); } } diff --git a/src/test/java/org/orekit/models/earth/troposphere/NiellMappingFunctionModelTest.java b/src/test/java/org/orekit/models/earth/troposphere/NiellMappingFunctionModelTest.java index 08524d9a37..ed81d1e11b 100644 --- a/src/test/java/org/orekit/models/earth/troposphere/NiellMappingFunctionModelTest.java +++ b/src/test/java/org/orekit/models/earth/troposphere/NiellMappingFunctionModelTest.java @@ -16,80 +16,17 @@ */ package org.orekit.models.earth.troposphere; -import org.hipparchus.util.FastMath; -import org.hipparchus.util.Precision; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.orekit.Utils; -import org.orekit.bodies.GeodeticPoint; -import org.orekit.errors.OrekitException; -import org.orekit.time.AbsoluteDate; -import org.orekit.time.TimeScalesFactory; -public class NiellMappingFunctionModelTest { +public class NiellMappingFunctionModelTest extends AbstractMappingFunctionTest { - @BeforeAll - public static void setUpGlobal() { - Utils.setDataRoot("atmosphere"); - } - - @BeforeEach - public void setUp() throws OrekitException { - Utils.setDataRoot("regular-data:potential/shm-format"); + protected TroposphereMappingFunction buildMappingFunction() { + return new NiellMappingFunctionModel(); } @Test public void testMappingFactors() { - - // Site (Le Mans, France): latitude: 48.0° - // longitude: 0.20° - // height: 68 m - // - // Date: 1st January 1994 at 0h UT - // - // Ref: Mercier F., Perosanz F., Mesures GNSS, Résolution des ambiguités. - // - // Expected mapping factors : hydrostatic -> 10.16 (Ref) - // wet -> 10.75 (Ref) - - final AbsoluteDate date = new AbsoluteDate(1994, 1, 1, TimeScalesFactory.getUTC()); - - final double latitude = FastMath.toRadians(48.0); - final double longitude = FastMath.toRadians(0.20); - final double height = 68.0; - final GeodeticPoint point = new GeodeticPoint(latitude, longitude, height); - - final double elevation = FastMath.toRadians(5.0); - final double expectedHydro = 10.16; - final double expectedWet = 10.75; - - final MappingFunction model = new NiellMappingFunctionModel(); - - final double[] computedMapping = model.mappingFactors(elevation, point, date); - - Assertions.assertEquals(expectedHydro, computedMapping[0], 1.0e-2); - Assertions.assertEquals(expectedWet, computedMapping[1], 1.0e-2); - } - - @Test - public void testFixedHeight() { - final AbsoluteDate date = new AbsoluteDate(); - final GeodeticPoint point = new GeodeticPoint(FastMath.toRadians(45.0), FastMath.toRadians(45.0), 350.0); - MappingFunction model = new NiellMappingFunctionModel(); - double[] lastFactors = new double[] { - Double.MAX_VALUE, - Double.MAX_VALUE - }; - // mapping functions shall decline with increasing elevation angle - for (double elev = 10d; elev < 90d; elev += 8d) { - final double[] factors = model.mappingFactors(FastMath.toRadians(elev), point, date); - Assertions.assertTrue(Precision.compareTo(factors[0], lastFactors[0], 1.0e-6) < 0); - Assertions.assertTrue(Precision.compareTo(factors[1], lastFactors[1], 1.0e-6) < 0); - lastFactors[0] = factors[0]; - lastFactors[1] = factors[1]; - } + doTestMappingFactors(10.16, 10.75); } } diff --git a/src/test/java/org/orekit/models/earth/troposphere/RevisedChaoMappingFunctionTest.java b/src/test/java/org/orekit/models/earth/troposphere/RevisedChaoMappingFunctionTest.java new file mode 100644 index 0000000000..6e37a3a6bc --- /dev/null +++ b/src/test/java/org/orekit/models/earth/troposphere/RevisedChaoMappingFunctionTest.java @@ -0,0 +1,32 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.troposphere; + +import org.junit.jupiter.api.Test; + +public class RevisedChaoMappingFunctionTest extends AbstractMappingFunctionTest { + + protected TroposphereMappingFunction buildMappingFunction() { + return new RevisedChaoMappingFunction(); + } + + @Test + public void testMappingFactors() { + doTestMappingFactors(10.13, 11.05); + } + +} diff --git a/src/test/java/org/orekit/models/earth/troposphere/SaastamoinenModelTest.java b/src/test/java/org/orekit/models/earth/troposphere/SaastamoinenModelTest.java index e02b8b1c77..0ecf1f2927 100644 --- a/src/test/java/org/orekit/models/earth/troposphere/SaastamoinenModelTest.java +++ b/src/test/java/org/orekit/models/earth/troposphere/SaastamoinenModelTest.java @@ -31,11 +31,13 @@ import org.orekit.errors.OrekitMessages; import org.orekit.time.AbsoluteDate; import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldTrackingCoordinates; +import org.orekit.utils.TrackingCoordinates; @Deprecated public class SaastamoinenModelTest { - private static double epsilon = 1e-6; + private static final double epsilon = 1e-6; private double[][] expectedValues; @@ -61,12 +63,21 @@ public void testIssue1078() { public void testFixedElevation() { Utils.setDataRoot("atmosphere"); SaastamoinenModel model = SaastamoinenModel.getStandardModel(); + TroposphericModel adapted = new TroposphericModelAdapter(model); + Assertions.assertTrue(adapted.getParametersDrivers().isEmpty()); double lastDelay = Double.MAX_VALUE; // delay shall decline with increasing height of the station for (double height = 0; height < 5000; height += 100) { - final double delay = model.pathDelay(FastMath.toRadians(5), new GeodeticPoint(0.0, 0.0, height), null, AbsoluteDate.J2000_EPOCH); + final double elevation = FastMath.toRadians(5); + final GeodeticPoint point = new GeodeticPoint(0.0, 0.0, height); + final AbsoluteDate date = AbsoluteDate.J2000_EPOCH; + final double delay = model.pathDelay(elevation, point, null, date); Assertions.assertTrue(Precision.compareTo(delay, lastDelay, epsilon) < 0); lastDelay = delay; + Assertions.assertEquals(delay, + adapted.pathDelay(new TrackingCoordinates(0.0, elevation, 0.0), + point, null, null, date).getDelay(), + 1.0e-10); } } @@ -79,12 +90,20 @@ private > void doTestFieldFixedElevation(final final T zero = field.getZero(); Utils.setDataRoot("atmosphere"); SaastamoinenModel model = SaastamoinenModel.getStandardModel(); + TroposphericModel adapted = new TroposphericModelAdapter(model); T lastDelay = zero.add(Double.MAX_VALUE); // delay shall decline with increasing height of the station for (double height = 0; height < 5000; height += 100) { - final T delay = model.pathDelay(zero.add(FastMath.toRadians(5)), new FieldGeodeticPoint<>(zero, zero, zero.add(height)), null, FieldAbsoluteDate.getJ2000Epoch(field)); + final T elevation = zero.newInstance(FastMath.toRadians(5)); + final FieldGeodeticPoint point = new FieldGeodeticPoint<>(zero, zero, zero.newInstance(height)); + final FieldAbsoluteDate date = FieldAbsoluteDate.getJ2000Epoch(field); + final T delay = model.pathDelay(elevation, point, null, date); Assertions.assertTrue(Precision.compareTo(delay.getReal(), lastDelay.getReal(), epsilon) < 0); lastDelay = delay; + Assertions.assertEquals(delay.getReal(), + adapted.pathDelay(new FieldTrackingCoordinates<>(zero, elevation, zero), + point, null, null, date).getDelay().getReal(), + 1.0e-10); } } @@ -148,10 +167,8 @@ public void compareDefaultAndLoaded() { FastMath.toRadians(70.0), FastMath.toRadians(75.0), FastMath.toRadians(80.0), FastMath.toRadians(85.0), FastMath.toRadians(90.0) }; - for (int h = 0; h < heights.length; h++) { - for (int e = 0; e < elevations.length; e++) { - double height = heights[h]; - double elevation = elevations[e]; + for (double height : heights) { + for (double elevation : elevations) { double expectedValue = defaultModel.pathDelay(elevation, new GeodeticPoint(0.0, 0.0, height), null, AbsoluteDate.J2000_EPOCH); double actualValue = loadedModel.pathDelay(elevation, new GeodeticPoint(0.0, 0.0, height), null, AbsoluteDate.J2000_EPOCH); Assertions.assertEquals(expectedValue, actualValue, epsilon, "For height=" + height + " elevation = " + @@ -232,7 +249,9 @@ public void compareExpectedValues() { double height = heights[h]; double elevation = elevations[e]; double expectedValue = expectedValues[h][e]; - double actualValue = model.pathDelay(elevation, new GeodeticPoint(0.0, 0.0, height), null, AbsoluteDate.J2000_EPOCH); + final GeodeticPoint location = new GeodeticPoint(0.0, 0.0, height); + final AbsoluteDate date = AbsoluteDate.J2000_EPOCH; + double actualValue = model.pathDelay(elevation, location, null, date); Assertions.assertEquals(expectedValue, actualValue, epsilon, "For height=" + height + " elevation = " + FastMath.toDegrees(elevation) + " precision not met"); } @@ -254,7 +273,9 @@ private > void doCompareFieldExpectedValues(fi T height = zero.add(heights[h]); T elevation = zero.add(elevations[e]); double expectedValue = expectedValues[h][e]; - T actualValue = model.pathDelay(elevation, new FieldGeodeticPoint<>(zero, zero, zero.add(height)), null, FieldAbsoluteDate.getJ2000Epoch(field)); + FieldGeodeticPoint location = new FieldGeodeticPoint<>(zero, zero, zero.add(height)); + FieldAbsoluteDate date = FieldAbsoluteDate.getJ2000Epoch(field); + T actualValue = model.pathDelay(elevation, location, null, date); Assertions.assertEquals(expectedValue, actualValue.getReal(), epsilon, "For height=" + height + " elevation = " + FastMath.toDegrees(elevation.getReal()) + " precision not met"); } diff --git a/src/test/java/org/orekit/models/earth/troposphere/TimeSpanEstimatedModelTest.java b/src/test/java/org/orekit/models/earth/troposphere/TimeSpanEstimatedModelTest.java new file mode 100644 index 0000000000..7c5ddeb15c --- /dev/null +++ b/src/test/java/org/orekit/models/earth/troposphere/TimeSpanEstimatedModelTest.java @@ -0,0 +1,506 @@ +/* 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.models.earth.troposphere; + +import java.util.List; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.analysis.differentiation.DSFactory; +import org.hipparchus.analysis.differentiation.DerivativeStructure; +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.util.Binary64Field; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.Precision; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.orekit.Utils; +import org.orekit.attitudes.Attitude; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.bodies.OneAxisEllipsoid; +import org.orekit.errors.OrekitException; +import org.orekit.estimation.measurements.GroundStation; +import org.orekit.frames.Frame; +import org.orekit.frames.FramesFactory; +import org.orekit.frames.TopocentricFrame; +import org.orekit.models.earth.weather.FieldPressureTemperatureHumidity; +import org.orekit.orbits.FieldKeplerianOrbit; +import org.orekit.orbits.FieldOrbit; +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.numerical.NumericalPropagator; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.TimeScalesFactory; +import org.orekit.utils.Constants; +import org.orekit.utils.FieldTrackingCoordinates; +import org.orekit.utils.IERSConventions; +import org.orekit.utils.ParameterDriver; +import org.orekit.utils.ParameterDriversList; +import org.orekit.utils.TrackingCoordinates; + +public class TimeSpanEstimatedModelTest { + + @BeforeAll + public static void setUpGlobal() { + Utils.setDataRoot("atmosphere"); + } + + @BeforeEach + public void setUp() throws OrekitException { + Utils.setDataRoot("regular-data:potential/shm-format"); + } + + @Test + public void testFixedHeight() { + final AbsoluteDate date = new AbsoluteDate(); + GeodeticPoint point = new GeodeticPoint(FastMath.toRadians(45.0), FastMath.toRadians(45.0), 350.0); + TroposphereMappingFunction mapping = new NiellMappingFunctionModel(); + EstimatedModel model = new EstimatedModel(mapping, 2.0); + TroposphericModel timeSpanModel = new TimeSpanEstimatedModel(model); + double lastDelay = Double.MAX_VALUE; + // delay shall decline with increasing elevation angle + for (double elev = 10d; elev < 90d; elev += 8d) { + final double delay = timeSpanModel.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(elev), 0.0), + point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + timeSpanModel.getParameters(), date).getDelay(); + Assertions.assertTrue(Precision.compareTo(delay, lastDelay, 1.0e-6) < 0); + lastDelay = delay; + } + } + + @Test + public void testDelay() { + final double elevation = 10d; + final double height = 100d; + final AbsoluteDate date = new AbsoluteDate(); + GeodeticPoint point = new GeodeticPoint(FastMath.toRadians(45.0), FastMath.toRadians(45.0), height); + TroposphereMappingFunction mapping = new NiellMappingFunctionModel(); + EstimatedModel model = new EstimatedModel(mapping, 2.0); + TroposphericModel timeSpanModel = new TimeSpanEstimatedModel(model); + final double path = timeSpanModel.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(elevation), 0.0), + point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + timeSpanModel.getParameters(), date).getDelay(); + Assertions.assertTrue(Precision.compareTo(path, 20d, 1.0e-6) < 0); + Assertions.assertTrue(Precision.compareTo(path, 0d, 1.0e-6) > 0); + } + + @Test + public void testStateDerivativesGMF() { + final double latitude = FastMath.toRadians(45.0); + final double longitude = FastMath.toRadians(45.0); + GeodeticPoint point = new GeodeticPoint(latitude, longitude, 0.0); + final TroposphereMappingFunction gmf = new GlobalMappingFunctionModel(); + doTestDelayStateDerivatives(gmf, point, 4.7e-9); + } + + @Test + public void testStateDerivativesNMF() { + final double latitude = FastMath.toRadians(45.0); + final double longitude = FastMath.toRadians(45.0); + GeodeticPoint point = new GeodeticPoint(latitude, longitude, 0.0); + final TroposphereMappingFunction nmf = new NiellMappingFunctionModel(); + doTestDelayStateDerivatives(nmf, point, 4.4e-9); + } + + private void doTestDelayStateDerivatives(final TroposphereMappingFunction func, final GeodeticPoint point, final double tolerance) { + + // Body: earth + final OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, + Constants.WGS84_EARTH_FLATTENING, + FramesFactory.getITRF(IERSConventions.IERS_2010, true)); + // Topocentric frame + final TopocentricFrame baseFrame = new TopocentricFrame(earth, point, "topo"); + + // Station + final GroundStation station = new GroundStation(baseFrame); + + // Tropospheric model + EstimatedModel model = new EstimatedModel(func, 2.0); + TroposphericModel timeSpanModel = new TimeSpanEstimatedModel(model); + + // Derivative Structure + final DSFactory factory = new DSFactory(6, 1); + final DerivativeStructure a0 = factory.variable(0, 24464560.0); + final DerivativeStructure e0 = factory.variable(1, 0.05); + final DerivativeStructure i0 = factory.variable(2, 0.122138); + final DerivativeStructure pa0 = factory.variable(3, 3.10686); + final DerivativeStructure raan0 = factory.variable(4, 1.00681); + final DerivativeStructure anomaly0 = factory.variable(5, 0.048363); + final Field field = a0.getField(); + final DerivativeStructure zero = field.getZero(); + + // Field Date + final FieldAbsoluteDate dsDate = new FieldAbsoluteDate<>(field); + // Field Orbit + final Frame frame = FramesFactory.getEME2000(); + final FieldOrbit dsOrbit = new FieldKeplerianOrbit<>(a0, e0, i0, pa0, raan0, anomaly0, + PositionAngleType.MEAN, frame, + dsDate, zero.add(3.9860047e14)); + // Field State + final FieldSpacecraftState dsState = new FieldSpacecraftState<>(dsOrbit); + + // Initial satellite elevation + final FieldVector3D position = dsState.getPosition(); + final FieldTrackingCoordinates dsTrackingCoordinates = + baseFrame.getTrackingCoordinates(position, frame, dsDate); + + // Set drivers reference date + for (final ParameterDriver driver : timeSpanModel.getParametersDrivers()) { + driver.setReferenceDate(dsDate.toAbsoluteDate()); + } + + // Compute Delay with state derivatives + final FieldGeodeticPoint dsPoint = new FieldGeodeticPoint<>(zero.add(point.getLatitude()), zero.add(point.getLongitude()), zero.add(point.getAltitude())); + final DerivativeStructure delay = timeSpanModel.pathDelay(dsTrackingCoordinates, dsPoint, + new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + timeSpanModel.getParameters(field), dsDate).getDelay(); + + final double[] compDeriv = delay.getAllDerivatives(); + + // Field -> non-field + final Orbit orbit = dsOrbit.toOrbit(); + final SpacecraftState state = dsState.toSpacecraftState(); + + // Finite differences for reference values + final double[][] refDeriv = new double[1][6]; + final OrbitType orbitType = OrbitType.KEPLERIAN; + final PositionAngleType angleType = PositionAngleType.MEAN; + double dP = 0.001; + double[] steps = NumericalPropagator.tolerances(1000000 * dP, orbit, orbitType)[0]; + for (int i = 0; i < 6; i++) { + SpacecraftState stateM4 = shiftState(state, orbitType, angleType, -4 * steps[i], i); + final Vector3D positionM4 = stateM4.getPosition(); + final TrackingCoordinates trackingCoordinatesM4 = station.getBaseFrame(). + getTrackingCoordinates(positionM4, stateM4.getFrame(), stateM4.getDate()); + double delayM4 = timeSpanModel.pathDelay(trackingCoordinatesM4, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + timeSpanModel.getParameters(), stateM4.getDate()).getDelay(); + + SpacecraftState stateM3 = shiftState(state, orbitType, angleType, -3 * steps[i], i); + final Vector3D positionM3 = stateM3.getPosition(); + final TrackingCoordinates trackingCoordinatesM3 = station.getBaseFrame(). + getTrackingCoordinates(positionM3, stateM3.getFrame(), stateM3.getDate()); + double delayM3 = timeSpanModel.pathDelay(trackingCoordinatesM3, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + timeSpanModel.getParameters(), stateM3.getDate()).getDelay(); + + SpacecraftState stateM2 = shiftState(state, orbitType, angleType, -2 * steps[i], i); + final Vector3D positionM2 = stateM2.getPosition(); + final TrackingCoordinates trackingCoordinatesM2 = station.getBaseFrame(). + getTrackingCoordinates(positionM2, stateM2.getFrame(), stateM2.getDate()); + double delayM2 = timeSpanModel.pathDelay(trackingCoordinatesM2, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + timeSpanModel.getParameters(), stateM2.getDate()).getDelay(); + + SpacecraftState stateM1 = shiftState(state, orbitType, angleType, -1 * steps[i], i); + final Vector3D positionM1 = stateM1.getPosition(); + final TrackingCoordinates trackingCoordinatesM1 = station.getBaseFrame(). + getTrackingCoordinates(positionM1, stateM1.getFrame(), stateM1.getDate()); + double delayM1 = timeSpanModel.pathDelay(trackingCoordinatesM1, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + timeSpanModel.getParameters(), stateM1.getDate()).getDelay(); + + SpacecraftState stateP1 = shiftState(state, orbitType, angleType, 1 * steps[i], i); + final Vector3D positionP1 = stateP1.getPosition(); + final TrackingCoordinates trackingCoordinatesP1 = station.getBaseFrame(). + getTrackingCoordinates(positionP1, stateP1.getFrame(), stateP1.getDate()); + double delayP1 = timeSpanModel.pathDelay(trackingCoordinatesP1, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + timeSpanModel.getParameters(), stateP1.getDate()).getDelay(); + + SpacecraftState stateP2 = shiftState(state, orbitType, angleType, 2 * steps[i], i); + final Vector3D positionP2 = stateP2.getPosition(); + final TrackingCoordinates trackingCoordinatesP2 = station.getBaseFrame(). + getTrackingCoordinates(positionP2, stateP2.getFrame(), stateP2.getDate()); + double delayP2 = timeSpanModel.pathDelay(trackingCoordinatesP2, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + timeSpanModel.getParameters(), stateP2.getDate()).getDelay(); + + SpacecraftState stateP3 = shiftState(state, orbitType, angleType, 3 * steps[i], i); + final Vector3D positionP3 = stateP3.getPosition(); + final TrackingCoordinates trackingCoordinatesP3 = station.getBaseFrame(). + getTrackingCoordinates(positionP3, stateP3.getFrame(), stateP3.getDate()); + double delayP3 = timeSpanModel.pathDelay(trackingCoordinatesP3, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + timeSpanModel.getParameters(), stateP3.getDate()).getDelay(); + + SpacecraftState stateP4 = shiftState(state, orbitType, angleType, 4 * steps[i], i); + final Vector3D positionP4 = stateP4.getPosition(); + final TrackingCoordinates trackingCoordinatesP4 = station.getBaseFrame(). + getTrackingCoordinates(positionP4, stateP4.getFrame(), stateP4.getDate()); + double delayP4 = timeSpanModel.pathDelay(trackingCoordinatesP4, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + timeSpanModel.getParameters(), stateP4.getDate()).getDelay(); + + fillJacobianColumn(refDeriv, i, orbitType, angleType, steps[i], + delayM4, delayM3, delayM2, delayM1, + delayP1, delayP2, delayP3, delayP4); + } + + for (int i = 0; i < 6; i++) { + Assertions.assertEquals(compDeriv[i + 1], refDeriv[0][i], tolerance); + } + } + + @Test + public void testDelayParameterDerivative() { + doTestParametersDerivatives(EstimatedModel.TOTAL_ZENITH_DELAY, 5.0e-15); + } + + private void doTestParametersDerivatives(String parameterName, double tolerance) { + + // Geodetic point + final double latitude = FastMath.toRadians(45.0); + final double longitude = FastMath.toRadians(45.0); + final double height = 0.0; + final GeodeticPoint point = new GeodeticPoint(latitude, longitude, height); + // Body: earth + final OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, + Constants.WGS84_EARTH_FLATTENING, + FramesFactory.getITRF(IERSConventions.IERS_2010, true)); + // Topocentric frame + final TopocentricFrame baseFrame = new TopocentricFrame(earth, point, "topo"); + + // Tropospheric model + final TroposphereMappingFunction gmf = new GlobalMappingFunctionModel(); + final TroposphericModel model = new EstimatedModel(gmf, 5.0); + + // Set Parameter Driver + for (final ParameterDriver driver : model.getParametersDrivers()) { + driver.setValue(driver.getReferenceValue()); + driver.setSelected(driver.getName().equals(parameterName)); + } + + // Count the required number of parameters + int nbParams = 0; + for (final ParameterDriver driver : model.getParametersDrivers()) { + if (driver.isSelected()) { + ++nbParams; + } + } + + // Derivative Structure + final DSFactory factory = new DSFactory(6 + nbParams, 1); + final DerivativeStructure a0 = factory.variable(0, 24464560.0); + final DerivativeStructure e0 = factory.variable(1, 0.05); + final DerivativeStructure i0 = factory.variable(2, 0.122138); + final DerivativeStructure pa0 = factory.variable(3, 3.10686); + final DerivativeStructure raan0 = factory.variable(4, 1.00681); + final DerivativeStructure anomaly0 = factory.variable(5, 0.048363); + final Field field = a0.getField(); + final DerivativeStructure zero = field.getZero(); + + // Field Date + final FieldAbsoluteDate dsDate = new FieldAbsoluteDate<>(field, 2018, 11, 19, 18, 0, 0.0, + TimeScalesFactory.getUTC()); + + // Set drivers reference date + for (final ParameterDriver driver : model.getParametersDrivers()) { + driver.setReferenceDate(dsDate.toAbsoluteDate()); + } + + // Field Orbit + final Frame frame = FramesFactory.getEME2000(); + final FieldOrbit dsOrbit = new FieldKeplerianOrbit<>(a0, e0, i0, pa0, raan0, anomaly0, + PositionAngleType.MEAN, frame, + dsDate, zero.add(3.9860047e14)); + + // Field State + final FieldSpacecraftState dsState = new FieldSpacecraftState<>(dsOrbit); + + // Initial satellite elevation + final FieldVector3D position = dsState.getPosition(); + final FieldTrackingCoordinates dsTrackingCoordinates = + baseFrame.getTrackingCoordinates(position, frame, dsState.getDate()); + + // Add parameter as a variable + final List drivers = model.getParametersDrivers(); + final DerivativeStructure[] parameters = new DerivativeStructure[drivers.size()]; + int index = 6; + for (int i = 0; i < drivers.size(); ++i) { + parameters[i] = drivers.get(i).isSelected() ? + factory.variable(index++, drivers.get(i).getValue()) : + factory.constant(drivers.get(i).getValue()); + } + + // Compute delay state derivatives + final FieldGeodeticPoint dsPoint = new FieldGeodeticPoint<>(zero.add(point.getLatitude()), zero.add(point.getLongitude()), zero.add(point.getAltitude())); + final DerivativeStructure delay = model.pathDelay(dsTrackingCoordinates, dsPoint, + new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + parameters, dsState.getDate()).getDelay(); + + final double[] compDeriv = delay.getAllDerivatives(); + + // Field -> non-field + final SpacecraftState state = dsState.toSpacecraftState(); + final TrackingCoordinates trackingCoordinates = new TrackingCoordinates(dsTrackingCoordinates.getAzimuth().getReal(), + dsTrackingCoordinates.getElevation().getReal(), + dsTrackingCoordinates.getRange().getReal()); + + // Finite differences for reference values + final double[][] refDeriv = new double[1][1]; + ParameterDriversList bound = new ParameterDriversList(); + for (final ParameterDriver driver : model.getParametersDrivers()) { + if (driver.getName().equals(parameterName)) { + driver.setSelected(true); + bound.add(driver); + } else { + driver.setSelected(false); + } + } + ParameterDriver selected = bound.getDrivers().get(0); + double p0 = selected.getReferenceValue(); + double h = selected.getScale(); + + final OrbitType orbitType = OrbitType.KEPLERIAN; + final PositionAngleType angleType = PositionAngleType.MEAN; + + selected.setValue(p0 - 4 * h); + double delayM4 = model.pathDelay(trackingCoordinates, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), state.getDate()).getDelay(); + + selected.setValue(p0 - 3 * h); + double delayM3 = model.pathDelay(trackingCoordinates, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), state.getDate()).getDelay(); + + selected.setValue(p0 - 2 * h); + double delayM2 = model.pathDelay(trackingCoordinates, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), state.getDate()).getDelay(); + + selected.setValue(p0 - 1 * h); + double delayM1 = model.pathDelay(trackingCoordinates, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), state.getDate()).getDelay(); + + selected.setValue(p0 + 1 * h); + double delayP1 = model.pathDelay(trackingCoordinates, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), state.getDate()).getDelay(); + + selected.setValue(p0 + 2 * h); + double delayP2 = model.pathDelay(trackingCoordinates, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), state.getDate()).getDelay(); + + selected.setValue(p0 + 3 * h); + double delayP3 = model.pathDelay(trackingCoordinates, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), state.getDate()).getDelay(); + + selected.setValue(p0 + 4 * h); + double delayP4 = model.pathDelay(trackingCoordinates, point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(), state.getDate()).getDelay(); + + fillJacobianColumn(refDeriv, 0, orbitType, angleType, h, + delayM4, delayM3, delayM2, delayM1, + delayP1, delayP2, delayP3, delayP4); + + Assertions.assertEquals(compDeriv[7], refDeriv[0][0], tolerance); + + } + + @Test + public void testComparisonWithEstimatedModel() { + final AbsoluteDate date = new AbsoluteDate(); + TroposphereMappingFunction mapping = new NiellMappingFunctionModel(); + EstimatedModel estimatedModel = new EstimatedModel(mapping, 2.0); + TroposphericModel timeSpanModel = new TimeSpanEstimatedModel(estimatedModel); + final double elevation = 45.0; + final double height = 100.0; + final double[] estimatedParameters = estimatedModel.getParameters(); + final double[] timeSpanParameters = estimatedModel.getParameters(); + GeodeticPoint point = new GeodeticPoint(FastMath.toRadians(45.0), FastMath.toRadians(45.0), height); + + Assertions.assertEquals(estimatedModel.pathDelay(new TrackingCoordinates(0.0, elevation, 0.0), + point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + estimatedParameters, date).getDelay(), + timeSpanModel.pathDelay(new TrackingCoordinates(0.0, elevation, 0.0), + point, TroposphericModelUtils.STANDARD_ATMOSPHERE, + timeSpanParameters, date).getDelay(), + Double.MIN_VALUE); + } + + @Test + public void testFieldComparisonWithEstimatedModel() { + doTestFieldComparisonWithEstimatedModel(Binary64Field.getInstance()); + } + + private > void doTestFieldComparisonWithEstimatedModel(final Field field) { + final T zero = field.getZero(); + final FieldAbsoluteDate date = new FieldAbsoluteDate<>(field); + TroposphereMappingFunction mapping = new NiellMappingFunctionModel(); + EstimatedModel estimatedModel = new EstimatedModel(mapping, 2.0); + TroposphericModel timeSpanModel = new TimeSpanEstimatedModel(estimatedModel); + final FieldTrackingCoordinates trackingCoordinates = new FieldTrackingCoordinates(zero, + zero.newInstance(FastMath.toRadians(45.0)), + zero); + final T height = zero.add(100.0); + final T[] estimatedParameters = estimatedModel.getParameters(field); + final T[] timeSpanParameters = estimatedModel.getParameters(field); + final FieldGeodeticPoint dsPoint = new FieldGeodeticPoint<>(zero.add(FastMath.toRadians(45.0)), zero.add(FastMath.toRadians(45.0)), height); + + Assertions.assertEquals(estimatedModel.pathDelay(trackingCoordinates, dsPoint, + new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + estimatedParameters, date).getDelay().getReal(), + timeSpanModel.pathDelay(trackingCoordinates, dsPoint, + new FieldPressureTemperatureHumidity<>(field, TroposphericModelUtils.STANDARD_ATMOSPHERE), + timeSpanParameters, date).getDelay().getReal(), + Double.MIN_VALUE); + } + + private SpacecraftState shiftState(SpacecraftState state, OrbitType orbitType, PositionAngleType angleType, + double delta, int column) { + + double[][] array = stateToArray(state, orbitType, angleType, true); + array[0][column] += delta; + + return arrayToState(array, orbitType, angleType, state.getFrame(), state.getDate(), + state.getMu(), state.getAttitude()); + + } + + private double[][] stateToArray(SpacecraftState state, OrbitType orbitType, PositionAngleType angleType, + boolean withMass) { + double[][] array = new double[2][withMass ? 7 : 6]; + orbitType.mapOrbitToArray(state.getOrbit(), angleType, array[0], array[1]); + if (withMass) { + array[0][6] = state.getMass(); + } + return array; + } + + private SpacecraftState arrayToState(double[][] array, OrbitType orbitType, PositionAngleType angleType, + Frame frame, AbsoluteDate date, double mu, + Attitude attitude) { + Orbit orbit = orbitType.mapArrayToOrbit(array[0], array[1], angleType, date, mu, frame); + return (array.length > 6) ? + new SpacecraftState(orbit, attitude) : + new SpacecraftState(orbit, attitude, array[0][6]); + } + + private void fillJacobianColumn(double[][] jacobian, int column, + OrbitType orbitType, PositionAngleType angleType, double h, + double sM4h, double sM3h, + double sM2h, double sM1h, + double sP1h, double sP2h, + double sP3h, double sP4h) { + + jacobian[0][column] = ( -3 * (sP4h - sM4h) + + 32 * (sP3h - sM3h) - + 168 * (sP2h - sM2h) + + 672 * (sP1h - sM1h)) / (840 * h); + } + +} diff --git a/src/test/java/org/orekit/models/earth/troposphere/TimeSpanEstimatedTroposphericModelTest.java b/src/test/java/org/orekit/models/earth/troposphere/TimeSpanEstimatedTroposphericModelTest.java index b7debdaad0..92618f5347 100644 --- a/src/test/java/org/orekit/models/earth/troposphere/TimeSpanEstimatedTroposphericModelTest.java +++ b/src/test/java/org/orekit/models/earth/troposphere/TimeSpanEstimatedTroposphericModelTest.java @@ -57,6 +57,7 @@ import java.util.List; +@Deprecated public class TimeSpanEstimatedTroposphericModelTest { @BeforeAll diff --git a/src/test/java/org/orekit/models/earth/troposphere/ViennaOneModelTest.java b/src/test/java/org/orekit/models/earth/troposphere/ViennaOneModelTest.java index c3dc393a4b..84bb09ed34 100644 --- a/src/test/java/org/orekit/models/earth/troposphere/ViennaOneModelTest.java +++ b/src/test/java/org/orekit/models/earth/troposphere/ViennaOneModelTest.java @@ -16,6 +16,8 @@ */ package org.orekit.models.earth.troposphere; +import org.hipparchus.util.Binary64; +import org.hipparchus.util.Binary64Field; import org.hipparchus.util.FastMath; import org.hipparchus.util.Precision; import org.junit.jupiter.api.Assertions; @@ -23,14 +25,18 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.orekit.Utils; +import org.orekit.bodies.FieldGeodeticPoint; import org.orekit.bodies.GeodeticPoint; import org.orekit.errors.OrekitException; import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; import org.orekit.time.TimeScalesFactory; +import org.orekit.utils.TrackingCoordinates; +@Deprecated public class ViennaOneModelTest { - private static double epsilon = 1e-6; + private static final double epsilon = 1e-6; @BeforeAll public static void setUpGlobal() { @@ -75,7 +81,7 @@ public void testMappingFactors() { final double height = 824.17; final GeodeticPoint point = new GeodeticPoint(latitude, longitude, height); - final double elevation = 0.5 * FastMath.PI - 1.278564131; + final TrackingCoordinates trackingCoordinates = new TrackingCoordinates(0.0, 0.5 * FastMath.PI - 1.278564131, 0.0); final double expectedHydro = 3.425088; final double expectedWet = 3.448300; @@ -84,7 +90,9 @@ public void testMappingFactors() { final ViennaOneModel model = new ViennaOneModel(a, z); - final double[] computedMapping = model.mappingFactors(elevation, point, date); + final double[] computedMapping = model.mappingFactors(trackingCoordinates, point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + date); Assertions.assertEquals(expectedHydro, computedMapping[0], 4.1e-6); Assertions.assertEquals(expectedWet, computedMapping[1], 1.0e-6); @@ -99,9 +107,24 @@ public void testDelay() { final double[] z = {2.0966, 0.2140}; final GeodeticPoint point = new GeodeticPoint(FastMath.toRadians(45.0), FastMath.toRadians(45.0), height); ViennaOneModel model = new ViennaOneModel(a, z); - final double path = model.pathDelay(FastMath.toRadians(elevation), point, model.getParameters(date), date); + final double path = model.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(elevation), 0.0), + point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + null, date).getDelay(); Assertions.assertTrue(Precision.compareTo(path, 20d, epsilon) < 0); Assertions.assertTrue(Precision.compareTo(path, 0d, epsilon) > 0); + Assertions.assertEquals(path, + model.pathDelay(FastMath.toRadians(elevation), + point, model.getParameters(date), date), + 1.0e-10); + Binary64Field field = Binary64Field.getInstance(); + Binary64 zero = field.getZero(); + Assertions.assertEquals(path, + model.pathDelay(FastMath.toRadians(zero.newInstance(elevation)), + new FieldGeodeticPoint<>(field, point), + null, + new FieldAbsoluteDate<>(field, date)).getReal(), + 1.0e-10); } @Test @@ -114,7 +137,10 @@ public void testFixedHeight() { double lastDelay = Double.MAX_VALUE; // delay shall decline with increasing elevation angle for (double elev = 10d; elev < 90d; elev += 8d) { - final double delay = model.pathDelay(FastMath.toRadians(elev), point, model.getParameters(date), date); + final double delay = model.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(elev), 0.0), + point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(date), date).getDelay(); Assertions.assertTrue(Precision.compareTo(delay, lastDelay, epsilon) < 0); lastDelay = delay; } diff --git a/src/test/java/org/orekit/models/earth/troposphere/ViennaOneTest.java b/src/test/java/org/orekit/models/earth/troposphere/ViennaOneTest.java new file mode 100644 index 0000000000..8644faab50 --- /dev/null +++ b/src/test/java/org/orekit/models/earth/troposphere/ViennaOneTest.java @@ -0,0 +1,134 @@ +/* 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.models.earth.troposphere; + +import org.hipparchus.util.FastMath; +import org.hipparchus.util.Precision; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.orekit.Utils; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.errors.OrekitException; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.TimeScalesFactory; +import org.orekit.utils.TrackingCoordinates; + +public class ViennaOneTest { + + private static double epsilon = 1e-6; + + @BeforeAll + public static void setUpGlobal() { + Utils.setDataRoot("atmosphere"); + } + + @BeforeEach + public void setUp() throws OrekitException { + Utils.setDataRoot("regular-data:potential/shm-format"); + } + + @Test + public void testMappingFactors() { + + // Site (NRAO, Green Bank, WV): latitude: 38° + // longitude: 280° + // height: 824.17 m + // + // Date: MJD 55055 -> 12 August 2009 at 0h UT + // + // Ref for the inputs: Petit, G. and Luzum, B. (eds.), IERS Conventions (2010), + // IERS Technical Note No. 36, BKG (2010) + // + // Values: ah = 0.00127683 + // aw = 0.00060955 + // zhd = 2.0966 m + // zwd = 0.2140 m + // + // Values taken from: http://vmf.geo.tuwien.ac.at/trop_products/GRID/2.5x2/VMF1/VMF1_OP/2009/VMFG_20090812.H00 + // + // Expected mapping factors : hydrostatic -> 3.425088 + // wet -> 3.448300 + // + // Expected outputs are obtained by performing the Matlab script vmf1_ht.m provided by TU WIEN: + // http://vmf.geo.tuwien.ac.at/codes/ + // + + final AbsoluteDate date = AbsoluteDate.createMJDDate(55055, 0, TimeScalesFactory.getUTC()); + + final double latitude = FastMath.toRadians(38.0); + final double longitude = FastMath.toRadians(280.0); + final double height = 824.17; + final GeodeticPoint point = new GeodeticPoint(latitude, longitude, height); + + final TrackingCoordinates trackingCoordinates = new TrackingCoordinates(0.0, 0.5 * FastMath.PI - 1.278564131, 0.0); + final double expectedHydro = 3.425088; + final double expectedWet = 3.448300; + + final ViennaOne model = new ViennaOne(new ConstantViennaAProvider(new ViennaACoefficients(0.00127683, 0.00060955)), + new ConstantAzimuthalGradientProvider(null), + new ConstantTroposphericModel(new TroposphericDelay(2.0966, 0.2140, 0, 0)), + TimeScalesFactory.getUTC()); + + final double[] computedMapping = model.mappingFactors(trackingCoordinates, point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + date); + + Assertions.assertEquals(expectedHydro, computedMapping[0], 4.1e-6); + Assertions.assertEquals(expectedWet, computedMapping[1], 1.0e-6); + } + + @Test + public void testDelay() { + final double elevation = 10d; + final double height = 100d; + final AbsoluteDate date = new AbsoluteDate(); + final GeodeticPoint point = new GeodeticPoint(FastMath.toRadians(45.0), FastMath.toRadians(45.0), height); + ViennaOne model = new ViennaOne(new ConstantViennaAProvider(new ViennaACoefficients(0.00127683, 0.00060955)), + new ConstantAzimuthalGradientProvider(null), + new ConstantTroposphericModel(new TroposphericDelay(2.0966, 0.2140, 0, 0)), + TimeScalesFactory.getUTC()); + final double path = model.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(elevation), 0.0), + point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(date), date).getDelay(); + Assertions.assertTrue(Precision.compareTo(path, 20d, epsilon) < 0); + Assertions.assertTrue(Precision.compareTo(path, 0d, epsilon) > 0); + } + + @Test + public void testFixedHeight() { + final AbsoluteDate date = new AbsoluteDate(); + final GeodeticPoint point = new GeodeticPoint(FastMath.toRadians(45.0), FastMath.toRadians(45.0), 350.0); + ViennaOne model = new ViennaOne(new ConstantViennaAProvider(new ViennaACoefficients(0.00127683, 0.00060955)), + new ConstantAzimuthalGradientProvider(null), + new ConstantTroposphericModel(new TroposphericDelay(2.0966, 0.2140, 0, 0)), + TimeScalesFactory.getUTC()); + double lastDelay = Double.MAX_VALUE; + // delay shall decline with increasing elevation angle + for (double elev = 10d; elev < 90d; elev += 8d) { + final double delay = model.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(elev), 0.0), + point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(date), date).getDelay(); + Assertions.assertTrue(Precision.compareTo(delay, lastDelay, epsilon) < 0); + lastDelay = delay; + } + } + +} diff --git a/src/test/java/org/orekit/models/earth/troposphere/ViennaThreeModelTest.java b/src/test/java/org/orekit/models/earth/troposphere/ViennaThreeModelTest.java index 68773493bb..e568186067 100644 --- a/src/test/java/org/orekit/models/earth/troposphere/ViennaThreeModelTest.java +++ b/src/test/java/org/orekit/models/earth/troposphere/ViennaThreeModelTest.java @@ -16,6 +16,8 @@ */ package org.orekit.models.earth.troposphere; +import org.hipparchus.util.Binary64; +import org.hipparchus.util.Binary64Field; import org.hipparchus.util.FastMath; import org.hipparchus.util.Precision; import org.junit.jupiter.api.Assertions; @@ -23,14 +25,18 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.orekit.Utils; +import org.orekit.bodies.FieldGeodeticPoint; import org.orekit.bodies.GeodeticPoint; import org.orekit.errors.OrekitException; import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; import org.orekit.time.TimeScalesFactory; +import org.orekit.utils.TrackingCoordinates; +@Deprecated public class ViennaThreeModelTest { - private static double epsilon = 1e-6; + private static final double epsilon = 1e-6; @BeforeAll public static void setUpGlobal() { @@ -72,7 +78,7 @@ public void testMappingFactors() { final double height = 824.0; final GeodeticPoint point = new GeodeticPoint(latitude, longitude, height); - final double elevation = FastMath.toRadians(38.0); + final TrackingCoordinates trackingCoordinates = new TrackingCoordinates(0.0, FastMath.toRadians(38.0), 0.0); final double expectedHydro = 1.621024; final double expectedWet = 1.623023; @@ -81,7 +87,9 @@ public void testMappingFactors() { final ViennaThreeModel model = new ViennaThreeModel(a, z); - final double[] computedMapping = model.mappingFactors(elevation, point, date); + final double[] computedMapping = model.mappingFactors(trackingCoordinates, point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + date); Assertions.assertEquals(expectedHydro, computedMapping[0], epsilon); Assertions.assertEquals(expectedWet, computedMapping[1], epsilon); @@ -117,7 +125,7 @@ public void testLowElevation() { final double height = 824.0; final GeodeticPoint point = new GeodeticPoint(latitude, longitude, height); - final double elevation = FastMath.toRadians(5.0); + final TrackingCoordinates trackingCoordinates = new TrackingCoordinates(0.0, FastMath.toRadians(5.0), 0.0); final double expectedHydro = 10.132802; final double expectedWet = 10.879154; @@ -126,7 +134,9 @@ public void testLowElevation() { final ViennaThreeModel model = new ViennaThreeModel(a, z); - final double[] computedMapping = model.mappingFactors(elevation, point, date); + final double[] computedMapping = model.mappingFactors(trackingCoordinates, point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + date); Assertions.assertEquals(expectedHydro, computedMapping[0], epsilon); Assertions.assertEquals(expectedWet, computedMapping[1], epsilon); @@ -162,7 +172,7 @@ public void testHightElevation() { final double height = 824.0; final GeodeticPoint point = new GeodeticPoint(latitude, longitude, height); - final double elevation = FastMath.toRadians(85.0); + final TrackingCoordinates trackingCoordinates = new TrackingCoordinates(0.0, FastMath.toRadians(85.0), 0.0); final double expectedHydro = 1.003810; final double expectedWet = 1.003816; @@ -171,7 +181,9 @@ public void testHightElevation() { final ViennaThreeModel model = new ViennaThreeModel(a, z); - final double[] computedMapping = model.mappingFactors(elevation, point, date); + final double[] computedMapping = model.mappingFactors(trackingCoordinates, point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + date); Assertions.assertEquals(expectedHydro, computedMapping[0], epsilon); Assertions.assertEquals(expectedWet, computedMapping[1], epsilon); @@ -186,9 +198,27 @@ public void testDelay() { final double[] z = {2.1993, 0.0690}; final GeodeticPoint point = new GeodeticPoint(FastMath.toRadians(37.5), FastMath.toRadians(277.5), height); ViennaThreeModel model = new ViennaThreeModel(a, z); - final double path = model.pathDelay(FastMath.toRadians(elevation), point, model.getParameters(date), date); - Assertions.assertTrue(Precision.compareTo(path, 20d, epsilon) < 0); - Assertions.assertTrue(Precision.compareTo(path, 0d, epsilon) > 0); + final TroposphericDelay delay = model.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(elevation), 0.0), + point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(date), date); + Assertions.assertEquals( 2.1993, delay.getZh(), 1.0e-4); + Assertions.assertEquals( 0.069, delay.getZw(), 1.0e-4); + Assertions.assertEquals(12.2124, delay.getSh(), 1.0e-4); + Assertions.assertEquals( 0.3916, delay.getSw(), 1.0e-4); + Assertions.assertEquals(12.6041, delay.getDelay(), 1.0e-4); + Assertions.assertEquals(delay.getDelay(), + model.pathDelay(FastMath.toRadians(elevation), + point, model.getParameters(date), date), + 1.0e-10); + Binary64Field field = Binary64Field.getInstance(); + Binary64 zero = field.getZero(); + Assertions.assertEquals(delay.getDelay(), + model.pathDelay(FastMath.toRadians(zero.newInstance(elevation)), + new FieldGeodeticPoint<>(field, point), + null, + new FieldAbsoluteDate<>(field, date)).getReal(), + 1.0e-10); } @Test @@ -201,7 +231,10 @@ public void testFixedHeight() { double lastDelay = Double.MAX_VALUE; // delay shall decline with increasing elevation angle for (double elev = 10d; elev < 90d; elev += 8d) { - final double delay = model.pathDelay(FastMath.toRadians(elev), point, model.getParameters(date), date); + final double delay = model.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(elev), 0.0), + point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(date), date).getDelay(); Assertions.assertTrue(Precision.compareTo(delay, lastDelay, epsilon) < 0); lastDelay = delay; } diff --git a/src/test/java/org/orekit/models/earth/troposphere/ViennaThreeTest.java b/src/test/java/org/orekit/models/earth/troposphere/ViennaThreeTest.java new file mode 100644 index 0000000000..db5825aadf --- /dev/null +++ b/src/test/java/org/orekit/models/earth/troposphere/ViennaThreeTest.java @@ -0,0 +1,252 @@ +/* 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.models.earth.troposphere; + +import org.hipparchus.util.FastMath; +import org.hipparchus.util.Precision; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.orekit.Utils; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.errors.OrekitException; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.TimeScalesFactory; +import org.orekit.utils.TrackingCoordinates; + +public class ViennaThreeTest { + + private static double epsilon = 1e-6; + + @BeforeAll + public static void setUpGlobal() { + Utils.setDataRoot("atmosphere"); + } + + @BeforeEach + public void setUp() throws OrekitException { + Utils.setDataRoot("regular-data:potential/shm-format"); + } + + @Test + public void testMappingFactors() { + + // Site: latitude: 37.5° + // longitude: 277.5° + // height: 824 m + // + // Date: 25 November 2018 at 0h UT + // + // Values: ah = 0.00123462 + // aw = 0.00047101 + // zhd = 2.1993 m + // zwd = 0.0690 m + // + // Values taken from: http://vmf.geo.tuwien.ac.at/trop_products/GRID/5x5/VMF3/VMF3_OP/2018/VMF3_20181125.H00 + // + // Expected mapping factors : hydrostatic -> 1.621024 + // wet -> 1.623023 + // + // Expected outputs are obtained by performing the Matlab script vmf3.m provided by TU WIEN: + // http://vmf.geo.tuwien.ac.at/codes/ + // + + final AbsoluteDate date = new AbsoluteDate(2018, 11, 25, TimeScalesFactory.getUTC()); + + final double latitude = FastMath.toRadians(37.5); + final double longitude = FastMath.toRadians(277.5); + final double height = 824.0; + final GeodeticPoint point = new GeodeticPoint(latitude, longitude, height); + + final TrackingCoordinates trackingCoordinates = new TrackingCoordinates(0.0, FastMath.toRadians(38.0), 0.0); + final double expectedHydro = 1.621024; + final double expectedWet = 1.623023; + + final ViennaThree model = new ViennaThree(new ConstantViennaAProvider(new ViennaACoefficients(0.00123462, 0.00047101)), + new ConstantAzimuthalGradientProvider(null), + new ConstantTroposphericModel(new TroposphericDelay(2.1993, 0.0690, 0, 0)), + TimeScalesFactory.getUTC()); + + final double[] computedMapping = model.mappingFactors(trackingCoordinates, point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + date); + + Assertions.assertEquals(expectedHydro, computedMapping[0], epsilon); + Assertions.assertEquals(expectedWet, computedMapping[1], epsilon); + } + + @Test + public void testLowElevation() { + + // Site: latitude: 37.5° + // longitude: 277.5° + // height: 824 m + // + // Date: 25 November 2018 at 0h UT + // + // Values: ah = 0.00123462 + // aw = 0.00047101 + // zhd = 2.1993 m + // zwd = 0.0690 m + // + // Values taken from: http://vmf.geo.tuwien.ac.at/trop_products/GRID/5x5/VMF3/VMF3_OP/2018/VMF3_20181125.H00 + // + // Expected mapping factors : hydrostatic -> 10.132802 + // wet -> 10.879154 + // + // Expected outputs are obtained by performing the Matlab script vmf3.m provided by TU WIEN: + // http://vmf.geo.tuwien.ac.at/codes/ + // + + final AbsoluteDate date = new AbsoluteDate(2018, 11, 25, TimeScalesFactory.getUTC()); + + final double latitude = FastMath.toRadians(37.5); + final double longitude = FastMath.toRadians(277.5); + final double height = 824.0; + final GeodeticPoint point = new GeodeticPoint(latitude, longitude, height); + + final TrackingCoordinates trackingCoordinates = new TrackingCoordinates(0.0, FastMath.toRadians(5.0), 0.0); + final double expectedHydro = 10.132802; + final double expectedWet = 10.879154; + + final ViennaThree model = new ViennaThree(new ConstantViennaAProvider(new ViennaACoefficients(0.00123462, 0.00047101)), + new ConstantAzimuthalGradientProvider(null), + new ConstantTroposphericModel(new TroposphericDelay(2.1993, 0.0690, 0, 0)), + TimeScalesFactory.getUTC()); + + final double[] computedMapping = model.mappingFactors(trackingCoordinates, point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + date); + + Assertions.assertEquals(expectedHydro, computedMapping[0], epsilon); + Assertions.assertEquals(expectedWet, computedMapping[1], epsilon); + } + + @Test + public void testHightElevation() { + + // Site: latitude: 37.5° + // longitude: 277.5° + // height: 824 m + // + // Date: 25 November 2018 at 0h UT + // + // Values: ah = 0.00123462 + // aw = 0.00047101 + // zhd = 2.1993 m + // zwd = 0.0690 m + // + // Values taken from: http://vmf.geo.tuwien.ac.at/trop_products/GRID/5x5/VMF3/VMF3_OP/2018/VMF3_20181125.H00 + // + // Expected mapping factors : hydrostatic -> 1.003810 + // wet -> 1.003816 + // + // Expected outputs are obtained by performing the Matlab script vmf3.m provided by TU WIEN: + // http://vmf.geo.tuwien.ac.at/codes/ + // + + final AbsoluteDate date = new AbsoluteDate(2018, 11, 25, TimeScalesFactory.getUTC()); + + final double latitude = FastMath.toRadians(37.5); + final double longitude = FastMath.toRadians(277.5); + final double height = 824.0; + final GeodeticPoint point = new GeodeticPoint(latitude, longitude, height); + + final TrackingCoordinates trackingCoordinates = new TrackingCoordinates(0.0, FastMath.toRadians(85.0), 0.0); + final double expectedHydro = 1.003810; + final double expectedWet = 1.003816; + + final ViennaThree model = new ViennaThree(new ConstantViennaAProvider(new ViennaACoefficients(0.00123462, 0.00047101)), + new ConstantAzimuthalGradientProvider(null), + new ConstantTroposphericModel(new TroposphericDelay(2.1993, 0.0690, 0, 0)), + TimeScalesFactory.getUTC()); + + final double[] computedMapping = model.mappingFactors(trackingCoordinates, point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + date); + + Assertions.assertEquals(expectedHydro, computedMapping[0], epsilon); + Assertions.assertEquals(expectedWet, computedMapping[1], epsilon); + } + + @Test + public void testDelay() { + final double azimuth = 30.0; + final double elevation = 10.0; + final double height = 100.0; + final AbsoluteDate date = new AbsoluteDate(); + final GeodeticPoint point = new GeodeticPoint(FastMath.toRadians(37.5), FastMath.toRadians(277.5), height); + final ViennaThree model = new ViennaThree(new ConstantViennaAProvider(new ViennaACoefficients(0.00123462, 0.00047101)), + new ConstantAzimuthalGradientProvider(null), + new ConstantTroposphericModel(new TroposphericDelay(2.1993, 0.0690, 0, 0)), + TimeScalesFactory.getUTC()); + final TroposphericDelay delay = model.pathDelay(new TrackingCoordinates(FastMath.toRadians(azimuth), FastMath.toRadians(elevation), 0.0), + point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(date), date); + Assertions.assertEquals( 2.1993, delay.getZh(), 1.0e-4); + Assertions.assertEquals( 0.069, delay.getZw(), 1.0e-4); + Assertions.assertEquals(12.2124, delay.getSh(), 1.0e-4); + Assertions.assertEquals( 0.3916, delay.getSw(), 1.0e-4); + Assertions.assertEquals(12.6041, delay.getDelay(), 1.0e-4); + } + + @Test + public void testDelayWithAzimuthalAsymmetry() { + final double azimuth = 30.0; + final double elevation = 10.0; + final double height = 100.0; + final AbsoluteDate date = new AbsoluteDate(); + final GeodeticPoint point = new GeodeticPoint(FastMath.toRadians(37.5), FastMath.toRadians(277.5), height); + final ViennaThree model = new ViennaThree(new ConstantViennaAProvider(new ViennaACoefficients(0.00123462, 0.00047101)), + new ConstantAzimuthalGradientProvider(new AzimuthalGradientCoefficients(12.0, 4.5, + 0.8, 1.25)), + new ConstantTroposphericModel(new TroposphericDelay(2.1993, 0.0690, 0, 0)), + TimeScalesFactory.getUTC()); + final TroposphericDelay delay = model.pathDelay(new TrackingCoordinates(FastMath.toRadians(azimuth), FastMath.toRadians(elevation), 0.0), + point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(date), date); + Assertions.assertEquals( 2.1993, delay.getZh(), 1.0e-4); + Assertions.assertEquals( 0.069, delay.getZw(), 1.0e-4); + Assertions.assertEquals(12.2124 + 373.8241, delay.getSh(), 1.0e-4); // second term is due to azimuthal gradient + Assertions.assertEquals( 0.3916 + 38.9670, delay.getSw(), 1.0e-4); // second term is due to azimuthal gradient + Assertions.assertEquals(12.6041 + 373.8241 + 38.9670, delay.getDelay(), 1.0e-4); + } + + @Test + public void testFixedHeight() { + final AbsoluteDate date = new AbsoluteDate(); + final GeodeticPoint point = new GeodeticPoint(FastMath.toRadians(37.5), FastMath.toRadians(277.5), 350.0); + ViennaThree model = new ViennaThree(new ConstantViennaAProvider(new ViennaACoefficients(0.00123462, 0.00047101)), + new ConstantAzimuthalGradientProvider(null), + new ConstantTroposphericModel(new TroposphericDelay(2.1993, 0.0690, 0, 0)), + TimeScalesFactory.getUTC()); + double lastDelay = Double.MAX_VALUE; + // delay shall decline with increasing elevation angle + for (double elev = 10d; elev < 90d; elev += 8d) { + final double delay = model.pathDelay(new TrackingCoordinates(0.0, FastMath.toRadians(elev), 0.0), + point, + TroposphericModelUtils.STANDARD_ATMOSPHERE, + model.getParameters(date), date).getDelay(); + Assertions.assertTrue(Precision.compareTo(delay, lastDelay, epsilon) < 0); + lastDelay = delay; + } + } + +} diff --git a/src/test/java/org/orekit/models/earth/weather/GlobalPressureTemperature2ModelTest.java b/src/test/java/org/orekit/models/earth/weather/GlobalPressureTemperature2ModelTest.java index 13d6cb3c4e..8f94572e70 100644 --- a/src/test/java/org/orekit/models/earth/weather/GlobalPressureTemperature2ModelTest.java +++ b/src/test/java/org/orekit/models/earth/weather/GlobalPressureTemperature2ModelTest.java @@ -31,14 +31,15 @@ import org.orekit.time.TimeScalesFactory; import org.orekit.utils.IERSConventions; +@Deprecated public class GlobalPressureTemperature2ModelTest { private static double epsilon = 1.0e-12; @Test - public void testWeatherParameters() { + public void testProvidedParameters() { - Utils.setDataRoot("regular-data:potential:gpt2-grid"); + Utils.setDataRoot("regular-data:potential:gpt-grid"); GravityFieldFactory.addPotentialCoefficientsReader(new GRGSFormatReader("grim4s4_gr", true)); // Site Vienna: latitude: 48.20°N @@ -74,7 +75,7 @@ public void testWeatherParameters() { final double e = model.getWaterVaporPressure(); Assertions.assertEquals(22.12, temperature, 2.3e-1); - Assertions.assertEquals(1002.56, pressure, 5.1e-1); + Assertions.assertEquals(1002.56, pressure, 7.4e-1); Assertions.assertEquals(0.0012647, a[0], 1.1e-7); Assertions.assertEquals(0.0005726, a[1], 8.6e-8); Assertions.assertEquals(15.63, e, 5.0e-2); @@ -84,7 +85,7 @@ public void testWeatherParameters() { @Test public void testEquality() { - Utils.setDataRoot("regular-data:potential:gpt2-grid"); + Utils.setDataRoot("regular-data:potential:gpt-grid"); GravityFieldFactory.addPotentialCoefficientsReader(new GRGSFormatReader("grim4s4_gr", true)); // Commons parameters @@ -154,7 +155,7 @@ public void testEquality() { @Test public void testCorruptedFileBadData() { - Utils.setDataRoot("regular-data:potential:gpt2-grid"); + Utils.setDataRoot("regular-data:potential:gpt-grid"); GravityFieldFactory.addPotentialCoefficientsReader(new GRGSFormatReader("grim4s4_gr", true)); final double latitude = FastMath.toRadians(14.0); @@ -164,7 +165,7 @@ public void testCorruptedFileBadData() { final Geoid geoid = new Geoid(GravityFieldFactory.getNormalizedProvider(12, 12), ReferenceEllipsoid.getWgs84(FramesFactory.getITRF(IERSConventions.IERS_2010, true))); - final String fileName = "corrupted-bad-data-gpt2_5.grd"; + final String fileName = "corrupted-bad-data-gpt3_15.grd"; try { new GlobalPressureTemperature2Model(fileName, latitude, longitude, geoid); Assertions.fail("An exception should have been thrown"); @@ -179,7 +180,7 @@ public void testCorruptedFileBadData() { @Test public void testCorruptedIrregularGrid() { - Utils.setDataRoot("regular-data:potential:gpt2-grid"); + Utils.setDataRoot("regular-data:potential:gpt-grid"); GravityFieldFactory.addPotentialCoefficientsReader(new GRGSFormatReader("grim4s4_gr", true)); final double latitude = FastMath.toRadians(14.0); @@ -189,7 +190,7 @@ public void testCorruptedIrregularGrid() { final Geoid geoid = new Geoid(GravityFieldFactory.getNormalizedProvider(12, 12), ReferenceEllipsoid.getWgs84(FramesFactory.getITRF(IERSConventions.IERS_2010, true))); - final String fileName = "corrupted-irregular-grid-gpt2_5.grd"; + final String fileName = "corrupted-irregular-grid-gpt3_15.grd"; try { new GlobalPressureTemperature2Model(fileName, latitude, longitude, geoid); Assertions.fail("An exception should have been thrown"); diff --git a/src/test/java/org/orekit/models/earth/weather/GlobalPressureTemperature2Test.java b/src/test/java/org/orekit/models/earth/weather/GlobalPressureTemperature2Test.java new file mode 100644 index 0000000000..4d9cfcda3f --- /dev/null +++ b/src/test/java/org/orekit/models/earth/weather/GlobalPressureTemperature2Test.java @@ -0,0 +1,306 @@ +/* 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.models.earth.weather; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.util.Binary64Field; +import org.hipparchus.util.FastMath; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.orekit.Utils; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.data.DataSource; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.models.earth.troposphere.AzimuthalGradientCoefficients; +import org.orekit.models.earth.troposphere.FieldAzimuthalGradientCoefficients; +import org.orekit.models.earth.troposphere.FieldViennaACoefficients; +import org.orekit.models.earth.troposphere.TroposphericModelUtils; +import org.orekit.models.earth.troposphere.ViennaACoefficients; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.TimeScalesFactory; + +public class GlobalPressureTemperature2Test { + + private static double epsilon = 1.0e-12; + + @Test + public void testProvidedParameters() throws IOException, URISyntaxException { + + Utils.setDataRoot("regular-data"); + + // Site Vienna: latitude: 48.20°N + // longitude: 16.37°E + // height: 156 m + // + // Date: 2 August 2012 + // + // Expected outputs are given by the Department of Geodesy and Geoinformation of the Vienna University. + // Expected parameters : temperature -> 22.12 °C + // pressure -> 1002.56 hPa + // e -> 15.63 hPa + // ah -> 0.0012647 + // aw -> 0.0005726 + // + // We test the fiability of our implementation by comparing our output values with + // the ones obtained by the Vienna University. + + final double latitude = FastMath.toRadians(48.20); + final double longitude = FastMath.toRadians(16.37); + final double height = 156.0; + final AbsoluteDate date = AbsoluteDate.createMJDDate(56141, 0.0, TimeScalesFactory.getUTC()); + final URL url = GlobalPressureTemperature2Test.class.getClassLoader().getResource("gpt-grid/gpt2_5_extract.grd"); + final GlobalPressureTemperature2 model = + new GlobalPressureTemperature2(new DataSource(url.toURI()), + TimeScalesFactory.getUTC()); + + final GeodeticPoint location = new GeodeticPoint(latitude, longitude, height); + final ViennaACoefficients a = model.getA(location, date); + final PressureTemperatureHumidity pth = model.getWeatherParamerers(location, date); + final AzimuthalGradientCoefficients gradient = model.getGradientCoefficients(location, date); + + Assertions.assertEquals(0.0012647, a.getAh(), 1.1e-7); + Assertions.assertEquals(0.0005726, a.getAw(), 8.6e-8); + Assertions.assertEquals(273.15 + 22.12, pth.getTemperature(), 2.3e-1); + Assertions.assertEquals(1002.56, TroposphericModelUtils.HECTO_PASCAL.fromSI(pth.getPressure()), 7.4e-1); + Assertions.assertEquals(15.63, TroposphericModelUtils.HECTO_PASCAL.fromSI(pth.getWaterVaporPressure()), 5.0e-2); + Assertions.assertTrue(Double.isNaN(pth.getTm())); + Assertions.assertTrue(Double.isNaN(pth.getLambda())); + Assertions.assertNull(gradient); + + } + + @Test + public void testFieldProvidedParameters() throws IOException, URISyntaxException { + doTestFieldProvidedParameters(Binary64Field.getInstance()); + } + + protected > void doTestFieldProvidedParameters(final Field field) + throws IOException, URISyntaxException { + + Utils.setDataRoot("regular-data"); + + // Site Vienna: latitude: 48.20°N + // longitude: 16.37°E + // height: 156 m + // + // Date: 2 August 2012 + // + // Expected outputs are given by the Department of Geodesy and Geoinformation of the Vienna University. + // Expected parameters : temperature -> 22.12 °C + // pressure -> 1002.56 hPa + // e -> 15.63 hPa + // ah -> 0.0012647 + // aw -> 0.0005726 + // + // We test the fiability of our implementation by comparing our output values with + // the ones obtained by the Vienna University. + + final T latitude = FastMath.toRadians(field.getZero().newInstance(48.20)); + final T longitude = FastMath.toRadians(field.getZero().newInstance(16.37)); + final T height = field.getZero().newInstance(156.0); + final FieldAbsoluteDate date = FieldAbsoluteDate.createMJDDate(56141, field.getZero().newInstance(0.0), TimeScalesFactory.getUTC()); + final URL url = GlobalPressureTemperature2Test.class.getClassLoader().getResource("gpt-grid/gpt2_5_extract.grd"); + final GlobalPressureTemperature2 model = + new GlobalPressureTemperature2(new DataSource(url.toURI()), + TimeScalesFactory.getUTC()); + + final FieldGeodeticPoint location = new FieldGeodeticPoint<>(latitude, longitude, height); + final FieldViennaACoefficients a = model.getA(location, date); + final FieldPressureTemperatureHumidity pth = model.getWeatherParamerers(location, date); + final FieldAzimuthalGradientCoefficients gradient = model.getGradientCoefficients(location, date); + + Assertions.assertEquals(0.0012647, a.getAh().getReal(), 1.1e-7); + Assertions.assertEquals(0.0005726, a.getAw().getReal(), 8.6e-8); + Assertions.assertEquals(273.15 + 22.12, pth.getTemperature().getReal(), 2.3e-1); + Assertions.assertEquals(1002.56, TroposphericModelUtils.HECTO_PASCAL.fromSI(pth.getPressure()).getReal(), 7.4e-1); + Assertions.assertEquals(15.63, TroposphericModelUtils.HECTO_PASCAL.fromSI(pth.getWaterVaporPressure()).getReal(), 5.0e-2); + Assertions.assertTrue(pth.getTm().isNaN()); + Assertions.assertTrue(pth.getLambda().isNaN()); + Assertions.assertNull(gradient); + + } + + @Test + public void testEquality() throws IOException, URISyntaxException { + doTestEquality("gpt-grid/gpt2_15.grd"); + } + + @Test + public void testEqualityLoadingGpt2w() throws IOException, URISyntaxException { + doTestEquality("gpt-grid/gpt2_15w.grd"); + } + + @Test + public void testEqualityLoadingGpt3() throws IOException, URISyntaxException { + doTestEquality("gpt-grid/gpt3_15.grd"); + } + + private void doTestEquality(final String resourceName) throws IOException, URISyntaxException { + + Utils.setDataRoot("regular-data"); + + // Commons parameters + final AbsoluteDate date = AbsoluteDate.createMJDDate(56141, 0.0, TimeScalesFactory.getUTC()); + final double latitude = FastMath.toRadians(45.0); + final double height = 0.0; + + final URL url = GlobalPressureTemperature2Test.class.getClassLoader().getResource(resourceName); + GlobalPressureTemperature2 model = new GlobalPressureTemperature2(new DataSource(url.toURI()), + TimeScalesFactory.getUTC()); + + // Test longitude = 181° and longitude = -179° + GeodeticPoint location1 = new GeodeticPoint(latitude, FastMath.toRadians(181.0), height); + ViennaACoefficients a1 = model.getA(location1, date); + PressureTemperatureHumidity pth1 = model.getWeatherParamerers(location1, date); + GeodeticPoint location2 = new GeodeticPoint(latitude, FastMath.toRadians(-179.0), height); + ViennaACoefficients a2 = model.getA(location2, date); + PressureTemperatureHumidity pth2 = model.getWeatherParamerers(location2, date); + + Assertions.assertEquals(pth1.getTemperature(), pth2.getTemperature(), epsilon); + Assertions.assertEquals(pth1.getPressure(), pth2.getPressure(), epsilon); + Assertions.assertEquals(pth1.getWaterVaporPressure(), pth2.getWaterVaporPressure(), epsilon); + Assertions.assertEquals(a1.getAh(), a2.getAh(), epsilon); + Assertions.assertEquals(a1.getAw(), a2.getAw(), epsilon); + + // Test longitude = 180° and longitude = -180° + location1 = new GeodeticPoint(latitude, FastMath.toRadians(180.0), height); + a1 = model.getA(location1, date); + pth1 = model.getWeatherParamerers(location1, date); + location2 = new GeodeticPoint(latitude, FastMath.toRadians(-180.0), height); + a2 = model.getA(location2, date); + pth2 = model.getWeatherParamerers(location2, date); + + Assertions.assertEquals(pth1.getTemperature(), pth2.getTemperature(), epsilon); + Assertions.assertEquals(pth1.getPressure(), pth2.getPressure(), epsilon); + Assertions.assertEquals(pth1.getWaterVaporPressure(), pth2.getWaterVaporPressure(), epsilon); + Assertions.assertEquals(a1.getAh(), a2.getAh(), epsilon); + Assertions.assertEquals(a1.getAw(), a2.getAw(), epsilon); + + // Test longitude = 0° and longitude = 360° + location1 = new GeodeticPoint(latitude, FastMath.toRadians(0.0), height); + a1 = model.getA(location1, date); + pth1 = model.getWeatherParamerers(location1, date); + location2 = new GeodeticPoint(latitude, FastMath.toRadians(360.0), height); + a2 = model.getA(location2, date); + pth2 = model.getWeatherParamerers(location2, date); + + Assertions.assertEquals(pth1.getTemperature(), pth2.getTemperature(), epsilon); + Assertions.assertEquals(pth1.getPressure(), pth2.getPressure(), epsilon); + Assertions.assertEquals(pth1.getWaterVaporPressure(), pth2.getWaterVaporPressure(), epsilon); + Assertions.assertEquals(a1.getAh(), a2.getAh(), epsilon); + Assertions.assertEquals(a1.getAw(), a2.getAw(), epsilon); + + } + + @Test + public void testCorruptedFileBadData() throws IOException, URISyntaxException { + + Utils.setDataRoot("regular-data"); + + final String fileName = "corrupted-bad-data-gpt3_15.grd"; + final URL url = GlobalPressureTemperature2Test.class.getClassLoader().getResource("gpt-grid/" + fileName); + try { + new GlobalPressureTemperature2(new DataSource(url.toURI()), TimeScalesFactory.getUTC()); + Assertions.fail("An exception should have been thrown"); + } catch (OrekitException oe) { + Assertions.assertEquals(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, oe.getSpecifier()); + Assertions.assertEquals(6, ((Integer) oe.getParts()[0]).intValue()); + Assertions.assertTrue(((String) oe.getParts()[1]).endsWith(fileName)); + } + + } + + @Test + public void testCorruptedIrregularGrid() throws IOException, URISyntaxException { + + Utils.setDataRoot("regular-data"); + + final String fileName = "corrupted-irregular-grid-gpt3_15.grd"; + final URL url = GlobalPressureTemperature2Test.class.getClassLoader().getResource("gpt-grid/" + fileName); + try { + new GlobalPressureTemperature2(new DataSource(url.toURI()), TimeScalesFactory.getUTC()); + Assertions.fail("An exception should have been thrown"); + } catch (OrekitException oe) { + Assertions.assertEquals(OrekitMessages.IRREGULAR_OR_INCOMPLETE_GRID, oe.getSpecifier()); + Assertions.assertTrue(((String) oe.getParts()[0]).endsWith(fileName)); + } + + } + + @Test + public void testCorruptedIncompleteHeader() throws IOException, URISyntaxException { + + Utils.setDataRoot("regular-data"); + + final String fileName = "corrupted-incomplete-header.grd"; + final URL url = GlobalPressureTemperature2Test.class.getClassLoader().getResource("gpt-grid/" + fileName); + try { + new GlobalPressureTemperature2(new DataSource(url.toURI()), TimeScalesFactory.getUTC()); + Assertions.fail("An exception should have been thrown"); + } catch (OrekitException oe) { + Assertions.assertEquals(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, oe.getSpecifier()); + Assertions.assertEquals(1, ((Integer) oe.getParts()[0]).intValue()); + Assertions.assertTrue(((String) oe.getParts()[1]).endsWith(fileName)); + } + + } + + @Test + public void testCorruptedMissingSeasonalColumns() throws IOException, URISyntaxException { + + Utils.setDataRoot("regular-data"); + + final String fileName = "corrupted-missing-seasonal-columns-gpt3_15.grd"; + final URL url = GlobalPressureTemperature2Test.class.getClassLoader().getResource("gpt-grid/" + fileName); + try { + new GlobalPressureTemperature2(new DataSource(url.toURI()), TimeScalesFactory.getUTC()); + Assertions.fail("An exception should have been thrown"); + } catch (OrekitException oe) { + Assertions.assertEquals(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, oe.getSpecifier()); + Assertions.assertEquals(1, ((Integer) oe.getParts()[0]).intValue()); + Assertions.assertTrue(((String) oe.getParts()[1]).endsWith(fileName)); + } + + } + + @Test + public void testCorruptedMissingDataFields() throws IOException, URISyntaxException { + + Utils.setDataRoot("regular-data"); + + final String fileName = "corrupted-missing-data-fields-gpt3_15.grd"; + final URL url = GlobalPressureTemperature2Test.class.getClassLoader().getResource("gpt-grid/" + fileName); + try { + new GlobalPressureTemperature2(new DataSource(url.toURI()), TimeScalesFactory.getUTC()); + Assertions.fail("An exception should have been thrown"); + } catch (OrekitException oe) { + Assertions.assertEquals(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, oe.getSpecifier()); + Assertions.assertEquals(4, ((Integer) oe.getParts()[0]).intValue()); + Assertions.assertTrue(((String) oe.getParts()[1]).endsWith(fileName)); + } + + } + +} diff --git a/src/test/java/org/orekit/models/earth/weather/GlobalPressureTemperature2wTest.java b/src/test/java/org/orekit/models/earth/weather/GlobalPressureTemperature2wTest.java new file mode 100644 index 0000000000..86daddaa89 --- /dev/null +++ b/src/test/java/org/orekit/models/earth/weather/GlobalPressureTemperature2wTest.java @@ -0,0 +1,281 @@ +/* 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.models.earth.weather; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.util.Binary64Field; +import org.hipparchus.util.FastMath; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.orekit.Utils; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.data.DataSource; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.models.earth.troposphere.AzimuthalGradientCoefficients; +import org.orekit.models.earth.troposphere.FieldAzimuthalGradientCoefficients; +import org.orekit.models.earth.troposphere.FieldViennaACoefficients; +import org.orekit.models.earth.troposphere.TroposphericModelUtils; +import org.orekit.models.earth.troposphere.ViennaACoefficients; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.TimeScalesFactory; + +public class GlobalPressureTemperature2wTest { + + private static double epsilon = 1.0e-12; + + @Test + public void testProvidedParameters() throws IOException, URISyntaxException { + + Utils.setDataRoot("regular-data"); + + // Site Vienna: latitude: 48.20°N + // longitude: 16.37°E + // height: 156 m + // + // Date: 2 August 2012 + + final double latitude = FastMath.toRadians(48.20); + final double longitude = FastMath.toRadians(16.37); + final double height = 156.0; + final AbsoluteDate date = AbsoluteDate.createMJDDate(56141, 0.0, TimeScalesFactory.getUTC()); + final URL url = GlobalPressureTemperature2wTest.class.getClassLoader().getResource("gpt-grid/gpt2_15w.grd"); + final GlobalPressureTemperature2w model = + new GlobalPressureTemperature2w(new DataSource(url.toURI()), + TimeScalesFactory.getUTC()); + + final GeodeticPoint location = new GeodeticPoint(latitude, longitude, height); + final ViennaACoefficients a = model.getA(location, date); + final PressureTemperatureHumidity pth = model.getWeatherParamerers(location, date); + final AzimuthalGradientCoefficients gradient = model.getGradientCoefficients(location, date); + + Assertions.assertEquals(0.0012658, a.getAh(), 1.0e-7); + Assertions.assertEquals(0.0005684, a.getAw(), 1.0e-7); + Assertions.assertEquals(273.15 + 21.50, pth.getTemperature(), 1.0e-2); + Assertions.assertEquals(1000.72, TroposphericModelUtils.HECTO_PASCAL.fromSI(pth.getPressure()), 1.0e-2); + Assertions.assertEquals(16.38, TroposphericModelUtils.HECTO_PASCAL.fromSI(pth.getWaterVaporPressure()), 1.0e-2); + Assertions.assertEquals(273.15 + 9.41, pth.getTm(), 1.0e-2); + Assertions.assertEquals(3.105482, pth.getLambda(), 1.0e-6); + Assertions.assertNull(gradient); + + } + + @Test + public void testFieldProvidedParameters() throws IOException, URISyntaxException { + doTestFieldProvidedParameters(Binary64Field.getInstance()); + } + + protected > void doTestFieldProvidedParameters(final Field field) + throws IOException, URISyntaxException { + + Utils.setDataRoot("regular-data"); + + // Site Vienna: latitude: 48.20°N + // longitude: 16.37°E + // height: 156 m + // + // Date: 2 August 2012 + + final T latitude = FastMath.toRadians(field.getZero().newInstance(48.20)); + final T longitude = FastMath.toRadians(field.getZero().newInstance(16.37)); + final T height = field.getZero().newInstance(156.0); + final FieldAbsoluteDate date = FieldAbsoluteDate.createMJDDate(56141, field.getZero().newInstance(0.0), TimeScalesFactory.getUTC()); + final URL url = GlobalPressureTemperature2Test.class.getClassLoader().getResource("gpt-grid/gpt2_15w.grd"); + final GlobalPressureTemperature2w model = + new GlobalPressureTemperature2w(new DataSource(url.toURI()), + TimeScalesFactory.getUTC()); + + final FieldGeodeticPoint location = new FieldGeodeticPoint<>(latitude, longitude, height); + final FieldViennaACoefficients a = model.getA(location, date); + final FieldPressureTemperatureHumidity pth = model.getWeatherParamerers(location, date); + final FieldAzimuthalGradientCoefficients gradient = model.getGradientCoefficients(location, date); + + Assertions.assertEquals(0.0012658, a.getAh().getReal(), 1.1e-7); + Assertions.assertEquals(0.0005684, a.getAw().getReal(), 8.6e-8); + Assertions.assertEquals(273.15 + 21.50, pth.getTemperature().getReal(), 2.3e-1); + Assertions.assertEquals(1000.72, TroposphericModelUtils.HECTO_PASCAL.fromSI(pth.getPressure()).getReal(), 7.4e-1); + Assertions.assertEquals(16.38, TroposphericModelUtils.HECTO_PASCAL.fromSI(pth.getWaterVaporPressure()).getReal(), 5.0e-2); + Assertions.assertEquals(273.15 + 9.41, pth.getTm().getReal(), 1.0e-2); + Assertions.assertEquals(3.105482, pth.getLambda().getReal(), 1.0e-6); + Assertions.assertNull(gradient); + + } + + @Test + public void testEquality() throws IOException, URISyntaxException { + doTestEquality("gpt-grid/gpt2_15w.grd"); + } + + @Test + public void testEqualityLoadingGpt3() throws IOException, URISyntaxException { + doTestEquality("gpt-grid/gpt3_15.grd"); + } + + private void doTestEquality(final String resourceName) throws IOException, URISyntaxException { + + Utils.setDataRoot("regular-data"); + + // Commons parameters + final AbsoluteDate date = AbsoluteDate.createMJDDate(56141, 0.0, TimeScalesFactory.getUTC()); + final double latitude = FastMath.toRadians(45.0); + final double height = 0.0; + + final URL url = GlobalPressureTemperature2wTest.class.getClassLoader().getResource(resourceName); + GlobalPressureTemperature2w model = new GlobalPressureTemperature2w(new DataSource(url.toURI()), + TimeScalesFactory.getUTC()); + + // Test longitude = 181° and longitude = -179° + GeodeticPoint location1 = new GeodeticPoint(latitude, FastMath.toRadians(181.0), height); + ViennaACoefficients a1 = model.getA(location1, date); + PressureTemperatureHumidity pth1 = model.getWeatherParamerers(location1, date); + GeodeticPoint location2 = new GeodeticPoint(latitude, FastMath.toRadians(-179.0), height); + ViennaACoefficients a2 = model.getA(location2, date); + PressureTemperatureHumidity pth2 = model.getWeatherParamerers(location2, date); + + Assertions.assertEquals(pth1.getTemperature(), pth2.getTemperature(), epsilon); + Assertions.assertEquals(pth1.getPressure(), pth2.getPressure(), epsilon); + Assertions.assertEquals(pth1.getWaterVaporPressure(), pth2.getWaterVaporPressure(), epsilon); + Assertions.assertEquals(a1.getAh(), a2.getAh(), epsilon); + Assertions.assertEquals(a1.getAw(), a2.getAw(), epsilon); + + // Test longitude = 180° and longitude = -180° + location1 = new GeodeticPoint(latitude, FastMath.toRadians(180.0), height); + a1 = model.getA(location1, date); + pth1 = model.getWeatherParamerers(location1, date); + location2 = new GeodeticPoint(latitude, FastMath.toRadians(-180.0), height); + a2 = model.getA(location2, date); + pth2 = model.getWeatherParamerers(location2, date); + + Assertions.assertEquals(pth1.getTemperature(), pth2.getTemperature(), epsilon); + Assertions.assertEquals(pth1.getPressure(), pth2.getPressure(), epsilon); + Assertions.assertEquals(pth1.getWaterVaporPressure(), pth2.getWaterVaporPressure(), epsilon); + Assertions.assertEquals(a1.getAh(), a2.getAh(), epsilon); + Assertions.assertEquals(a1.getAw(), a2.getAw(), epsilon); + + // Test longitude = 0° and longitude = 360° + location1 = new GeodeticPoint(latitude, FastMath.toRadians(0.0), height); + a1 = model.getA(location1, date); + pth1 = model.getWeatherParamerers(location1, date); + location2 = new GeodeticPoint(latitude, FastMath.toRadians(360.0), height); + a2 = model.getA(location2, date); + pth2 = model.getWeatherParamerers(location2, date); + + Assertions.assertEquals(pth1.getTemperature(), pth2.getTemperature(), epsilon); + Assertions.assertEquals(pth1.getPressure(), pth2.getPressure(), epsilon); + Assertions.assertEquals(pth1.getWaterVaporPressure(), pth2.getWaterVaporPressure(), epsilon); + Assertions.assertEquals(a1.getAh(), a2.getAh(), epsilon); + Assertions.assertEquals(a1.getAw(), a2.getAw(), epsilon); + + } + + @Test + public void testCorruptedFileBadData() throws IOException, URISyntaxException { + + Utils.setDataRoot("regular-data"); + + final String fileName = "corrupted-bad-data-gpt3_15.grd"; + final URL url = GlobalPressureTemperature2wTest.class.getClassLoader().getResource("gpt-grid/" + fileName); + try { + new GlobalPressureTemperature2w(new DataSource(url.toURI()), TimeScalesFactory.getUTC()); + Assertions.fail("An exception should have been thrown"); + } catch (OrekitException oe) { + Assertions.assertEquals(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, oe.getSpecifier()); + Assertions.assertEquals(6, ((Integer) oe.getParts()[0]).intValue()); + Assertions.assertTrue(((String) oe.getParts()[1]).endsWith(fileName)); + } + + } + + @Test + public void testCorruptedIrregularGrid() throws IOException, URISyntaxException { + + Utils.setDataRoot("regular-data"); + + final String fileName = "corrupted-irregular-grid-gpt3_15.grd"; + final URL url = GlobalPressureTemperature2wTest.class.getClassLoader().getResource("gpt-grid/" + fileName); + try { + new GlobalPressureTemperature2w(new DataSource(url.toURI()), TimeScalesFactory.getUTC()); + Assertions.fail("An exception should have been thrown"); + } catch (OrekitException oe) { + Assertions.assertEquals(OrekitMessages.IRREGULAR_OR_INCOMPLETE_GRID, oe.getSpecifier()); + Assertions.assertTrue(((String) oe.getParts()[0]).endsWith(fileName)); + } + + } + + @Test + public void testCorruptedIncompleteHeader() throws IOException, URISyntaxException { + + Utils.setDataRoot("regular-data"); + + final String fileName = "corrupted-incomplete-header.grd"; + final URL url = GlobalPressureTemperature2wTest.class.getClassLoader().getResource("gpt-grid/" + fileName); + try { + new GlobalPressureTemperature2w(new DataSource(url.toURI()), TimeScalesFactory.getUTC()); + Assertions.fail("An exception should have been thrown"); + } catch (OrekitException oe) { + Assertions.assertEquals(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, oe.getSpecifier()); + Assertions.assertEquals(1, ((Integer) oe.getParts()[0]).intValue()); + Assertions.assertTrue(((String) oe.getParts()[1]).endsWith(fileName)); + } + + } + + @Test + public void testCorruptedMissingSeasonalColumns() throws IOException, URISyntaxException { + + Utils.setDataRoot("regular-data"); + + final String fileName = "corrupted-missing-seasonal-columns-gpt3_15.grd"; + final URL url = GlobalPressureTemperature2wTest.class.getClassLoader().getResource("gpt-grid/" + fileName); + try { + new GlobalPressureTemperature2w(new DataSource(url.toURI()), TimeScalesFactory.getUTC()); + Assertions.fail("An exception should have been thrown"); + } catch (OrekitException oe) { + Assertions.assertEquals(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, oe.getSpecifier()); + Assertions.assertEquals(1, ((Integer) oe.getParts()[0]).intValue()); + Assertions.assertTrue(((String) oe.getParts()[1]).endsWith(fileName)); + } + + } + + @Test + public void testCorruptedMissingDataFields() throws IOException, URISyntaxException { + + Utils.setDataRoot("regular-data"); + + final String fileName = "corrupted-missing-data-fields-gpt3_15.grd"; + final URL url = GlobalPressureTemperature2wTest.class.getClassLoader().getResource("gpt-grid/" + fileName); + try { + new GlobalPressureTemperature2w(new DataSource(url.toURI()), TimeScalesFactory.getUTC()); + Assertions.fail("An exception should have been thrown"); + } catch (OrekitException oe) { + Assertions.assertEquals(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, oe.getSpecifier()); + Assertions.assertEquals(4, ((Integer) oe.getParts()[0]).intValue()); + Assertions.assertTrue(((String) oe.getParts()[1]).endsWith(fileName)); + } + + } + +} diff --git a/src/test/java/org/orekit/models/earth/weather/GlobalPressureTemperature3Test.java b/src/test/java/org/orekit/models/earth/weather/GlobalPressureTemperature3Test.java new file mode 100644 index 0000000000..5ad539e69f --- /dev/null +++ b/src/test/java/org/orekit/models/earth/weather/GlobalPressureTemperature3Test.java @@ -0,0 +1,282 @@ +/* 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.models.earth.weather; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.util.Binary64Field; +import org.hipparchus.util.FastMath; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.orekit.Utils; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.GeodeticPoint; +import org.orekit.data.DataSource; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.models.earth.troposphere.AzimuthalGradientCoefficients; +import org.orekit.models.earth.troposphere.FieldAzimuthalGradientCoefficients; +import org.orekit.models.earth.troposphere.FieldViennaACoefficients; +import org.orekit.models.earth.troposphere.TroposphericModelUtils; +import org.orekit.models.earth.troposphere.ViennaACoefficients; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.TimeScalesFactory; + +public class GlobalPressureTemperature3Test { + + private static double epsilon = 1.0e-12; + + @Test + public void testProvidedParameters() throws IOException, URISyntaxException { + + Utils.setDataRoot("regular-data"); + + // Site Vienna: latitude: 48.20°N + // longitude: 16.37°E + // height: 156 m + // + // Date: 2 August 2012 + + final double latitude = FastMath.toRadians(48.20); + final double longitude = FastMath.toRadians(16.37); + final double height = 156.0; + final AbsoluteDate date = AbsoluteDate.createMJDDate(56141, 0.0, TimeScalesFactory.getUTC()); + final URL url = GlobalPressureTemperature3Test.class.getClassLoader().getResource("gpt-grid/gpt3_15.grd"); + final GlobalPressureTemperature3 model = + new GlobalPressureTemperature3(new DataSource(url.toURI()), + TimeScalesFactory.getUTC()); + + final GeodeticPoint location = new GeodeticPoint(latitude, longitude, height); + final ViennaACoefficients a = model.getA(location, date); + final PressureTemperatureHumidity pth = model.getWeatherParamerers(location, date); + final AzimuthalGradientCoefficients gradient = model.getGradientCoefficients(location, date); + + Assertions.assertEquals(0.0012594, a.getAh(), 1.0e-7); + Assertions.assertEquals(0.0005648, a.getAw(), 1.0e-7); + Assertions.assertEquals(273.15 + 21.50, pth.getTemperature(), 1.0e-2); + Assertions.assertEquals(1000.72, TroposphericModelUtils.HECTO_PASCAL.fromSI(pth.getPressure()), 1.0e-2); + Assertions.assertEquals(16.38, TroposphericModelUtils.HECTO_PASCAL.fromSI(pth.getWaterVaporPressure()), 1.0e-2); + Assertions.assertEquals(273.15 + 9.41, pth.getTm(), 1.0e-2); + Assertions.assertEquals(3.105482, pth.getLambda(), 1.0e-6); + Assertions.assertEquals(-18.5382468, gradient.getGnh(), 1.0e-7); + Assertions.assertEquals( 0.4685513, gradient.getGeh(), 1.0e-7); + Assertions.assertEquals( -4.4695832, gradient.getGnw(), 1.0e-7); + Assertions.assertEquals( 0.1445528, gradient.getGew(), 1.0e-7); + + } + + @Test + public void testFieldProvidedParameters() throws IOException, URISyntaxException { + doTestFieldProvidedParameters(Binary64Field.getInstance()); + } + + protected > void doTestFieldProvidedParameters(final Field field) + throws IOException, URISyntaxException { + + Utils.setDataRoot("regular-data"); + + // Site Vienna: latitude: 48.20°N + // longitude: 16.37°E + // height: 156 m + // + // Date: 2 August 2012 + + final T latitude = FastMath.toRadians(field.getZero().newInstance(48.20)); + final T longitude = FastMath.toRadians(field.getZero().newInstance(16.37)); + final T height = field.getZero().newInstance(156.0); + final FieldAbsoluteDate date = FieldAbsoluteDate.createMJDDate(56141, field.getZero().newInstance(0.0), TimeScalesFactory.getUTC()); + final URL url = GlobalPressureTemperature2Test.class.getClassLoader().getResource("gpt-grid/gpt3_15.grd"); + final GlobalPressureTemperature3 model = + new GlobalPressureTemperature3(new DataSource(url.toURI()), + TimeScalesFactory.getUTC()); + + final FieldGeodeticPoint location = new FieldGeodeticPoint<>(latitude, longitude, height); + final FieldViennaACoefficients a = model.getA(location, date); + final FieldPressureTemperatureHumidity pth = model.getWeatherParamerers(location, date); + final FieldAzimuthalGradientCoefficients gradient = model.getGradientCoefficients(location, date); + + Assertions.assertEquals(0.0012594, a.getAh().getReal(), 1.0e-7); + Assertions.assertEquals(0.0005648, a.getAw().getReal(), 1.0e-7); + Assertions.assertEquals(273.15 + 21.50, pth.getTemperature().getReal(), 1.0e-2); + Assertions.assertEquals(1000.72, TroposphericModelUtils.HECTO_PASCAL.fromSI(pth.getPressure()).getReal(), 1.0e-2); + Assertions.assertEquals(16.38, TroposphericModelUtils.HECTO_PASCAL.fromSI(pth.getWaterVaporPressure()).getReal(), 1.0e-2); + Assertions.assertEquals(273.15 + 9.41, pth.getTm().getReal(), 1.0e-2); + Assertions.assertEquals(3.105482, pth.getLambda().getReal(), 1.0e-6); + Assertions.assertEquals(-18.5382468, gradient.getGnh().getReal(), 1.0e-7); + Assertions.assertEquals( 0.4685513, gradient.getGeh().getReal(), 1.0e-7); + Assertions.assertEquals( -4.4695832, gradient.getGnw().getReal(), 1.0e-7); + Assertions.assertEquals( 0.1445528, gradient.getGew().getReal(), 1.0e-7); + + } + + @Test + public void testEquality() throws IOException, URISyntaxException { + doTestEquality("gpt-grid/gpt3_15.grd"); + } + + private void doTestEquality(final String resourceName) throws IOException, URISyntaxException { + + Utils.setDataRoot("regular-data"); + + // Commons parameters + final AbsoluteDate date = AbsoluteDate.createMJDDate(56141, 0.0, TimeScalesFactory.getUTC()); + final double latitude = FastMath.toRadians(45.0); + final double height = 0.0; + + final URL url = GlobalPressureTemperature3Test.class.getClassLoader().getResource(resourceName); + GlobalPressureTemperature3 model = new GlobalPressureTemperature3(new DataSource(url.toURI()), + TimeScalesFactory.getUTC()); + + // Test longitude = 181° and longitude = -179° + GeodeticPoint location1 = new GeodeticPoint(latitude, FastMath.toRadians(181.0), height); + ViennaACoefficients a1 = model.getA(location1, date); + PressureTemperatureHumidity pth1 = model.getWeatherParamerers(location1, date); + GeodeticPoint location2 = new GeodeticPoint(latitude, FastMath.toRadians(-179.0), height); + ViennaACoefficients a2 = model.getA(location2, date); + PressureTemperatureHumidity pth2 = model.getWeatherParamerers(location2, date); + + Assertions.assertEquals(pth1.getTemperature(), pth2.getTemperature(), epsilon); + Assertions.assertEquals(pth1.getPressure(), pth2.getPressure(), epsilon); + Assertions.assertEquals(pth1.getWaterVaporPressure(), pth2.getWaterVaporPressure(), epsilon); + Assertions.assertEquals(a1.getAh(), a2.getAh(), epsilon); + Assertions.assertEquals(a1.getAw(), a2.getAw(), epsilon); + + // Test longitude = 180° and longitude = -180° + location1 = new GeodeticPoint(latitude, FastMath.toRadians(180.0), height); + a1 = model.getA(location1, date); + pth1 = model.getWeatherParamerers(location1, date); + location2 = new GeodeticPoint(latitude, FastMath.toRadians(-180.0), height); + a2 = model.getA(location2, date); + pth2 = model.getWeatherParamerers(location2, date); + + Assertions.assertEquals(pth1.getTemperature(), pth2.getTemperature(), epsilon); + Assertions.assertEquals(pth1.getPressure(), pth2.getPressure(), epsilon); + Assertions.assertEquals(pth1.getWaterVaporPressure(), pth2.getWaterVaporPressure(), epsilon); + Assertions.assertEquals(a1.getAh(), a2.getAh(), epsilon); + Assertions.assertEquals(a1.getAw(), a2.getAw(), epsilon); + + // Test longitude = 0° and longitude = 360° + location1 = new GeodeticPoint(latitude, FastMath.toRadians(0.0), height); + a1 = model.getA(location1, date); + pth1 = model.getWeatherParamerers(location1, date); + location2 = new GeodeticPoint(latitude, FastMath.toRadians(360.0), height); + a2 = model.getA(location2, date); + pth2 = model.getWeatherParamerers(location2, date); + + Assertions.assertEquals(pth1.getTemperature(), pth2.getTemperature(), epsilon); + Assertions.assertEquals(pth1.getPressure(), pth2.getPressure(), epsilon); + Assertions.assertEquals(pth1.getWaterVaporPressure(), pth2.getWaterVaporPressure(), epsilon); + Assertions.assertEquals(a1.getAh(), a2.getAh(), epsilon); + Assertions.assertEquals(a1.getAw(), a2.getAw(), epsilon); + + } + + @Test + public void testCorruptedFileBadData() throws IOException, URISyntaxException { + + Utils.setDataRoot("regular-data"); + + final String fileName = "corrupted-bad-data-gpt3_15.grd"; + final URL url = GlobalPressureTemperature3Test.class.getClassLoader().getResource("gpt-grid/" + fileName); + try { + new GlobalPressureTemperature3(new DataSource(url.toURI()), TimeScalesFactory.getUTC()); + Assertions.fail("An exception should have been thrown"); + } catch (OrekitException oe) { + Assertions.assertEquals(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, oe.getSpecifier()); + Assertions.assertEquals(6, ((Integer) oe.getParts()[0]).intValue()); + Assertions.assertTrue(((String) oe.getParts()[1]).endsWith(fileName)); + } + + } + + @Test + public void testCorruptedIrregularGrid() throws IOException, URISyntaxException { + + Utils.setDataRoot("regular-data"); + + final String fileName = "corrupted-irregular-grid-gpt3_15.grd"; + final URL url = GlobalPressureTemperature3Test.class.getClassLoader().getResource("gpt-grid/" + fileName); + try { + new GlobalPressureTemperature3(new DataSource(url.toURI()), TimeScalesFactory.getUTC()); + Assertions.fail("An exception should have been thrown"); + } catch (OrekitException oe) { + Assertions.assertEquals(OrekitMessages.IRREGULAR_OR_INCOMPLETE_GRID, oe.getSpecifier()); + Assertions.assertTrue(((String) oe.getParts()[0]).endsWith(fileName)); + } + + } + + @Test + public void testCorruptedIncompleteHeader() throws IOException, URISyntaxException { + + Utils.setDataRoot("regular-data"); + + final String fileName = "corrupted-incomplete-header.grd"; + final URL url = GlobalPressureTemperature3Test.class.getClassLoader().getResource("gpt-grid/" + fileName); + try { + new GlobalPressureTemperature3(new DataSource(url.toURI()), TimeScalesFactory.getUTC()); + Assertions.fail("An exception should have been thrown"); + } catch (OrekitException oe) { + Assertions.assertEquals(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, oe.getSpecifier()); + Assertions.assertEquals(1, ((Integer) oe.getParts()[0]).intValue()); + Assertions.assertTrue(((String) oe.getParts()[1]).endsWith(fileName)); + } + + } + + @Test + public void testCorruptedMissingSeasonalColumns() throws IOException, URISyntaxException { + + Utils.setDataRoot("regular-data"); + + final String fileName = "corrupted-missing-seasonal-columns-gpt3_15.grd"; + final URL url = GlobalPressureTemperature3Test.class.getClassLoader().getResource("gpt-grid/" + fileName); + try { + new GlobalPressureTemperature3(new DataSource(url.toURI()), TimeScalesFactory.getUTC()); + Assertions.fail("An exception should have been thrown"); + } catch (OrekitException oe) { + Assertions.assertEquals(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, oe.getSpecifier()); + Assertions.assertEquals(1, ((Integer) oe.getParts()[0]).intValue()); + Assertions.assertTrue(((String) oe.getParts()[1]).endsWith(fileName)); + } + + } + + @Test + public void testCorruptedMissingDataFields() throws IOException, URISyntaxException { + + Utils.setDataRoot("regular-data"); + + final String fileName = "corrupted-missing-data-fields-gpt3_15.grd"; + final URL url = GlobalPressureTemperature3Test.class.getClassLoader().getResource("gpt-grid/" + fileName); + try { + new GlobalPressureTemperature3(new DataSource(url.toURI()), TimeScalesFactory.getUTC()); + Assertions.fail("An exception should have been thrown"); + } catch (OrekitException oe) { + Assertions.assertEquals(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, oe.getSpecifier()); + Assertions.assertEquals(4, ((Integer) oe.getParts()[0]).intValue()); + Assertions.assertTrue(((String) oe.getParts()[1]).endsWith(fileName)); + } + + } + +} diff --git a/src/test/java/org/orekit/models/earth/weather/GlobalPressureTemperatureModelTest.java b/src/test/java/org/orekit/models/earth/weather/GlobalPressureTemperatureModelTest.java index 7b8d9ef17e..1f0457a913 100644 --- a/src/test/java/org/orekit/models/earth/weather/GlobalPressureTemperatureModelTest.java +++ b/src/test/java/org/orekit/models/earth/weather/GlobalPressureTemperatureModelTest.java @@ -29,6 +29,7 @@ import org.orekit.time.TimeScalesFactory; import org.orekit.utils.IERSConventions; +@Deprecated public class GlobalPressureTemperatureModelTest { @BeforeEach diff --git a/src/test/java/org/orekit/models/earth/weather/GlobalPressureTemperatureTest.java b/src/test/java/org/orekit/models/earth/weather/GlobalPressureTemperatureTest.java new file mode 100644 index 0000000000..b9f3d067a0 --- /dev/null +++ b/src/test/java/org/orekit/models/earth/weather/GlobalPressureTemperatureTest.java @@ -0,0 +1,148 @@ +/* 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.models.earth.weather; + +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.GeodeticPoint; +import org.orekit.data.DataContext; +import org.orekit.errors.OrekitException; +import org.orekit.forces.gravity.potential.GRGSFormatReader; +import org.orekit.forces.gravity.potential.GravityFieldFactory; +import org.orekit.frames.FramesFactory; +import org.orekit.models.earth.Geoid; +import org.orekit.models.earth.ReferenceEllipsoid; +import org.orekit.models.earth.troposphere.TroposphericModelUtils; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.TimeScalesFactory; +import org.orekit.utils.IERSConventions; + +public class GlobalPressureTemperatureTest { + + @BeforeEach + public void setUp() throws OrekitException { + Utils.setDataRoot("regular-data:potential"); + GravityFieldFactory.addPotentialCoefficientsReader(new GRGSFormatReader("grim4s4_gr", true)); + } + + @Test + public void testParameterComputation() { + + // Site Toulouse, Cité de l'Espace (France): latitude: 43.59°N + // longitude: 1.49°E + // height: 140 m + // + // Date: 09 January 2019 at 0h UT + // + // Expected outputs are obtained by performing the Matlab script gpt.m provided by TU WIEN: + // http://vmf.geo.tuwien.ac.at/codes/ + // + // Expected parameters : temperature -> 7.3311 °C + // pressure -> 1010.2749 hPa + // + // The real weather conditions are obtained with www.infoclimat.fr + // + // Real weather conditions: temperature -> 7.3 °C + // pressure -> 1027.5 hPa + + final AbsoluteDate date = new AbsoluteDate(2019, 1, 8, 0, 0, 0.0, TimeScalesFactory.getUTC()); + final GeodeticPoint location = new GeodeticPoint(FastMath.toRadians(43.59), + FastMath.toRadians(1.49), + 140.0); + + // Given by the model + final double expectedTemperature = 7.3311; + final double expectedPressure = 1010.2749; + + final DataContext dataContext = DataContext.getDefault(); + final Geoid geoid = new Geoid(dataContext.getGravityFields().getNormalizedProvider(9, 9), + ReferenceEllipsoid.getWgs84(FramesFactory.getITRF(IERSConventions.IERS_2010, true))); + final GlobalPressureTemperature model = new GlobalPressureTemperature(geoid, + dataContext.getTimeScales().getUTC()); + PressureTemperature pt = model.getWeatherParameters(location, date); + + final double computedTemperature = pt.getTemperature() - 273.15; + final double computedPressure = TroposphericModelUtils.HECTO_PASCAL.fromSI(pt.getPressure()); + + Assertions.assertEquals(expectedPressure, computedPressure, 0.1); + Assertions.assertEquals(expectedTemperature, computedTemperature, 0.1); + + // Real weather conditions + final double realTemperature = 7.3; + final double realPressure = 1027.5; + + // We test the model accuracy (10°C and 20 hPa) + Assertions.assertEquals(realTemperature, computedTemperature, 10); + Assertions.assertEquals(realPressure, computedPressure, 20); + } + + @Test + public void testHighAltitude() { + + // Site Pic du Midi de Bigorre (France): latitude: 42.94°N + // longitude: 0.14°E + // height: 2877 m + // + // Date: 09 January 2019 at 0h UT + // + // Expected outputs are obtained by performing the Matlab script gpt.m provided by TU WIEN: + // http://vmf.geo.tuwien.ac.at/codes/ + // + // Expected parameters : temperature -> -9.88 °C + // pressure -> 723.33 hPa + // + // The real weather conditions are obtained by the Laboratoire d'Aérologie de l'Observatoire Midi Pyrénées + // + // Real weather conditions: temperature -> -8.3 °C + // pressure -> 717.9 hPa + + final AbsoluteDate date = new AbsoluteDate(2019, 1, 8, 0, 0, 0.0, TimeScalesFactory.getUTC()); + final GeodeticPoint location = new GeodeticPoint(FastMath.toRadians(42.94), + FastMath.toRadians(0.14), + 2877); + + // Given by the model + final double expectedTemperature = -9.88; + final double expectedPressure = 723.33; + + final DataContext dataContext = DataContext.getDefault(); + final Geoid geoid = new Geoid(dataContext.getGravityFields().getNormalizedProvider(9, 9), + ReferenceEllipsoid.getWgs84(FramesFactory.getITRF(IERSConventions.IERS_2010, true))); + final GlobalPressureTemperature model = new GlobalPressureTemperature(geoid, + dataContext.getTimeScales().getUTC()); + PressureTemperature pt = model.getWeatherParameters(location, date); + + + final double computedTemperature = pt.getTemperature() - 273.15; + final double computedPressure = TroposphericModelUtils.HECTO_PASCAL.fromSI(pt.getPressure()); + + Assertions.assertEquals(expectedPressure, computedPressure, 0.1); + Assertions.assertEquals(expectedTemperature, computedTemperature, 0.1); + + // Real weather conditions + final double realTemperature = -8.3; + final double realPressure = 717.9; + + // We test the model accuracy (10°C and 20 hPa) + Assertions.assertEquals(realTemperature, computedTemperature, 10); + Assertions.assertEquals(realPressure, computedPressure, 20); + } + +} diff --git a/src/test/java/org/orekit/models/earth/weather/water/AbstractWaterVaporPressureProviderTest.java b/src/test/java/org/orekit/models/earth/weather/water/AbstractWaterVaporPressureProviderTest.java new file mode 100644 index 0000000000..ec47bbe648 --- /dev/null +++ b/src/test/java/org/orekit/models/earth/weather/water/AbstractWaterVaporPressureProviderTest.java @@ -0,0 +1,89 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.weather.water; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.util.Binary64Field; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.orekit.models.earth.troposphere.TroposphericModelUtils; + +public abstract class AbstractWaterVaporPressureProviderTest { + + protected abstract WaterVaporPressureProvider buildProvider(); + + @Test + public abstract void testReferenceWaterVaporPressure(); + + protected void doTestReferenceWaterVaporPressure(final double tolerance) { + // the reference value is from NBS/NRC steam table + final WaterVaporPressureProvider provider = buildProvider(); + Assertions.assertEquals(TroposphericModelUtils.HECTO_PASCAL.toSI(10.55154), + provider.waterVaporPressure(TroposphericModelUtils.HECTO_PASCAL.toSI(1013.25), 273.5 + 18, 0.5), + tolerance); + } + + @Test + public abstract void testReferenceWaterVaporPressureField(); + + protected > void doTestReferenceWaterVaporPressureField(final Field field, + final double tolerance) { + // the reference value is from NBS/NRC steam table + final WaterVaporPressureProvider provider = buildProvider(); + Assertions.assertEquals(TroposphericModelUtils.HECTO_PASCAL.toSI(10.55154), + provider.waterVaporPressure(TroposphericModelUtils.HECTO_PASCAL.toSI(field.getZero().newInstance(1013.25)), + field.getZero().newInstance(273.5 + 18), + field.getZero().newInstance(0.5)).getReal(), + tolerance); + } + + @Test + public void testRelativeHumidity() { + final WaterVaporPressureProvider provider = buildProvider(); + for (double pPa = 700; pPa < 1100; pPa += 0.5) { + final double p = TroposphericModelUtils.HECTO_PASCAL.toSI(pPa); + for (double tC = 0.01; tC < 99; tC += 0.25) { + final double t = 273.15 + tC; + for (double rH = 0.0; rH < 1.0; rH += 0.02) { + final double e = provider.waterVaporPressure(p, t, rH); + Assertions.assertEquals(rH, provider.relativeHumidity(p, t, e), 1.0e-10); + } + } + } + } + + @Test + public void testRelativeHumidityField() { + doTestRelativeHumidityField(Binary64Field.getInstance()); + } + + private > void doTestRelativeHumidityField(final Field field) { + final WaterVaporPressureProvider provider = buildProvider(); + for (double pPa = 700; pPa < 1100; pPa += 0.5) { + final T p = TroposphericModelUtils.HECTO_PASCAL.toSI(field.getZero().newInstance(pPa)); + for (double tC = 0.01; tC < 99; tC += 0.25) { + final T t = field.getZero().newInstance(273.15 + tC); + for (double rH = 0.0; rH < 1.0; rH += 0.02) { + final T e = provider.waterVaporPressure(p, t, field.getZero().newInstance(rH)); + Assertions.assertEquals(rH, provider.relativeHumidity(p, t, e).getReal(), 1.0e-10); + } + } + } + } + +} diff --git a/src/test/java/org/orekit/models/earth/weather/water/CIPM2007Test.java b/src/test/java/org/orekit/models/earth/weather/water/CIPM2007Test.java new file mode 100644 index 0000000000..9abb4cfae3 --- /dev/null +++ b/src/test/java/org/orekit/models/earth/weather/water/CIPM2007Test.java @@ -0,0 +1,38 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.weather.water; + +import org.hipparchus.util.Binary64Field; +import org.junit.jupiter.api.Test; + +public class CIPM2007Test extends AbstractWaterVaporPressureProviderTest { + + protected WaterVaporPressureProvider buildProvider() { + return new CIPM2007(); + } + + @Test + public void testReferenceWaterVaporPressure() { + doTestReferenceWaterVaporPressure(5.0); + } + + @Test + public void testReferenceWaterVaporPressureField() { + doTestReferenceWaterVaporPressureField(Binary64Field.getInstance(), 5.0); + } + +} diff --git a/src/test/java/org/orekit/models/earth/weather/water/NbsNrcSteamTableTest.java b/src/test/java/org/orekit/models/earth/weather/water/NbsNrcSteamTableTest.java new file mode 100644 index 0000000000..277143150a --- /dev/null +++ b/src/test/java/org/orekit/models/earth/weather/water/NbsNrcSteamTableTest.java @@ -0,0 +1,38 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.weather.water; + +import org.hipparchus.util.Binary64Field; +import org.junit.jupiter.api.Test; + +public class NbsNrcSteamTableTest extends AbstractWaterVaporPressureProviderTest { + + protected WaterVaporPressureProvider buildProvider() { + return new NbsNrcSteamTable(); + } + + @Test + public void testReferenceWaterVaporPressure() { + doTestReferenceWaterVaporPressure(1.0e-3); + } + + @Test + public void testReferenceWaterVaporPressureField() { + doTestReferenceWaterVaporPressureField(Binary64Field.getInstance(), 1.0e-3); + } + +} diff --git a/src/test/java/org/orekit/models/earth/weather/water/Wang1988Test.java b/src/test/java/org/orekit/models/earth/weather/water/Wang1988Test.java new file mode 100644 index 0000000000..4fabc9573b --- /dev/null +++ b/src/test/java/org/orekit/models/earth/weather/water/Wang1988Test.java @@ -0,0 +1,38 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.models.earth.weather.water; + +import org.hipparchus.util.Binary64Field; +import org.junit.jupiter.api.Test; + +public class Wang1988Test extends AbstractWaterVaporPressureProviderTest { + + protected WaterVaporPressureProvider buildProvider() { + return new Wang1988(); + } + + @Test + public void testReferenceWaterVaporPressure() { + doTestReferenceWaterVaporPressure(13.0); + } + + @Test + public void testReferenceWaterVaporPressureField() { + doTestReferenceWaterVaporPressureField(Binary64Field.getInstance(), 13.0); + } + +} diff --git a/src/test/java/org/orekit/orbits/CircularLatitudeArgumentUtilityTest.java b/src/test/java/org/orekit/orbits/CircularLatitudeArgumentUtilityTest.java new file mode 100644 index 0000000000..120a496acd --- /dev/null +++ b/src/test/java/org/orekit/orbits/CircularLatitudeArgumentUtilityTest.java @@ -0,0 +1,78 @@ +/* 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.orbits; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; + +class CircularLatitudeArgumentUtilityTest { + + private static final double EX = 0.1; + private static final double EY = 0.66; + private static final double TOLERANCE = 1e-10; + + @Test + void testMeanToTrueAndBack() { + // GIVEN + final double expectedLatitudeArgument = 3.; + // WHEN + final double intermediateLatitudeArgument = CircularLatitudeArgumentUtility.meanToTrue(EX, EY, + expectedLatitudeArgument); + final double actualLatitudeArgument = CircularLatitudeArgumentUtility.trueToMean(EX, EY, + intermediateLatitudeArgument); + // THEN + Assertions.assertEquals(expectedLatitudeArgument, actualLatitudeArgument, TOLERANCE); + } + + @Test + void testEccentricToTrueAndBack() { + // GIVEN + final double expectedLatitudeArgument = 3.; + // WHEN + final double intermediateLatitudeArgument = CircularLatitudeArgumentUtility.eccentricToTrue(EX, EY, + expectedLatitudeArgument); + final double actualLatitudeArgument = CircularLatitudeArgumentUtility.trueToEccentric(EX, EY, + intermediateLatitudeArgument); + // THEN + Assertions.assertEquals(expectedLatitudeArgument, actualLatitudeArgument, TOLERANCE); + } + + @Test + void testEccentricToMeanAndBack() { + // GIVEN + final double expectedLatitudeArgument = 3.; + // WHEN + final double intermediateLatitudeArgument = CircularLatitudeArgumentUtility.eccentricToMean(EX, EY, + expectedLatitudeArgument); + final double actualLatitudeArgument = CircularLatitudeArgumentUtility.meanToEccentric(EX, EY, + intermediateLatitudeArgument); + // THEN + Assertions.assertEquals(expectedLatitudeArgument, actualLatitudeArgument, TOLERANCE); + } + + @Test + void testMeanToEccentricException() { + // GIVEN + final double nanLatitudeArgument = Double.NaN; + // WHEN & THEN + Assertions.assertThrows(OrekitException.class, () -> CircularLatitudeArgumentUtility.meanToEccentric(EX, EY, + nanLatitudeArgument), OrekitMessages.UNABLE_TO_COMPUTE_ECCENTRIC_LATITUDE_ARGUMENT.toString()); + } + +} \ No newline at end of file diff --git a/src/test/java/org/orekit/orbits/CircularOrbitTest.java b/src/test/java/org/orekit/orbits/CircularOrbitTest.java index cd16ba5b43..c39fb74216 100644 --- a/src/test/java/org/orekit/orbits/CircularOrbitTest.java +++ b/src/test/java/org/orekit/orbits/CircularOrbitTest.java @@ -52,7 +52,7 @@ import static org.orekit.OrekitMatchers.relativelyCloseTo; -public class CircularOrbitTest { +class CircularOrbitTest { // Computation date private AbsoluteDate date; @@ -61,7 +61,7 @@ public class CircularOrbitTest { private double mu; @Test - public void testCircularToEquinoctialEll() { + void testCircularToEquinoctialEll() { double ix = 1.200e-04; double iy = -1.16e-04; @@ -70,9 +70,9 @@ public void testCircularToEquinoctialEll() { // elliptic orbit CircularOrbit circ = - new CircularOrbit(42166712.0, 0.5, -0.5, i, raan, - 5.300 - raan, PositionAngleType.MEAN, - FramesFactory.getEME2000(), date, mu); + new CircularOrbit(42166712.0, 0.5, -0.5, i, raan, + 5.300 - raan, PositionAngleType.MEAN, + FramesFactory.getEME2000(), date, mu); Vector3D pos = circ.getPosition(); Vector3D vit = circ.getPVCoordinates().getVelocity(); @@ -89,7 +89,7 @@ public void testCircularToEquinoctialEll() { } @Test - public void testCircularToEquinoctialCirc() { + void testCircularToEquinoctialCirc() { double ix = 1.200e-04; double iy = -1.16e-04; @@ -98,9 +98,9 @@ public void testCircularToEquinoctialCirc() { // circular orbit EquinoctialOrbit circCir = - new EquinoctialOrbit(42166712.0, 0.1e-10, -0.1e-10, i, raan, - 5.300 - raan, PositionAngleType.MEAN, - FramesFactory.getEME2000(), date, mu); + new EquinoctialOrbit(42166712.0, 0.1e-10, -0.1e-10, i, raan, + 5.300 - raan, PositionAngleType.MEAN, + FramesFactory.getEME2000(), date, mu); Vector3D posCir = circCir.getPosition(); Vector3D vitCir = circCir.getPVCoordinates().getVelocity(); @@ -117,7 +117,7 @@ public void testCircularToEquinoctialCirc() { } @Test - public void testCircularToCartesian() { + void testCircularToCartesian() { double ix = 1.200e-04; double iy = -1.16e-04; @@ -131,9 +131,9 @@ public void testCircularToCartesian() { double ey = eyTilde * cosRaan - exTilde * sinRaan; CircularOrbit circ= - new CircularOrbit(42166712.0, ex, ey, i, raan, - 5.300 - raan, PositionAngleType.MEAN, - FramesFactory.getEME2000(), date, mu); + new CircularOrbit(42166712.0, ex, ey, i, raan, + 5.300 - raan, PositionAngleType.MEAN, + FramesFactory.getEME2000(), date, mu); Vector3D pos = circ.getPosition(); Vector3D vel = circ.getPVCoordinates().getVelocity(); @@ -153,7 +153,7 @@ public void testCircularToCartesian() { } @Test - public void testCircularToKeplerian() { + void testCircularToKeplerian() { double ix = 1.20e-4; double iy = -1.16e-4; @@ -167,50 +167,52 @@ public void testCircularToKeplerian() { double ey = eyTilde * cosRaan - exTilde * sinRaan; CircularOrbit circ= - new CircularOrbit(42166712.0, ex, ey, i, raan, - 5.300 - raan, PositionAngleType.MEAN, - FramesFactory.getEME2000(), date, mu); + new CircularOrbit(42166712.0, ex, ey, i, raan, + 5.300 - raan, PositionAngleType.MEAN, + FramesFactory.getEME2000(), date, mu); KeplerianOrbit kep = new KeplerianOrbit(circ); Assertions.assertEquals(42166712.000, circ.getA(), Utils.epsilonTest * kep.getA()); Assertions.assertEquals(0.110283316961361e-03, kep.getE(), Utils.epsilonE * FastMath.abs(kep.getE())); Assertions.assertEquals(0.166901168553917e-03, kep.getI(), - Utils.epsilonAngle * FastMath.abs(kep.getI())); + Utils.epsilonAngle * FastMath.abs(kep.getI())); Assertions.assertEquals(MathUtils.normalizeAngle(-3.87224326008837, kep.getPerigeeArgument()), - kep.getPerigeeArgument(), - Utils.epsilonTest * FastMath.abs(kep.getPerigeeArgument())); + kep.getPerigeeArgument(), + Utils.epsilonTest * FastMath.abs(kep.getPerigeeArgument())); Assertions.assertEquals(MathUtils.normalizeAngle(5.51473467358854, kep.getRightAscensionOfAscendingNode()), - kep.getRightAscensionOfAscendingNode(), - Utils.epsilonTest * FastMath.abs(kep.getRightAscensionOfAscendingNode())); + kep.getRightAscensionOfAscendingNode(), + Utils.epsilonTest * FastMath.abs(kep.getRightAscensionOfAscendingNode())); Assertions.assertEquals(MathUtils.normalizeAngle(3.65750858649982, kep.getMeanAnomaly()), - kep.getMeanAnomaly(), - Utils.epsilonTest * FastMath.abs(kep.getMeanAnomaly())); + kep.getMeanAnomaly(), + Utils.epsilonTest * FastMath.abs(kep.getMeanAnomaly())); } @Test - public void testHyperbolic1() { + void testHyperbolic1() { try { - new CircularOrbit(42166712.0, 0.9, 0.5, 0.01, -0.02, 5.300, - PositionAngleType.MEAN, FramesFactory.getEME2000(), date, mu); + new CircularOrbit(-42166712.0, 1.9, 0.5, 0.01, -0.02, 5.300, + PositionAngleType.MEAN, FramesFactory.getEME2000(), date, mu); + Assertions.fail("an exception should have been thrown"); } catch (OrekitIllegalArgumentException oe) { Assertions.assertEquals(OrekitMessages.HYPERBOLIC_ORBIT_NOT_HANDLED_AS, oe.getSpecifier()); } } @Test - public void testHyperbolic2() { - Orbit orbit = new KeplerianOrbit(42166712.0, 0.9, 0.5, 0.01, -0.02, 5.300, - PositionAngleType.MEAN, FramesFactory.getEME2000(), date, mu); + void testHyperbolic2() { + Orbit orbit = new KeplerianOrbit(-42166712.0, 1.9, 0.5, 0.01, -0.02, 5.300, + PositionAngleType.MEAN, FramesFactory.getEME2000(), date, mu); try { new CircularOrbit(orbit.getPVCoordinates(), orbit.getFrame(), orbit.getMu()); + Assertions.fail("an exception should have been thrown"); } catch (OrekitIllegalArgumentException oe) { Assertions.assertEquals(OrekitMessages.HYPERBOLIC_ORBIT_NOT_HANDLED_AS, oe.getSpecifier()); } } @Test - public void testAnomalyEll() { + void testAnomalyEll() { // elliptic orbit Vector3D position = new Vector3D(7.0e6, 1.0e6, 4.0e6); @@ -232,29 +234,29 @@ public void testAnomalyEll() { double lM = lE - e * FastMath.sin(lE - paPraan); p = new CircularOrbit(p.getA() , p.getCircularEx(), p.getCircularEy(), - p.getRightAscensionOfAscendingNode(), - p.getAlphaV(), lv - raan, PositionAngleType.TRUE, p.getFrame(), date, mu); + p.getRightAscensionOfAscendingNode(), + p.getAlphaV(), lv - raan, PositionAngleType.TRUE, p.getFrame(), date, mu); Assertions.assertEquals(p.getAlphaV() + raan, lv, Utils.epsilonAngle * FastMath.abs(lv)); Assertions.assertEquals(p.getAlphaE() + raan, lE, Utils.epsilonAngle * FastMath.abs(lE)); Assertions.assertEquals(p.getAlphaM() + raan, lM, Utils.epsilonAngle * FastMath.abs(lM)); p = new CircularOrbit(p.getA() , p.getCircularEx(), p.getCircularEy(), - p.getRightAscensionOfAscendingNode(), - p.getAlphaV(), 0, PositionAngleType.TRUE, p.getFrame(), date, mu); + p.getRightAscensionOfAscendingNode(), + p.getAlphaV(), 0, PositionAngleType.TRUE, p.getFrame(), date, mu); p = new CircularOrbit(p.getA() , p.getCircularEx(), p.getCircularEy(), - p.getRightAscensionOfAscendingNode(), - p.getAlphaV(), lE - raan, PositionAngleType.ECCENTRIC, p.getFrame(), date, mu); + p.getRightAscensionOfAscendingNode(), + p.getAlphaV(), lE - raan, PositionAngleType.ECCENTRIC, p.getFrame(), date, mu); Assertions.assertEquals(p.getAlphaV() + raan, lv, Utils.epsilonAngle * FastMath.abs(lv)); Assertions.assertEquals(p.getAlphaE() + raan, lE, Utils.epsilonAngle * FastMath.abs(lE)); Assertions.assertEquals(p.getAlphaM() + raan, lM, Utils.epsilonAngle * FastMath.abs(lM)); p = new CircularOrbit(p.getA() , p.getCircularEx(), p.getCircularEy(), - p.getRightAscensionOfAscendingNode(), - p.getAlphaV(), 0, PositionAngleType.TRUE, p.getFrame(), date, mu); + p.getRightAscensionOfAscendingNode(), + p.getAlphaV(), 0, PositionAngleType.TRUE, p.getFrame(), date, mu); p = new CircularOrbit(p.getA() , p.getCircularEx(), p.getCircularEy(), - p.getRightAscensionOfAscendingNode(), - p.getAlphaV(), lM - raan, PositionAngleType.MEAN, p.getFrame(), date, mu); + p.getRightAscensionOfAscendingNode(), + p.getAlphaV(), lM - raan, PositionAngleType.MEAN, p.getFrame(), date, mu); Assertions.assertEquals(p.getAlphaV() + raan, lv, Utils.epsilonAngle * FastMath.abs(lv)); Assertions.assertEquals(p.getAlphaE() + raan, lE, Utils.epsilonAngle * FastMath.abs(lE)); Assertions.assertEquals(p.getAlphaM() + raan, lM, Utils.epsilonAngle * FastMath.abs(lM)); @@ -262,7 +264,7 @@ public void testAnomalyEll() { } @Test - public void testAnomalyCirc() { + void testAnomalyCirc() { Vector3D position = new Vector3D(7.0e6, 1.0e6, 4.0e6); Vector3D velocity = new Vector3D(-500.0, 8000.0, 1000.0); @@ -272,36 +274,36 @@ public void testAnomalyCirc() { // circular orbit p = new CircularOrbit(p.getA() , 0, 0, p.getRightAscensionOfAscendingNode(), - p.getAlphaV(), p.getAlphaV(), PositionAngleType.TRUE, p.getFrame(), date, mu); + p.getAlphaV(), p.getAlphaV(), PositionAngleType.TRUE, p.getFrame(), date, mu); double lv = 1.1; double lE = lv; double lM = lE; p = new CircularOrbit(p.getA() , p.getCircularEx(), p.getCircularEy(), - p.getRightAscensionOfAscendingNode(), - p.getAlphaV(), lv - raan, PositionAngleType.TRUE, p.getFrame(), date, mu); + p.getRightAscensionOfAscendingNode(), + p.getAlphaV(), lv - raan, PositionAngleType.TRUE, p.getFrame(), date, mu); Assertions.assertEquals(p.getAlphaV() + raan, lv, Utils.epsilonAngle * FastMath.abs(lv)); Assertions.assertEquals(p.getAlphaE() + raan, lE, Utils.epsilonAngle * FastMath.abs(lE)); Assertions.assertEquals(p.getAlphaM() + raan, lM, Utils.epsilonAngle * FastMath.abs(lM)); p = new CircularOrbit(p.getA() , p.getCircularEx(), p.getCircularEy(), - p.getRightAscensionOfAscendingNode(), - p.getAlphaV(), 0, PositionAngleType.TRUE, p.getFrame(), date, mu); + p.getRightAscensionOfAscendingNode(), + p.getAlphaV(), 0, PositionAngleType.TRUE, p.getFrame(), date, mu); p = new CircularOrbit(p.getA() , p.getCircularEx(), p.getCircularEy(), - p.getRightAscensionOfAscendingNode(), - p.getAlphaV(), lE - raan, PositionAngleType.ECCENTRIC, p.getFrame(), date, mu); + p.getRightAscensionOfAscendingNode(), + p.getAlphaV(), lE - raan, PositionAngleType.ECCENTRIC, p.getFrame(), date, mu); Assertions.assertEquals(p.getAlphaV() + raan, lv, Utils.epsilonAngle * FastMath.abs(lv)); Assertions.assertEquals(p.getAlphaE() + raan, lE, Utils.epsilonAngle * FastMath.abs(lE)); Assertions.assertEquals(p.getAlphaM() + raan, lM, Utils.epsilonAngle * FastMath.abs(lM)); p = new CircularOrbit(p.getA() , p.getCircularEx(), p.getCircularEy(), - p.getRightAscensionOfAscendingNode(), - p.getAlphaV(), 0, PositionAngleType.TRUE, p.getFrame(), date, mu); + p.getRightAscensionOfAscendingNode(), + p.getAlphaV(), 0, PositionAngleType.TRUE, p.getFrame(), date, mu); p = new CircularOrbit(p.getA() , p.getCircularEx(), p.getCircularEy(), - p.getRightAscensionOfAscendingNode(), - p.getAlphaV(), lM - raan, PositionAngleType.MEAN, p.getFrame(), date, mu); + p.getRightAscensionOfAscendingNode(), + p.getAlphaV(), lM - raan, PositionAngleType.MEAN, p.getFrame(), date, mu); Assertions.assertEquals(p.getAlphaV() + raan, lv, Utils.epsilonAngle * FastMath.abs(lv)); Assertions.assertEquals(p.getAlphaE() + raan, lE, Utils.epsilonAngle * FastMath.abs(lE)); Assertions.assertEquals(p.getAlphaM() + raan, lM, Utils.epsilonAngle * FastMath.abs(lM)); @@ -309,7 +311,7 @@ public void testAnomalyCirc() { } @Test - public void testPositionVelocityNormsEll() { + void testPositionVelocityNormsEll() { // elliptic and non equatorial (i retrograde) orbit double hx = 1.2; @@ -317,9 +319,9 @@ public void testPositionVelocityNormsEll() { double i = 2 * FastMath.atan(FastMath.sqrt(hx * hx + hy * hy)); double raan = FastMath.atan2(hy, hx); CircularOrbit p = - new CircularOrbit(42166712.0, 0.5, -0.5, i, raan, - 0.67 - raan, PositionAngleType.TRUE, - FramesFactory.getEME2000(), date, mu); + new CircularOrbit(42166712.0, 0.5, -0.5, i, raan, + 0.67 - raan, PositionAngleType.TRUE, + FramesFactory.getEME2000(), date, mu); double ex = p.getEquinoctialEx(); double ey = p.getEquinoctialEy(); @@ -332,41 +334,41 @@ public void testPositionVelocityNormsEll() { double na = FastMath.sqrt(mu / a); Assertions.assertEquals(a * epsilon * epsilon / ksi, - p.getPosition().getNorm(), - Utils.epsilonTest * FastMath.abs(p.getPosition().getNorm())); + p.getPosition().getNorm(), + Utils.epsilonTest * FastMath.abs(p.getPosition().getNorm())); Assertions.assertEquals(na * FastMath.sqrt(ksi * ksi + nu * nu) / epsilon, - p.getPVCoordinates().getVelocity().getNorm(), - Utils.epsilonTest * FastMath.abs(p.getPVCoordinates().getVelocity().getNorm())); + p.getPVCoordinates().getVelocity().getNorm(), + Utils.epsilonTest * FastMath.abs(p.getPVCoordinates().getVelocity().getNorm())); } @Test - public void testNumericalIssue25() { + void testNumericalIssue25() { Vector3D position = new Vector3D(3782116.14107698, 416663.11924914, 5875541.62103057); Vector3D velocity = new Vector3D(-6349.7848910501, 288.4061811651, 4066.9366759691); CircularOrbit orbit = new CircularOrbit(new PVCoordinates(position, velocity), - FramesFactory.getEME2000(), - new AbsoluteDate("2004-01-01T23:00:00.000", - TimeScalesFactory.getUTC()), - 3.986004415E14); + FramesFactory.getEME2000(), + new AbsoluteDate("2004-01-01T23:00:00.000", + TimeScalesFactory.getUTC()), + 3.986004415E14); Assertions.assertEquals(0.0, orbit.getE(), 2.0e-14); } @Test - public void testPerfectlyEquatorial() { + void testPerfectlyEquatorial() { Vector3D position = new Vector3D(-7293947.695148368, 5122184.668436634, 0.0); Vector3D velocity = new Vector3D(-3890.4029433398, -5369.811285264604, 0.0); CircularOrbit orbit = new CircularOrbit(new PVCoordinates(position, velocity), - FramesFactory.getEME2000(), - new AbsoluteDate("2004-01-01T23:00:00.000", - TimeScalesFactory.getUTC()), - 3.986004415E14); + FramesFactory.getEME2000(), + new AbsoluteDate("2004-01-01T23:00:00.000", + TimeScalesFactory.getUTC()), + 3.986004415E14); Assertions.assertEquals(0.0, orbit.getI(), 2.0e-14); Assertions.assertEquals(0.0, orbit.getRightAscensionOfAscendingNode(), 2.0e-14); } @Test - public void testPositionVelocityNormsCirc() { + void testPositionVelocityNormsCirc() { // elliptic and non equatorial (i retrograde) orbit double hx = 0.1e-8; @@ -374,9 +376,9 @@ public void testPositionVelocityNormsCirc() { double i = 2 * FastMath.atan(FastMath.sqrt(hx * hx + hy * hy)); double raan = FastMath.atan2(hy, hx); CircularOrbit pCirEqua = - new CircularOrbit(42166712.0, 0.1e-8, 0.1e-8, i, raan, - 0.67 - raan, PositionAngleType.TRUE, - FramesFactory.getEME2000(), date, mu); + new CircularOrbit(42166712.0, 0.1e-8, 0.1e-8, i, raan, + 0.67 - raan, PositionAngleType.TRUE, + FramesFactory.getEME2000(), date, mu); double ex = pCirEqua.getEquinoctialEx(); double ey = pCirEqua.getEquinoctialEy(); @@ -389,15 +391,15 @@ public void testPositionVelocityNormsCirc() { double na = FastMath.sqrt(mu / a); Assertions.assertEquals(a * epsilon * epsilon / ksi, - pCirEqua.getPosition().getNorm(), - Utils.epsilonTest * FastMath.abs(pCirEqua.getPosition().getNorm())); + pCirEqua.getPosition().getNorm(), + Utils.epsilonTest * FastMath.abs(pCirEqua.getPosition().getNorm())); Assertions.assertEquals(na * FastMath.sqrt(ksi * ksi + nu * nu) / epsilon, - pCirEqua.getPVCoordinates().getVelocity().getNorm(), - Utils.epsilonTest * FastMath.abs(pCirEqua.getPVCoordinates().getVelocity().getNorm())); + pCirEqua.getPVCoordinates().getVelocity().getNorm(), + Utils.epsilonTest * FastMath.abs(pCirEqua.getPVCoordinates().getVelocity().getNorm())); } @Test - public void testGeometryEll() { + void testGeometryEll() { // elliptic and non equatorial (i retrograde) orbit double hx = 1.2; @@ -405,9 +407,9 @@ public void testGeometryEll() { double i = 2 * FastMath.atan(FastMath.sqrt(hx * hx + hy * hy)); double raan = FastMath.atan2(hy, hx); CircularOrbit p = - new CircularOrbit(42166712.0, 0.5, -0.5, i, raan, - 0.67 - raan, PositionAngleType.TRUE, - FramesFactory.getEME2000(), date, mu); + new CircularOrbit(42166712.0, 0.5, -0.5, i, raan, + 0.67 - raan, PositionAngleType.TRUE, + FramesFactory.getEME2000(), date, mu); Vector3D position = p.getPosition(); Vector3D velocity = p.getPVCoordinates().getVelocity(); @@ -418,8 +420,8 @@ public void testGeometryEll() { for (double alphaV = 0; alphaV <= 2 * FastMath.PI; alphaV += 2 * FastMath.PI/100.) { p = new CircularOrbit(p.getA() , p.getCircularEx(), p.getCircularEy(), p.getI(), - p.getRightAscensionOfAscendingNode(), - alphaV, PositionAngleType.TRUE, p.getFrame(), date, mu); + p.getRightAscensionOfAscendingNode(), + alphaV, PositionAngleType.TRUE, p.getFrame(), date, mu); position = p.getPosition(); // test if the norm of the position is in the range [perigee radius, apogee radius] // Warning: these tests are without absolute value by choice @@ -441,7 +443,7 @@ public void testGeometryEll() { } @Test - public void testGeometryCirc() { + void testGeometryCirc() { // circular and equatorial orbit double hx = 0.1e-8; @@ -449,9 +451,9 @@ public void testGeometryCirc() { double i = 2 * FastMath.atan(FastMath.sqrt(hx * hx + hy * hy)); double raan = FastMath.atan2(hy, hx); CircularOrbit pCirEqua = - new CircularOrbit(42166712.0, 0.1e-8, 0.1e-8, i, raan, - 0.67 - raan, PositionAngleType.TRUE, - FramesFactory.getEME2000(), date, mu); + new CircularOrbit(42166712.0, 0.1e-8, 0.1e-8, i, raan, + 0.67 - raan, PositionAngleType.TRUE, + FramesFactory.getEME2000(), date, mu); Vector3D position = pCirEqua.getPosition(); Vector3D velocity = pCirEqua.getPVCoordinates().getVelocity(); @@ -464,8 +466,8 @@ public void testGeometryCirc() { for (double alphaV = 0; alphaV <= 2 * FastMath.PI; alphaV += 2 * FastMath.PI/100.) { pCirEqua = new CircularOrbit(pCirEqua.getA() , pCirEqua.getCircularEx(), pCirEqua.getCircularEy(), pCirEqua.getI(), - pCirEqua.getRightAscensionOfAscendingNode(), - alphaV, PositionAngleType.TRUE, pCirEqua.getFrame(), date, mu); + pCirEqua.getRightAscensionOfAscendingNode(), + alphaV, PositionAngleType.TRUE, pCirEqua.getFrame(), date, mu); position = pCirEqua.getPosition(); // test if the norm pf the position is in the range [perigee radius, apogee radius] @@ -486,7 +488,7 @@ public void testGeometryCirc() { } @Test - public void testSymmetryEll() { + void testSymmetryEll() { // elliptic and non equatorail orbit Vector3D position = new Vector3D(4512.9, 18260., -5127.); @@ -507,7 +509,7 @@ public void testSymmetryEll() { } @Test - public void testSymmetryCir() { + void testSymmetryCir() { // circular and equatorial orbit Vector3D position = new Vector3D(33051.2, 26184.9, -1.3E-5); Vector3D velocity = new Vector3D(-60376.2, 76208., 2.7E-4); @@ -524,7 +526,7 @@ public void testSymmetryCir() { } @Test - public void testNonInertialFrame() throws IllegalArgumentException { + void testNonInertialFrame() throws IllegalArgumentException { Assertions.assertThrows(IllegalArgumentException.class, () -> { Vector3D position = new Vector3D(33051.2, 26184.9, -1.3E-5); Vector3D velocity = new Vector3D(-60376.2, 76208., 2.7E-4); @@ -536,13 +538,13 @@ public void testNonInertialFrame() throws IllegalArgumentException { } @Test - public void testJacobianReference() { + void testJacobianReference() { AbsoluteDate dateTca = new AbsoluteDate(2000, 04, 01, 0, 0, 0.000, TimeScalesFactory.getUTC()); double mu = 3.986004415e+14; CircularOrbit orbCir = new CircularOrbit(7000000.0, 0.01, -0.02, 1.2, 2.1, - 0.7, PositionAngleType.MEAN, - FramesFactory.getEME2000(), dateTca, mu); + 0.7, PositionAngleType.MEAN, + FramesFactory.getEME2000(), dateTca, mu); // the following reference values have been computed using the free software // version 6.2 of the MSLIB fortran library by the following program: @@ -591,12 +593,12 @@ public void testJacobianReference() { Vector3D pRef = new Vector3D(-4106905.105389204807580, 3603162.539798960555345, 4439730.167038885876536); Vector3D vRef = new Vector3D(740.132407342422994, -5308.773280141396754, 5250.338353483879473); double[][] jRef = { - { -1.1535467596325562, 1.0120556393573172, 1.2470306024626943, 181.96913090864561, -1305.2162699469984, 1290.8494448855752 }, - { -5.07367368325471104E-008, -1.27870567070456834E-008, 1.31544531338558113E-007, -3.09332106417043592E-005, -9.60781276304445404E-005, 1.91506964883791605E-004 }, - { -6.59428471712402018E-008, 1.24561703203882533E-007, -1.41907027322388158E-008, 7.63442601186485441E-005, -1.77446722746170009E-004, 5.99464401287846734E-005 }, - { 7.55079920652274275E-008, 4.41606835295069131E-008, 3.40079310688458225E-008, 7.89724635377817962E-005, 4.61868720707717372E-005, 3.55682891687782599E-005 }, - { -9.20788748896973282E-008, -5.38521280004949642E-008, -4.14712660805579618E-008, 7.78626692360739821E-005, 4.55378113077967091E-005, 3.50684505810897702E-005 }, - { 1.85082436324531617E-008, 1.20506219457886855E-007, -8.31277842285972640E-008, 1.27364008345789645E-004, -1.54770720974742483E-004, -1.78589436862677754E-004 } + { -1.1535467596325562, 1.0120556393573172, 1.2470306024626943, 181.96913090864561, -1305.2162699469984, 1290.8494448855752 }, + { -5.07367368325471104E-008, -1.27870567070456834E-008, 1.31544531338558113E-007, -3.09332106417043592E-005, -9.60781276304445404E-005, 1.91506964883791605E-004 }, + { -6.59428471712402018E-008, 1.24561703203882533E-007, -1.41907027322388158E-008, 7.63442601186485441E-005, -1.77446722746170009E-004, 5.99464401287846734E-005 }, + { 7.55079920652274275E-008, 4.41606835295069131E-008, 3.40079310688458225E-008, 7.89724635377817962E-005, 4.61868720707717372E-005, 3.55682891687782599E-005 }, + { -9.20788748896973282E-008, -5.38521280004949642E-008, -4.14712660805579618E-008, 7.78626692360739821E-005, 4.55378113077967091E-005, 3.50684505810897702E-005 }, + { 1.85082436324531617E-008, 1.20506219457886855E-007, -8.31277842285972640E-008, 1.27364008345789645E-004, -1.54770720974742483E-004, -1.78589436862677754E-004 } }; PVCoordinates pv = orbCir.getPVCoordinates(); @@ -617,13 +619,13 @@ public void testJacobianReference() { } @Test - public void testJacobianFinitedifferences() { + void testJacobianFinitedifferences() { AbsoluteDate dateTca = new AbsoluteDate(2000, 04, 01, 0, 0, 0.000, TimeScalesFactory.getUTC()); double mu = 3.986004415e+14; CircularOrbit orbCir = new CircularOrbit(7000000.0, 0.01, -0.02, 1.2, 2.1, - 0.7, PositionAngleType.MEAN, - FramesFactory.getEME2000(), dateTca, mu); + 0.7, PositionAngleType.MEAN, + FramesFactory.getEME2000(), dateTca, mu); for (PositionAngleType type : PositionAngleType.values()) { double hP = 2.0; @@ -642,27 +644,27 @@ public void testJacobianFinitedifferences() { double[][] invJacobian = new double[6][6]; orbCir.getJacobianWrtParameters(type, invJacobian); MatrixUtils.createRealMatrix(jacobian). - multiply(MatrixUtils.createRealMatrix(invJacobian)). - walkInRowOrder(new RealMatrixPreservingVisitor() { - public void start(int rows, int columns, - int startRow, int endRow, int startColumn, int endColumn) { - } + multiply(MatrixUtils.createRealMatrix(invJacobian)). + walkInRowOrder(new RealMatrixPreservingVisitor() { + public void start(int rows, int columns, + int startRow, int endRow, int startColumn, int endColumn) { + } - public void visit(int row, int column, double value) { - Assertions.assertEquals(row == column ? 1.0 : 0.0, value, 4.0e-9); - } + public void visit(int row, int column, double value) { + Assertions.assertEquals(row == column ? 1.0 : 0.0, value, 4.0e-9); + } - public double end() { - return Double.NaN; - } - }); + public double end() { + return Double.NaN; + } + }); } } private double[][] finiteDifferencesJacobian(PositionAngleType type, CircularOrbit orbit, double hP) - { + { double[][] jacobian = new double[6][6]; for (int i = 0; i < 6; ++i) { fillColumn(type, i, orbit, hP, jacobian); @@ -682,79 +684,79 @@ private void fillColumn(PositionAngleType type, int i, CircularOrbit orbit, doub Vector3D dP = Vector3D.ZERO; Vector3D dV = Vector3D.ZERO; switch (i) { - case 0: - h = hP; - dP = new Vector3D(hP, 0, 0); - break; - case 1: - h = hP; - dP = new Vector3D(0, hP, 0); - break; - case 2: - h = hP; - dP = new Vector3D(0, 0, hP); - break; - case 3: - h = hV; - dV = new Vector3D(hV, 0, 0); - break; - case 4: - h = hV; - dV = new Vector3D(0, hV, 0); - break; - default: - h = hV; - dV = new Vector3D(0, 0, hV); - break; + case 0: + h = hP; + dP = new Vector3D(hP, 0, 0); + break; + case 1: + h = hP; + dP = new Vector3D(0, hP, 0); + break; + case 2: + h = hP; + dP = new Vector3D(0, 0, hP); + break; + case 3: + h = hV; + dV = new Vector3D(hV, 0, 0); + break; + case 4: + h = hV; + dV = new Vector3D(0, hV, 0); + break; + default: + h = hV; + dV = new Vector3D(0, 0, hV); + break; } CircularOrbit oM4h = new CircularOrbit(new PVCoordinates(new Vector3D(1, p, -4, dP), new Vector3D(1, v, -4, dV)), - orbit.getFrame(), orbit.getDate(), orbit.getMu()); + orbit.getFrame(), orbit.getDate(), orbit.getMu()); CircularOrbit oM3h = new CircularOrbit(new PVCoordinates(new Vector3D(1, p, -3, dP), new Vector3D(1, v, -3, dV)), - orbit.getFrame(), orbit.getDate(), orbit.getMu()); + orbit.getFrame(), orbit.getDate(), orbit.getMu()); CircularOrbit oM2h = new CircularOrbit(new PVCoordinates(new Vector3D(1, p, -2, dP), new Vector3D(1, v, -2, dV)), - orbit.getFrame(), orbit.getDate(), orbit.getMu()); + orbit.getFrame(), orbit.getDate(), orbit.getMu()); CircularOrbit oM1h = new CircularOrbit(new PVCoordinates(new Vector3D(1, p, -1, dP), new Vector3D(1, v, -1, dV)), - orbit.getFrame(), orbit.getDate(), orbit.getMu()); + orbit.getFrame(), orbit.getDate(), orbit.getMu()); CircularOrbit oP1h = new CircularOrbit(new PVCoordinates(new Vector3D(1, p, +1, dP), new Vector3D(1, v, +1, dV)), - orbit.getFrame(), orbit.getDate(), orbit.getMu()); + orbit.getFrame(), orbit.getDate(), orbit.getMu()); CircularOrbit oP2h = new CircularOrbit(new PVCoordinates(new Vector3D(1, p, +2, dP), new Vector3D(1, v, +2, dV)), - orbit.getFrame(), orbit.getDate(), orbit.getMu()); + orbit.getFrame(), orbit.getDate(), orbit.getMu()); CircularOrbit oP3h = new CircularOrbit(new PVCoordinates(new Vector3D(1, p, +3, dP), new Vector3D(1, v, +3, dV)), - orbit.getFrame(), orbit.getDate(), orbit.getMu()); + orbit.getFrame(), orbit.getDate(), orbit.getMu()); CircularOrbit oP4h = new CircularOrbit(new PVCoordinates(new Vector3D(1, p, +4, dP), new Vector3D(1, v, +4, dV)), - orbit.getFrame(), orbit.getDate(), orbit.getMu()); + orbit.getFrame(), orbit.getDate(), orbit.getMu()); jacobian[0][i] = (-3 * (oP4h.getA() - oM4h.getA()) + - 32 * (oP3h.getA() - oM3h.getA()) - - 168 * (oP2h.getA() - oM2h.getA()) + - 672 * (oP1h.getA() - oM1h.getA())) / (840 * h); + 32 * (oP3h.getA() - oM3h.getA()) - + 168 * (oP2h.getA() - oM2h.getA()) + + 672 * (oP1h.getA() - oM1h.getA())) / (840 * h); jacobian[1][i] = (-3 * (oP4h.getCircularEx() - oM4h.getCircularEx()) + - 32 * (oP3h.getCircularEx() - oM3h.getCircularEx()) - - 168 * (oP2h.getCircularEx() - oM2h.getCircularEx()) + - 672 * (oP1h.getCircularEx() - oM1h.getCircularEx())) / (840 * h); + 32 * (oP3h.getCircularEx() - oM3h.getCircularEx()) - + 168 * (oP2h.getCircularEx() - oM2h.getCircularEx()) + + 672 * (oP1h.getCircularEx() - oM1h.getCircularEx())) / (840 * h); jacobian[2][i] = (-3 * (oP4h.getCircularEy() - oM4h.getCircularEy()) + - 32 * (oP3h.getCircularEy() - oM3h.getCircularEy()) - - 168 * (oP2h.getCircularEy() - oM2h.getCircularEy()) + - 672 * (oP1h.getCircularEy() - oM1h.getCircularEy())) / (840 * h); + 32 * (oP3h.getCircularEy() - oM3h.getCircularEy()) - + 168 * (oP2h.getCircularEy() - oM2h.getCircularEy()) + + 672 * (oP1h.getCircularEy() - oM1h.getCircularEy())) / (840 * h); jacobian[3][i] = (-3 * (oP4h.getI() - oM4h.getI()) + - 32 * (oP3h.getI() - oM3h.getI()) - - 168 * (oP2h.getI() - oM2h.getI()) + - 672 * (oP1h.getI() - oM1h.getI())) / (840 * h); + 32 * (oP3h.getI() - oM3h.getI()) - + 168 * (oP2h.getI() - oM2h.getI()) + + 672 * (oP1h.getI() - oM1h.getI())) / (840 * h); jacobian[4][i] = (-3 * (oP4h.getRightAscensionOfAscendingNode() - oM4h.getRightAscensionOfAscendingNode()) + - 32 * (oP3h.getRightAscensionOfAscendingNode() - oM3h.getRightAscensionOfAscendingNode()) - - 168 * (oP2h.getRightAscensionOfAscendingNode() - oM2h.getRightAscensionOfAscendingNode()) + - 672 * (oP1h.getRightAscensionOfAscendingNode() - oM1h.getRightAscensionOfAscendingNode())) / (840 * h); + 32 * (oP3h.getRightAscensionOfAscendingNode() - oM3h.getRightAscensionOfAscendingNode()) - + 168 * (oP2h.getRightAscensionOfAscendingNode() - oM2h.getRightAscensionOfAscendingNode()) + + 672 * (oP1h.getRightAscensionOfAscendingNode() - oM1h.getRightAscensionOfAscendingNode())) / (840 * h); jacobian[5][i] = (-3 * (oP4h.getAlpha(type) - oM4h.getAlpha(type)) + - 32 * (oP3h.getAlpha(type) - oM3h.getAlpha(type)) - - 168 * (oP2h.getAlpha(type) - oM2h.getAlpha(type)) + - 672 * (oP1h.getAlpha(type) - oM1h.getAlpha(type))) / (840 * h); + 32 * (oP3h.getAlpha(type) - oM3h.getAlpha(type)) - + 168 * (oP2h.getAlpha(type) - oM2h.getAlpha(type)) + + 672 * (oP1h.getAlpha(type) - oM1h.getAlpha(type))) / (840 * h); } @Test - public void testSerialization() - throws IOException, ClassNotFoundException { + void testSerialization() + throws IOException, ClassNotFoundException { Vector3D position = new Vector3D(-29536113.0, 30329259.0, -100125.0); Vector3D velocity = new Vector3D(-2194.0, -2141.0, -8.0); PVCoordinates pvCoordinates = new PVCoordinates( position, velocity); @@ -765,8 +767,7 @@ public void testSerialization() ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(orbit); - Assertions.assertTrue(bos.size() > 350); - Assertions.assertTrue(bos.size() < 400); + Assertions.assertEquals(bos.size(), 527); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); @@ -790,14 +791,14 @@ public void testSerialization() } @Test - public void testSerializationWithDerivatives() - throws IOException, ClassNotFoundException { + void testSerializationWithDerivatives() + throws IOException, ClassNotFoundException { Vector3D position = new Vector3D(-29536113.0, 30329259.0, -100125.0); Vector3D velocity = new Vector3D(-2194.0, -2141.0, -8.0); double r2 = position.getNormSq(); double r = FastMath.sqrt(r2); Vector3D acceleration = new Vector3D(-mu / (r * r2), position, - 1, new Vector3D(-0.1, 0.2, 0.3)); + 1, new Vector3D(-0.1, 0.2, 0.3)); PVCoordinates pvCoordinates = new PVCoordinates( position, velocity, acceleration); CircularOrbit orbit = new CircularOrbit(pvCoordinates, FramesFactory.getEME2000(), date, mu); Assertions.assertEquals(42255170.003, orbit.getA(), 1.0e-3); @@ -806,84 +807,120 @@ public void testSerializationWithDerivatives() ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(orbit); - Assertions.assertTrue(bos.size() > 400); - Assertions.assertTrue(bos.size() < 450); + Assertions.assertEquals(bos.size(), 575); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); CircularOrbit deserialized = (CircularOrbit) ois.readObject(); - Assertions.assertEquals(orbit.getA(), deserialized.getA(), 1.0e-10); - Assertions.assertEquals(orbit.getCircularEx(), deserialized.getCircularEx(), 1.0e-10); - Assertions.assertEquals(orbit.getCircularEy(), deserialized.getCircularEy(), 1.0e-10); - Assertions.assertEquals(orbit.getI(), deserialized.getI(), 1.0e-10); - Assertions.assertEquals(orbit.getRightAscensionOfAscendingNode(), deserialized.getRightAscensionOfAscendingNode(), 1.0e-10); - Assertions.assertEquals(orbit.getAlphaV(), deserialized.getAlphaV(), 1.0e-10); - Assertions.assertEquals(orbit.getADot(), deserialized.getADot(), 1.0e-10); - Assertions.assertEquals(orbit.getCircularExDot(), deserialized.getCircularExDot(), 1.0e-10); - Assertions.assertEquals(orbit.getCircularEyDot(), deserialized.getCircularEyDot(), 1.0e-10); - Assertions.assertEquals(orbit.getIDot(), deserialized.getIDot(), 1.0e-10); - Assertions.assertEquals(orbit.getRightAscensionOfAscendingNodeDot(), deserialized.getRightAscensionOfAscendingNodeDot(), 1.0e-10); - Assertions.assertEquals(orbit.getAlphaVDot(), deserialized.getAlphaVDot(), 1.0e-10); + Assertions.assertEquals(orbit.getA(), deserialized.getA()); + Assertions.assertEquals(orbit.getCircularEx(), deserialized.getCircularEx()); + Assertions.assertEquals(orbit.getCircularEy(), deserialized.getCircularEy()); + Assertions.assertEquals(orbit.getI(), deserialized.getI()); + Assertions.assertEquals(orbit.getRightAscensionOfAscendingNode(), deserialized.getRightAscensionOfAscendingNode()); + Assertions.assertEquals(orbit.getAlphaV(), deserialized.getAlphaV()); + Assertions.assertEquals(orbit.getADot(), deserialized.getADot()); + Assertions.assertEquals(orbit.getCircularExDot(), deserialized.getCircularExDot()); + Assertions.assertEquals(orbit.getCircularEyDot(), deserialized.getCircularEyDot()); + Assertions.assertEquals(orbit.getIDot(), deserialized.getIDot()); + Assertions.assertEquals(orbit.getRightAscensionOfAscendingNodeDot(), deserialized.getRightAscensionOfAscendingNodeDot()); + Assertions.assertEquals(orbit.getAlphaVDot(), deserialized.getAlphaVDot()); Assertions.assertEquals(orbit.getDate(), deserialized.getDate()); - Assertions.assertEquals(orbit.getMu(), deserialized.getMu(), 1.0e-10); + Assertions.assertEquals(orbit.getMu(), deserialized.getMu()); Assertions.assertEquals(orbit.getFrame().getName(), deserialized.getFrame().getName()); } @Test - public void testSerializationNoPVWithDerivatives() - throws IOException, ClassNotFoundException { + void testSerializationNoPVWithDerivatives() + throws IOException, ClassNotFoundException { Vector3D position = new Vector3D(-29536113.0, 30329259.0, -100125.0); Vector3D velocity = new Vector3D(-2194.0, -2141.0, -8.0); double r2 = position.getNormSq(); double r = FastMath.sqrt(r2); Vector3D acceleration = new Vector3D(-mu / (r * r2), position, - 1, new Vector3D(-0.1, 0.2, 0.3)); + 1, new Vector3D(-0.1, 0.2, 0.3)); PVCoordinates pvCoordinates = new PVCoordinates( position, velocity, acceleration); CircularOrbit original = new CircularOrbit(pvCoordinates, FramesFactory.getEME2000(), date, mu); // rebuild the same orbit, preserving derivatives but removing Cartesian coordinates // (to check one specific path in serialization.deserialization) CircularOrbit orbit = new CircularOrbit(original.getA(), original.getCircularEx(), original.getCircularEy(), - original.getI(), original.getRightAscensionOfAscendingNode(), - original.getAlphaV(), - original.getADot(), original.getCircularExDot(), original.getCircularEyDot(), - original.getIDot(), original.getRightAscensionOfAscendingNodeDot(), - original.getAlphaVDot(), - PositionAngleType.TRUE, original.getFrame(), - original.getDate(), original.getMu()); + original.getI(), original.getRightAscensionOfAscendingNode(), + original.getAlphaV(), + original.getADot(), original.getCircularExDot(), original.getCircularEyDot(), + original.getIDot(), original.getRightAscensionOfAscendingNodeDot(), + original.getAlphaVDot(), + PositionAngleType.TRUE, original.getFrame(), + original.getDate(), original.getMu()); Assertions.assertEquals(42255170.003, orbit.getA(), 1.0e-3); ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(orbit); - Assertions.assertTrue(bos.size() > 330); - Assertions.assertTrue(bos.size() < 380); + Assertions.assertEquals(bos.size(), 503); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); CircularOrbit deserialized = (CircularOrbit) ois.readObject(); - Assertions.assertEquals(orbit.getA(), deserialized.getA(), 1.0e-10); - Assertions.assertEquals(orbit.getCircularEx(), deserialized.getCircularEx(), 1.0e-10); - Assertions.assertEquals(orbit.getCircularEy(), deserialized.getCircularEy(), 1.0e-10); - Assertions.assertEquals(orbit.getI(), deserialized.getI(), 1.0e-10); - Assertions.assertEquals(orbit.getRightAscensionOfAscendingNode(), deserialized.getRightAscensionOfAscendingNode(), 1.0e-10); - Assertions.assertEquals(orbit.getAlphaV(), deserialized.getAlphaV(), 1.0e-10); - Assertions.assertEquals(orbit.getADot(), deserialized.getADot(), 1.0e-10); - Assertions.assertEquals(orbit.getCircularExDot(), deserialized.getCircularExDot(), 1.0e-10); - Assertions.assertEquals(orbit.getCircularEyDot(), deserialized.getCircularEyDot(), 1.0e-10); - Assertions.assertEquals(orbit.getIDot(), deserialized.getIDot(), 1.0e-10); - Assertions.assertEquals(orbit.getRightAscensionOfAscendingNodeDot(), deserialized.getRightAscensionOfAscendingNodeDot(), 1.0e-10); - Assertions.assertEquals(orbit.getAlphaVDot(), deserialized.getAlphaVDot(), 1.0e-10); + compareOrbits(orbit, deserialized); + + } + + @Test + void testSerializationNoPVWithoutDerivatives() + throws IOException, ClassNotFoundException { + Vector3D position = new Vector3D(-29536113.0, 30329259.0, -100125.0); + Vector3D velocity = new Vector3D(-2194.0, -2141.0, -8.0); + double r2 = position.getNormSq(); + double r = FastMath.sqrt(r2); + Vector3D acceleration = new Vector3D(-mu / (r * r2), position, + 1, new Vector3D(-0.1, 0.2, 0.3)); + PVCoordinates pvCoordinates = new PVCoordinates( position, velocity, acceleration); + CircularOrbit original = new CircularOrbit(pvCoordinates, FramesFactory.getEME2000(), date, mu); + + // rebuild the same orbit, preserving derivatives but removing Cartesian coordinates + // (to check one specific path in serialization.deserialization) + CircularOrbit orbit = new CircularOrbit(original.getA(), original.getCircularEx(), original.getCircularEy(), + original.getI(), original.getRightAscensionOfAscendingNode(), + original.getAlphaV(), + PositionAngleType.TRUE, original.getFrame(), + original.getDate(), original.getMu()); + Assertions.assertEquals(42255170.003, orbit.getA(), 1.0e-3); + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(bos); + oos.writeObject(orbit); + + Assertions.assertEquals(bos.size(), 455); + + ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); + ObjectInputStream ois = new ObjectInputStream(bis); + CircularOrbit deserialized = (CircularOrbit) ois.readObject(); + compareOrbits(orbit, deserialized); + + } + + private void compareOrbits(final CircularOrbit orbit, final CircularOrbit deserialized) { + Assertions.assertEquals(orbit.getA(), deserialized.getA()); + Assertions.assertEquals(orbit.getCircularEx(), deserialized.getCircularEx()); + Assertions.assertEquals(orbit.getCircularEy(), deserialized.getCircularEy()); + Assertions.assertEquals(orbit.getI(), deserialized.getI()); + Assertions.assertEquals(orbit.getRightAscensionOfAscendingNode(), deserialized.getRightAscensionOfAscendingNode()); + Assertions.assertEquals(orbit.getAlphaV(), deserialized.getAlphaV()); + Assertions.assertEquals(orbit.getADot(), deserialized.getADot()); + Assertions.assertEquals(orbit.getCircularExDot(), deserialized.getCircularExDot()); + Assertions.assertEquals(orbit.getCircularEyDot(), deserialized.getCircularEyDot()); + Assertions.assertEquals(orbit.getIDot(), deserialized.getIDot()); + Assertions.assertEquals(orbit.getRightAscensionOfAscendingNodeDot(), deserialized.getRightAscensionOfAscendingNodeDot()); + Assertions.assertEquals(orbit.getAlphaVDot(), deserialized.getAlphaVDot()); Assertions.assertEquals(orbit.getDate(), deserialized.getDate()); - Assertions.assertEquals(orbit.getMu(), deserialized.getMu(), 1.0e-10); + Assertions.assertEquals(orbit.getMu(), deserialized.getMu()); Assertions.assertEquals(orbit.getFrame().getName(), deserialized.getFrame().getName()); - } @Test - public void testNonKeplerianDerivatives() { + void testNonKeplerianDerivatives() { final AbsoluteDate date = new AbsoluteDate("2003-05-01T00:00:20.000", TimeScalesFactory.getUTC()); final Vector3D position = new Vector3D(6896874.444705, 1956581.072644, -147476.245054); final Vector3D velocity = new Vector3D(166.816407662, -1106.783301861, -7372.745712770); @@ -894,59 +931,59 @@ public void testNonKeplerianDerivatives() { final CircularOrbit orbit = new CircularOrbit(pv, frame, mu); Assertions.assertEquals(differentiate(pv, frame, mu, shifted -> shifted.getA()), - orbit.getADot(), - 4.3e-8); + orbit.getADot(), + 4.3e-8); Assertions.assertEquals(differentiate(pv, frame, mu, shifted -> shifted.getEquinoctialEx()), - orbit.getEquinoctialExDot(), - 2.1e-15); + orbit.getEquinoctialExDot(), + 2.1e-15); Assertions.assertEquals(differentiate(pv, frame, mu, shifted -> shifted.getEquinoctialEy()), - orbit.getEquinoctialEyDot(), - 5.4e-16); + orbit.getEquinoctialEyDot(), + 5.4e-16); Assertions.assertEquals(differentiate(pv, frame, mu, shifted -> shifted.getHx()), - orbit.getHxDot(), - 1.6e-15); + orbit.getHxDot(), + 1.6e-15); Assertions.assertEquals(differentiate(pv, frame, mu, shifted -> shifted.getHy()), - orbit.getHyDot(), - 7.3e-17); + orbit.getHyDot(), + 7.3e-17); Assertions.assertEquals(differentiate(pv, frame, mu, shifted -> shifted.getLv()), - orbit.getLvDot(), - 3.4e-16); + orbit.getLvDot(), + 3.4e-16); Assertions.assertEquals(differentiate(pv, frame, mu, shifted -> shifted.getLE()), - orbit.getLEDot(), - 3.5e-15); + orbit.getLEDot(), + 3.5e-15); Assertions.assertEquals(differentiate(pv, frame, mu, shifted -> shifted.getLM()), - orbit.getLMDot(), - 5.3e-15); + orbit.getLMDot(), + 5.3e-15); Assertions.assertEquals(differentiate(pv, frame, mu, shifted -> shifted.getE()), - orbit.getEDot(), - 6.8e-16); + orbit.getEDot(), + 6.8e-16); Assertions.assertEquals(differentiate(pv, frame, mu, shifted -> shifted.getI()), - orbit.getIDot(), - 5.7e-16); + orbit.getIDot(), + 5.7e-16); Assertions.assertEquals(differentiate(pv, frame, mu, shifted -> shifted.getCircularEx()), - orbit.getCircularExDot(), - 2.2e-15); + orbit.getCircularExDot(), + 2.2e-15); Assertions.assertEquals(differentiate(pv, frame, mu, shifted -> shifted.getCircularEy()), - orbit.getCircularEyDot(), - 5.3e-17); + orbit.getCircularEyDot(), + 5.3e-17); Assertions.assertEquals(differentiate(pv, frame, mu, shifted -> shifted.getAlphaV()), - orbit.getAlphaVDot(), - 4.3e-15); + orbit.getAlphaVDot(), + 4.3e-15); Assertions.assertEquals(differentiate(pv, frame, mu, shifted -> shifted.getAlphaE()), - orbit.getAlphaEDot(), - 1.2e-15); + orbit.getAlphaEDot(), + 1.2e-15); Assertions.assertEquals(differentiate(pv, frame, mu, shifted -> shifted.getAlphaM()), - orbit.getAlphaMDot(), - 3.7e-15); + orbit.getAlphaMDot(), + 3.7e-15); Assertions.assertEquals(differentiate(pv, frame, mu, shifted -> shifted.getAlpha(PositionAngleType.TRUE)), - orbit.getAlphaDot(PositionAngleType.TRUE), - 4.3e-15); + orbit.getAlphaDot(PositionAngleType.TRUE), + 4.3e-15); Assertions.assertEquals(differentiate(pv, frame, mu, shifted -> shifted.getAlpha(PositionAngleType.ECCENTRIC)), - orbit.getAlphaDot(PositionAngleType.ECCENTRIC), - 1.2e-15); + orbit.getAlphaDot(PositionAngleType.ECCENTRIC), + 1.2e-15); Assertions.assertEquals(differentiate(pv, frame, mu, shifted -> shifted.getAlpha(PositionAngleType.MEAN)), - orbit.getAlphaDot(PositionAngleType.MEAN), - 3.7e-15); + orbit.getAlphaDot(PositionAngleType.MEAN), + 3.7e-15); } @@ -960,10 +997,10 @@ public double value(double dt) { } }); return diff.value(factory.variable(0, 0.0)).getPartialDerivative(1); - } + } @Test - public void testPositionAngleDerivatives() { + void testPositionAngleDerivatives() { final AbsoluteDate date = new AbsoluteDate("2003-05-01T00:00:20.000", TimeScalesFactory.getUTC()); final Vector3D position = new Vector3D(6896874.444705, 1956581.072644, -147476.245054); final Vector3D velocity = new Vector3D(166.816407662, -1106.783301861, -7372.745712770); @@ -975,18 +1012,18 @@ public void testPositionAngleDerivatives() { for (PositionAngleType type : PositionAngleType.values()) { final CircularOrbit rebuilt = new CircularOrbit(orbit.getA(), - orbit.getCircularEx(), - orbit.getCircularEy(), - orbit.getI(), - orbit.getRightAscensionOfAscendingNode(), - orbit.getAlpha(type), - orbit.getADot(), - orbit.getCircularExDot(), - orbit.getCircularEyDot(), - orbit.getIDot(), - orbit.getRightAscensionOfAscendingNodeDot(), - orbit.getAlphaDot(type), - type, orbit.getFrame(), orbit.getDate(), orbit.getMu()); + orbit.getCircularEx(), + orbit.getCircularEy(), + orbit.getI(), + orbit.getRightAscensionOfAscendingNode(), + orbit.getAlpha(type), + orbit.getADot(), + orbit.getCircularExDot(), + orbit.getCircularEyDot(), + orbit.getIDot(), + orbit.getRightAscensionOfAscendingNodeDot(), + orbit.getAlphaDot(type), + type, orbit.getFrame(), orbit.getDate(), orbit.getMu()); MatcherAssert.assertThat(rebuilt.getA(), relativelyCloseTo(orbit.getA(), 1)); MatcherAssert.assertThat(rebuilt.getCircularEx(), relativelyCloseTo(orbit.getCircularEx(), 1)); MatcherAssert.assertThat(rebuilt.getCircularEy(), relativelyCloseTo(orbit.getCircularEy(), 1)); @@ -1008,13 +1045,13 @@ public void testPositionAngleDerivatives() { } @Test - public void testEquatorialRetrograde() { + void testEquatorialRetrograde() { Vector3D position = new Vector3D(10000000.0, 0.0, 0.0); Vector3D velocity = new Vector3D(0.0, -6500.0, 1.0e-10); double r2 = position.getNormSq(); double r = FastMath.sqrt(r2); Vector3D acceleration = new Vector3D(-mu / (r * r2), position, - 1, new Vector3D(-0.1, 0.2, 0.3)); + 1, new Vector3D(-0.1, 0.2, 0.3)); PVCoordinates pvCoordinates = new PVCoordinates(position, velocity, acceleration); CircularOrbit orbit = new CircularOrbit(pvCoordinates, FramesFactory.getEME2000(), date, mu); Assertions.assertEquals(10637829.465, orbit.getA(), 1.0e-3); @@ -1030,14 +1067,14 @@ public void testEquatorialRetrograde() { } @Test - public void testDerivativesConversionSymmetry() { + void testDerivativesConversionSymmetry() { final AbsoluteDate date = new AbsoluteDate("2003-05-01T00:01:20.000", TimeScalesFactory.getUTC()); Vector3D position = new Vector3D(6893443.400234382, 1886406.1073757345, -589265.1150359757); Vector3D velocity = new Vector3D(-281.1261461082365, -1231.6165642450928, -7348.756363469432); Vector3D acceleration = new Vector3D(-7.460341170581685, -2.0415957334584527, 0.6393322823627762); PVCoordinates pvCoordinates = new PVCoordinates( position, velocity, acceleration); CircularOrbit orbit = new CircularOrbit(pvCoordinates, FramesFactory.getEME2000(), - date, Constants.EIGEN5C_EARTH_MU); + date, Constants.EIGEN5C_EARTH_MU); Assertions.assertTrue(orbit.hasDerivatives()); double r2 = position.getNormSq(); double r = FastMath.sqrt(r2); @@ -1060,13 +1097,13 @@ public void testDerivativesConversionSymmetry() { } @Test - public void testToString() { + void testToString() { Vector3D position = new Vector3D(-29536113.0, 30329259.0, -100125.0); Vector3D velocity = new Vector3D(-2194.0, -2141.0, -8.0); PVCoordinates pvCoordinates = new PVCoordinates(position, velocity); CircularOrbit orbit = new CircularOrbit(pvCoordinates, FramesFactory.getEME2000(), date, mu); Assertions.assertEquals("circular parameters: {a: 4.225517000282565E7, ex: 0.002082917137146049, ey: 5.173980074371024E-4, i: 0.20189257051515358, raan: -87.91788415673473, alphaV: -137.84099636616548;}", - orbit.toString()); + orbit.toString()); } @Test @@ -1094,7 +1131,7 @@ void testRemoveRates() { } @Test - public void testCopyNonKeplerianAcceleration() { + void testCopyNonKeplerianAcceleration() { final Frame eme2000 = FramesFactory.getEME2000(); @@ -1102,7 +1139,7 @@ public void testCopyNonKeplerianAcceleration() { final Vector3D position = new Vector3D(42164140, 0, 0); // Build PVCoodrinates starting from its position and computing the corresponding circular velocity final PVCoordinates pv = new PVCoordinates(position, - new Vector3D(0, FastMath.sqrt(mu / position.getNorm()), 0)); + new Vector3D(0, FastMath.sqrt(mu / position.getNorm()), 0)); // Build a KeplerianOrbit in eme2000 final Orbit orbit = new CircularOrbit(pv, eme2000, date, mu); @@ -1114,26 +1151,26 @@ public void testCopyNonKeplerianAcceleration() { final Orbit shiftedOrbitCopy = orbitCopy.shiftedBy(10); // This does not work Assertions.assertEquals(0.0, - Vector3D.distance(shiftedOrbit.getPosition(), - shiftedOrbitCopy.getPosition()), - 1.0e-10); + Vector3D.distance(shiftedOrbit.getPosition(), + shiftedOrbitCopy.getPosition()), + 1.0e-10); Assertions.assertEquals(0.0, - Vector3D.distance(shiftedOrbit.getPVCoordinates().getVelocity(), - shiftedOrbitCopy.getPVCoordinates().getVelocity()), - 1.0e-10); + Vector3D.distance(shiftedOrbit.getPVCoordinates().getVelocity(), + shiftedOrbitCopy.getPVCoordinates().getVelocity()), + 1.0e-10); } @Test - public void testNormalize() { + void testNormalize() { CircularOrbit withoutDerivatives = - new CircularOrbit(42166712.0, 0.005, -0.025, 1.6, - 1.25, 0.4, PositionAngleType.MEAN, - FramesFactory.getEME2000(), date, mu); + new CircularOrbit(42166712.0, 0.005, -0.025, 1.6, + 1.25, 0.4, PositionAngleType.MEAN, + FramesFactory.getEME2000(), date, mu); CircularOrbit ref = - new CircularOrbit(24000000.0, -0.012, 0.01, 0.2, - -6.28, 6.28, PositionAngleType.MEAN, - FramesFactory.getEME2000(), date, mu); + new CircularOrbit(24000000.0, -0.012, 0.01, 0.2, + -6.28, 6.28, PositionAngleType.MEAN, + FramesFactory.getEME2000(), date, mu); CircularOrbit normalized1 = (CircularOrbit) OrbitType.CIRCULAR.normalize(withoutDerivatives, ref); Assertions.assertFalse(normalized1.hasDerivatives()); @@ -1153,11 +1190,11 @@ public void testNormalize() { double[] p = new double[6]; OrbitType.CIRCULAR.mapOrbitToArray(withoutDerivatives, PositionAngleType.TRUE, p, null); CircularOrbit withDerivatives = (CircularOrbit) OrbitType.CIRCULAR.mapArrayToOrbit(p, - new double[] { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 }, - PositionAngleType.TRUE, - withoutDerivatives.getDate(), - withoutDerivatives.getMu(), - withoutDerivatives.getFrame()); + new double[] { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0 }, + PositionAngleType.TRUE, + withoutDerivatives.getDate(), + withoutDerivatives.getMu(), + withoutDerivatives.getFrame()); CircularOrbit normalized2 = (CircularOrbit) OrbitType.CIRCULAR.normalize(withDerivatives, ref); Assertions.assertTrue(normalized2.hasDerivatives()); Assertions.assertEquals(0.0, normalized2.getA() - withDerivatives.getA(), 1.0e-6); @@ -1175,6 +1212,75 @@ public void testNormalize() { } + @Test + @Deprecated + void positionAngleNonRegressionOnDeprecated() { + // Can be removed when deprecated routines are removed in next major release (13.0) + // GIVEN + final double ex = 0.2; + final double ey = 0.3; + final double originalPositionAngle = 1.; + // WHEN + final double actualEccentricToMean = CircularOrbit.eccentricToMean(originalPositionAngle, ex, ey); + final double actualEccentricToTrue = CircularOrbit.eccentricToTrue(originalPositionAngle, ex, ey); + final double actualMeanToEccentric = CircularOrbit.meanToEccentric(originalPositionAngle, ex, ey); + final double actualTrueToEccentric = CircularOrbit.trueToEccentric(originalPositionAngle, ex, ey); + // THEN + Assertions.assertEquals(CircularLatitudeArgumentUtility.eccentricToMean(ex, ey, originalPositionAngle), + actualEccentricToMean); + Assertions.assertEquals(CircularLatitudeArgumentUtility.eccentricToTrue(ex, ey, originalPositionAngle), + actualEccentricToTrue); + Assertions.assertEquals(CircularLatitudeArgumentUtility.meanToEccentric(ex, ey, originalPositionAngle), + actualMeanToEccentric); + Assertions.assertEquals(CircularLatitudeArgumentUtility.trueToEccentric(ex, ey, originalPositionAngle), + actualTrueToEccentric); + } + + @Test + void testCoverageCachedPositionAngleTypeWithRates() { + // GIVEN + final double semiMajorAxis = 1e4; + final double eccentricity = 0.; + final double expectedAlpha = 0.; + final double expectedAlphaDot = 0.; + // WHEN & THEN + for (final PositionAngleType inputPositionAngleType: PositionAngleType.values()) { + for (final PositionAngleType cachedPositionAngleType: PositionAngleType.values()) { + final CircularOrbit circularOrbit = new CircularOrbit(semiMajorAxis, eccentricity, 0., 0., 0., + expectedAlpha, 0., 0., 0., 0., 0., expectedAlphaDot, + inputPositionAngleType, cachedPositionAngleType, FramesFactory.getGCRF(), date, mu); + Assertions.assertEquals(expectedAlpha, circularOrbit.getAlphaV()); + Assertions.assertEquals(expectedAlpha, circularOrbit.getAlphaM()); + Assertions.assertEquals(expectedAlpha, circularOrbit.getAlphaE()); + Assertions.assertEquals(expectedAlphaDot, circularOrbit.getAlphaVDot()); + Assertions.assertEquals(expectedAlphaDot, circularOrbit.getAlphaMDot()); + Assertions.assertEquals(expectedAlphaDot, circularOrbit.getAlphaEDot()); + } + } + } + + @Test + void testCoverageCachedPositionAngleTypeWithoutRates() { + // GIVEN + final double semiMajorAxis = 1e5; + final double eccentricity = 0.; + final double expectedAlpha = 0.; + // WHEN & THEN + for (final PositionAngleType inputPositionAngleType: PositionAngleType.values()) { + for (final PositionAngleType cachedPositionAngleType: PositionAngleType.values()) { + final CircularOrbit circularOrbit = new CircularOrbit(semiMajorAxis, eccentricity, 0., 0., 0., + expectedAlpha, inputPositionAngleType, cachedPositionAngleType, + FramesFactory.getGCRF(), date, mu); + Assertions.assertEquals(expectedAlpha, circularOrbit.getAlphaV()); + Assertions.assertEquals(expectedAlpha, circularOrbit.getAlphaM()); + Assertions.assertEquals(expectedAlpha, circularOrbit.getAlphaE()); + Assertions.assertEquals(Double.NaN, circularOrbit.getAlphaVDot()); + Assertions.assertEquals(Double.NaN, circularOrbit.getAlphaMDot()); + Assertions.assertEquals(Double.NaN, circularOrbit.getAlphaEDot()); + } + } + } + @BeforeEach public void setUp() { diff --git a/src/test/java/org/orekit/orbits/EquinoctialLongitudeArgumentUtilityTest.java b/src/test/java/org/orekit/orbits/EquinoctialLongitudeArgumentUtilityTest.java new file mode 100644 index 0000000000..9bfb5a9e1f --- /dev/null +++ b/src/test/java/org/orekit/orbits/EquinoctialLongitudeArgumentUtilityTest.java @@ -0,0 +1,78 @@ +/* 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.orbits; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; + +class EquinoctialLongitudeArgumentUtilityTest { + + private static final double EX = 0.1; + private static final double EY = 0.66; + private static final double TOLERANCE = 1e-10; + + @Test + void testMeanToTrueAndBack() { + // GIVEN + final double expectedLatitudeArgument = 3.; + // WHEN + final double intermediateLatitudeArgument = EquinoctialLongitudeArgumentUtility.meanToTrue(EX, EY, + expectedLatitudeArgument); + final double actualLatitudeArgument = EquinoctialLongitudeArgumentUtility.trueToMean(EX, EY, + intermediateLatitudeArgument); + // THEN + Assertions.assertEquals(expectedLatitudeArgument, actualLatitudeArgument, TOLERANCE); + } + + @Test + void testEccentricToTrueAndBack() { + // GIVEN + final double expectedLatitudeArgument = 3.; + // WHEN + final double intermediateLatitudeArgument = EquinoctialLongitudeArgumentUtility.eccentricToTrue(EX, EY, + expectedLatitudeArgument); + final double actualLatitudeArgument = EquinoctialLongitudeArgumentUtility.trueToEccentric(EX, EY, + intermediateLatitudeArgument); + // THEN + Assertions.assertEquals(expectedLatitudeArgument, actualLatitudeArgument, TOLERANCE); + } + + @Test + void testEccentricToMeanAndBack() { + // GIVEN + final double expectedLatitudeArgument = 3.; + // WHEN + final double intermediateLatitudeArgument = EquinoctialLongitudeArgumentUtility.eccentricToMean(EX, EY, + expectedLatitudeArgument); + final double actualLatitudeArgument = EquinoctialLongitudeArgumentUtility.meanToEccentric(EX, EY, + intermediateLatitudeArgument); + // THEN + Assertions.assertEquals(expectedLatitudeArgument, actualLatitudeArgument, TOLERANCE); + } + + @Test + void testMeanToEccentricException() { + // GIVEN + final double nanLatitudeArgument = Double.NaN; + // WHEN & THEN + Assertions.assertThrows(OrekitException.class, () -> EquinoctialLongitudeArgumentUtility.meanToEccentric(EX, EY, + nanLatitudeArgument), OrekitMessages.UNABLE_TO_COMPUTE_ECCENTRIC_LONGITUDE_ARGUMENT.toString()); + } + +} \ No newline at end of file diff --git a/src/test/java/org/orekit/orbits/EquinoctialOrbitTest.java b/src/test/java/org/orekit/orbits/EquinoctialOrbitTest.java index ab200732f1..11fd6e043b 100644 --- a/src/test/java/org/orekit/orbits/EquinoctialOrbitTest.java +++ b/src/test/java/org/orekit/orbits/EquinoctialOrbitTest.java @@ -31,6 +31,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.orekit.Utils; +import org.orekit.errors.OrekitIllegalArgumentException; +import org.orekit.errors.OrekitMessages; import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; import org.orekit.frames.Transform; @@ -59,7 +61,7 @@ public class EquinoctialOrbitTest { private double mu; @Test - public void testEquinoctialToEquinoctialEll() { + void testEquinoctialToEquinoctialEll() { double ix = 1.200e-04; double iy = -1.16e-04; @@ -93,7 +95,7 @@ public void testEquinoctialToEquinoctialEll() { } @Test - public void testEquinoctialToEquinoctialCirc() { + void testEquinoctialToEquinoctialCirc() { double ix = 1.200e-04; double iy = -1.16e-04; @@ -129,7 +131,7 @@ public void testEquinoctialToEquinoctialCirc() { } @Test - public void testEquinoctialToCartesian() { + void testEquinoctialToCartesian() { double ix = 1.200e-04; double iy = -1.16e-04; @@ -159,7 +161,7 @@ public void testEquinoctialToCartesian() { } @Test - public void testEquinoctialToKeplerian() { + void testEquinoctialToKeplerian() { double ix = 1.20e-4; double iy = -1.16e-4; @@ -191,15 +193,31 @@ public void testEquinoctialToKeplerian() { } @Test - public void testHyperbolic() { - Assertions.assertThrows(IllegalArgumentException.class, () -> { - new EquinoctialOrbit(42166712.0, 0.9, 0.5, 0.01, -0.02, 5.300, - PositionAngleType.MEAN, FramesFactory.getEME2000(), date, mu); - }); + void testHyperbolic1() { + try { + new EquinoctialOrbit(-42166712.0, 1.9, 0.5, 0.01, -0.02, 5.300, + PositionAngleType.MEAN, FramesFactory.getEME2000(), date, mu); + Assertions.fail("an exception should have been thrown"); + } catch (OrekitIllegalArgumentException oe) { + Assertions.assertEquals(OrekitMessages.HYPERBOLIC_ORBIT_NOT_HANDLED_AS, oe.getSpecifier()); + } + } + + + @Test + void testHyperbolic2() { + Orbit orbit = new KeplerianOrbit(-42166712.0, 1.9, 0.5, 0.01, -0.02, 5.300, + PositionAngleType.MEAN, FramesFactory.getEME2000(), date, mu); + try { + new EquinoctialOrbit(orbit.getPVCoordinates(), orbit.getFrame(), orbit.getMu()); + Assertions.fail("an exception should have been thrown"); + } catch (OrekitIllegalArgumentException oe) { + Assertions.assertEquals(OrekitMessages.HYPERBOLIC_ORBIT_NOT_HANDLED_AS, oe.getSpecifier()); + } } @Test - public void testNumericalIssue25() { + void testNumericalIssue25() { Vector3D position = new Vector3D(3782116.14107698, 416663.11924914, 5875541.62103057); Vector3D velocity = new Vector3D(-6349.7848910501, 288.4061811651, 4066.9366759691); EquinoctialOrbit orbit = new EquinoctialOrbit(new PVCoordinates(position, velocity), @@ -212,7 +230,7 @@ public void testNumericalIssue25() { @Test - public void testAnomaly() { + void testAnomaly() { // elliptic orbit Vector3D position = new Vector3D(7.0e6, 1.0e6, 4.0e6); @@ -294,7 +312,7 @@ public void testAnomaly() { } @Test - public void testPositionVelocityNorms() { + void testPositionVelocityNorms() { // elliptic and non equatorial (i retrograde) orbit EquinoctialOrbit p = @@ -343,7 +361,7 @@ public void testPositionVelocityNorms() { } @Test - public void testGeometry() { + void testGeometry() { // elliptic and non equatorial (i retrograde) orbit EquinoctialOrbit p = @@ -427,7 +445,7 @@ public void testGeometry() { @Test - public void testRadiusOfCurvature() { + void testRadiusOfCurvature() { // elliptic and non equatorial (i retrograde) orbit EquinoctialOrbit p = @@ -465,7 +483,7 @@ public void testRadiusOfCurvature() { } @Test - public void testSymmetry() { + void testSymmetry() { // elliptic and non equatorial orbit Vector3D position = new Vector3D(4512.9, 18260., -5127.); @@ -495,7 +513,7 @@ public void testSymmetry() { } @Test - public void testNonInertialFrame() throws IllegalArgumentException { + void testNonInertialFrame() throws IllegalArgumentException { Assertions.assertThrows(IllegalArgumentException.class, () -> { Vector3D position = new Vector3D(4512.9, 18260., -5127.); Vector3D velocity = new Vector3D(134664.6, 90066.8, 72047.6); @@ -507,9 +525,9 @@ public void testNonInertialFrame() throws IllegalArgumentException { } @Test - public void testJacobianReference() { + void testJacobianReference() { - AbsoluteDate dateTca = new AbsoluteDate(2000, 04, 01, 0, 0, 0.000, TimeScalesFactory.getUTC()); + AbsoluteDate dateTca = new AbsoluteDate(2000, 4, 1, 0, 0, 0.000, TimeScalesFactory.getUTC()); double mu = 3.986004415e+14; EquinoctialOrbit orbEqu = new EquinoctialOrbit(7000000.0, 0.01, -0.02, 1.2, 2.1, FastMath.toRadians(40.), PositionAngleType.MEAN, @@ -613,9 +631,9 @@ public void testJacobianReference() { } @Test - public void testJacobianFinitedifferences() { + void testJacobianFinitedifferences() { - AbsoluteDate dateTca = new AbsoluteDate(2000, 04, 01, 0, 0, 0.000, TimeScalesFactory.getUTC()); + AbsoluteDate dateTca = new AbsoluteDate(2000, 4, 1, 0, 0, 0.000, TimeScalesFactory.getUTC()); double mu = 3.986004415e+14; EquinoctialOrbit orbEqu = new EquinoctialOrbit(7000000.0, 0.01, -0.02, 1.2, 2.1, FastMath.toRadians(40.), PositionAngleType.MEAN, @@ -749,7 +767,7 @@ private void fillColumn(PositionAngleType type, int i, EquinoctialOrbit orbit, d } @Test - public void testSerialization() + void testSerialization() throws IOException, ClassNotFoundException { Vector3D position = new Vector3D(-29536113.0, 30329259.0, -100125.0); Vector3D velocity = new Vector3D(-2194.0, -2141.0, -8.0); @@ -761,8 +779,7 @@ public void testSerialization() ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(orbit); - Assertions.assertTrue(bos.size() > 280); - Assertions.assertTrue(bos.size() < 330); + Assertions.assertEquals(bos.size(), 458); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); @@ -786,7 +803,7 @@ public void testSerialization() } @Test - public void testSerializationWithDerivatives() + void testSerializationWithDerivatives() throws IOException, ClassNotFoundException { Vector3D position = new Vector3D(-29536113.0, 30329259.0, -100125.0); Vector3D velocity = new Vector3D(-2194.0, -2141.0, -8.0); @@ -802,8 +819,7 @@ public void testSerializationWithDerivatives() ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(orbit); - Assertions.assertTrue(bos.size() > 330); - Assertions.assertTrue(bos.size() < 380); + Assertions.assertEquals(bos.size(), 506); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); @@ -827,7 +843,7 @@ public void testSerializationWithDerivatives() } @Test - public void testNonKeplerianDerivatives() { + void testNonKeplerianDerivatives() { final AbsoluteDate date = new AbsoluteDate("2003-05-01T00:00:20.000", TimeScalesFactory.getUTC()); final Vector3D position = new Vector3D(6896874.444705, 1956581.072644, -147476.245054); final Vector3D velocity = new Vector3D(166.816407662, -1106.783301861, -7372.745712770); @@ -883,16 +899,13 @@ public void testNonKeplerianDerivatives() { double differentiate(TimeStampedPVCoordinates pv, Frame frame, double mu, S picker) { final DSFactory factory = new DSFactory(1, 1); FiniteDifferencesDifferentiator differentiator = new FiniteDifferencesDifferentiator(8, 0.1); - UnivariateDifferentiableFunction diff = differentiator.differentiate(new UnivariateFunction() { - public double value(double dt) { - return picker.apply(new EquinoctialOrbit(pv.shiftedBy(dt), frame, mu)); - } - }); + UnivariateDifferentiableFunction diff = differentiator.differentiate((UnivariateFunction) dt -> + picker.apply(new EquinoctialOrbit(pv.shiftedBy(dt), frame, mu))); return diff.value(factory.variable(0, 0.0)).getPartialDerivative(1); } @Test - public void testPositionAngleDerivatives() { + void testPositionAngleDerivatives() { final AbsoluteDate date = new AbsoluteDate("2003-05-01T00:00:20.000", TimeScalesFactory.getUTC()); final Vector3D position = new Vector3D(6896874.444705, 1956581.072644, -147476.245054); final Vector3D velocity = new Vector3D(166.816407662, -1106.783301861, -7372.745712770); @@ -935,7 +948,7 @@ public void testPositionAngleDerivatives() { } @Test - public void testEquatorialRetrograde() { + void testEquatorialRetrograde() { Vector3D position = new Vector3D(10000000.0, 0.0, 0.0); Vector3D velocity = new Vector3D(0.0, -6500.0, 0.0); double r2 = position.getNormSq(); @@ -960,7 +973,7 @@ public void testEquatorialRetrograde() { } @Test - public void testDerivativesConversionSymmetry() { + void testDerivativesConversionSymmetry() { final AbsoluteDate date = new AbsoluteDate("2003-05-01T00:01:20.000", TimeScalesFactory.getUTC()); Vector3D position = new Vector3D(6893443.400234382, 1886406.1073757345, -589265.1150359757); Vector3D velocity = new Vector3D(-281.1261461082365, -1231.6165642450928, -7348.756363469432); @@ -990,7 +1003,7 @@ public void testDerivativesConversionSymmetry() { } @Test - public void testToString() { + void testToString() { Vector3D position = new Vector3D(-29536113.0, 30329259.0, -100125.0); Vector3D velocity = new Vector3D(-2194.0, -2141.0, -8.0); PVCoordinates pvCoordinates = new PVCoordinates(position, velocity); @@ -1023,7 +1036,7 @@ void testRemoveRates() { } @Test - public void testCopyNonKeplerianAcceleration() { + void testCopyNonKeplerianAcceleration() { final Frame eme2000 = FramesFactory.getEME2000(); @@ -1054,7 +1067,7 @@ public void testCopyNonKeplerianAcceleration() { } @Test - public void testNormalize() { + void testNormalize() { EquinoctialOrbit withoutDerivatives = new EquinoctialOrbit(42166712.0, 0.005, -0.025, 0.17, 0.34, 0.4, PositionAngleType.MEAN, @@ -1105,6 +1118,153 @@ public void testNormalize() { } + @Test + @Deprecated + void positionAngleNonRegressionOnDeprecated() { + // Can be removed when deprecated routines are removed in next major release (13.0) + // GIVEN + final double ex = 0.2; + final double ey = 0.3; + final double originalPositionAngle = 1.; + // WHEN + final double actualEccentricToMean = EquinoctialOrbit.eccentricToMean(originalPositionAngle, ex, ey); + final double actualEccentricToTrue = EquinoctialOrbit.eccentricToTrue(originalPositionAngle, ex, ey); + final double actualMeanToEccentric = EquinoctialOrbit.meanToEccentric(originalPositionAngle, ex, ey); + final double actualTrueToEccentric = EquinoctialOrbit.trueToEccentric(originalPositionAngle, ex, ey); + // THEN + Assertions.assertEquals(EquinoctialLongitudeArgumentUtility.eccentricToMean(ex, ey, originalPositionAngle), + actualEccentricToMean); + Assertions.assertEquals(EquinoctialLongitudeArgumentUtility.eccentricToTrue(ex, ey, originalPositionAngle), + actualEccentricToTrue); + Assertions.assertEquals(EquinoctialLongitudeArgumentUtility.meanToEccentric(ex, ey, originalPositionAngle), + actualMeanToEccentric); + Assertions.assertEquals(EquinoctialLongitudeArgumentUtility.trueToEccentric(ex, ey, originalPositionAngle), + actualTrueToEccentric); + } + + @Test + void testCoverageCachedPositionAngleTypeWithRates() { + // GIVEN + final double semiMajorAxis = 1e4; + final double ex = 0.; + final double ey = 0.; + final double expectedL = 0.; + final double expectedLDot = 0.; + // WHEN & THEN + for (final PositionAngleType inputPositionAngleType: PositionAngleType.values()) { + for (final PositionAngleType cachedPositionAngleType: PositionAngleType.values()) { + final EquinoctialOrbit equinoctialOrbit = new EquinoctialOrbit(semiMajorAxis, ex, ey, 0., 0., + expectedL, 0., 0., 0., 0., 0., expectedLDot, + inputPositionAngleType, cachedPositionAngleType, FramesFactory.getGCRF(), date, mu); + Assertions.assertEquals(expectedL, equinoctialOrbit.getLv()); + Assertions.assertEquals(expectedL, equinoctialOrbit.getLM()); + Assertions.assertEquals(expectedL, equinoctialOrbit.getLE()); + Assertions.assertEquals(expectedLDot, equinoctialOrbit.getLvDot()); + Assertions.assertEquals(expectedLDot, equinoctialOrbit.getLMDot()); + Assertions.assertEquals(expectedLDot, equinoctialOrbit.getLEDot()); + } + } + } + + @Test + void testCachedPositionAngleTypeTrue() { + // GIVEN + final double semiMajorAxis = 1e4; + final double ex = 1e-2; + final double ey = -1e-3; + final double expectedLv = 2.; + final PositionAngleType inputPositionAngleType = PositionAngleType.TRUE; + // WHEN & THEN + for (final PositionAngleType cachedPositionAngleType: PositionAngleType.values()) { + final EquinoctialOrbit equinoctialOrbit = new EquinoctialOrbit(semiMajorAxis, ex, ey, 0., 0., + expectedLv, inputPositionAngleType, cachedPositionAngleType, FramesFactory.getGCRF(), date, mu); + Assertions.assertEquals(expectedLv, equinoctialOrbit.getLv(), 1e-15); + final double actualL = equinoctialOrbit.getL(cachedPositionAngleType); + switch (cachedPositionAngleType) { + + case MEAN: + Assertions.assertEquals(EquinoctialLongitudeArgumentUtility.trueToMean(ex, ey, expectedLv), + actualL); + break; + + case ECCENTRIC: + Assertions.assertEquals(EquinoctialLongitudeArgumentUtility.trueToEccentric(ex, ey, expectedLv), + actualL); + break; + + case TRUE: + Assertions.assertEquals(expectedLv, actualL); + break; + } + } + } + + @Test + void testCachedPositionAngleTypeMean() { + // GIVEN + final double semiMajorAxis = 1e4; + final double ex = 1e-2; + final double ey = -1e-3; + final double expectedLM = 2.; + final PositionAngleType inputPositionAngleType = PositionAngleType.MEAN; + // WHEN & THEN + for (final PositionAngleType cachedPositionAngleType: PositionAngleType.values()) { + final EquinoctialOrbit equinoctialOrbit = new EquinoctialOrbit(semiMajorAxis, ex, ey, 0., 0., + expectedLM, inputPositionAngleType, cachedPositionAngleType, FramesFactory.getGCRF(), date, mu); + Assertions.assertEquals(expectedLM, equinoctialOrbit.getLM(), 1e-15); + final double actualL = equinoctialOrbit.getL(cachedPositionAngleType); + switch (cachedPositionAngleType) { + + case TRUE: + Assertions.assertEquals(EquinoctialLongitudeArgumentUtility.meanToTrue(ex, ey, expectedLM), + actualL); + break; + + case ECCENTRIC: + Assertions.assertEquals(EquinoctialLongitudeArgumentUtility.meanToEccentric(ex, ey, expectedLM), + actualL); + break; + + case MEAN: + Assertions.assertEquals(expectedLM, actualL); + break; + } + } + } + + @Test + void testCachedPositionAngleTypeEccentric() { + // GIVEN + final double semiMajorAxis = 1e4; + final double ex = 1e-2; + final double ey = -1e-3; + final double expectedLE = 2.; + final PositionAngleType inputPositionAngleType = PositionAngleType.ECCENTRIC; + // WHEN & THEN + for (final PositionAngleType cachedPositionAngleType: PositionAngleType.values()) { + final EquinoctialOrbit equinoctialOrbit = new EquinoctialOrbit(semiMajorAxis, ex, ey, 0., 0., + expectedLE, inputPositionAngleType, cachedPositionAngleType, FramesFactory.getGCRF(), date, mu); + Assertions.assertEquals(expectedLE, equinoctialOrbit.getLE(), 1e-15); + final double actualL = equinoctialOrbit.getL(cachedPositionAngleType); + switch (cachedPositionAngleType) { + + case TRUE: + Assertions.assertEquals(EquinoctialLongitudeArgumentUtility.eccentricToTrue(ex, ey, expectedLE), + actualL); + break; + + case MEAN: + Assertions.assertEquals(EquinoctialLongitudeArgumentUtility.eccentricToMean(ex, ey, expectedLE), + actualL); + break; + + case ECCENTRIC: + Assertions.assertEquals(expectedLE, actualL); + break; + } + } + } + @BeforeEach public void setUp() { diff --git a/src/test/java/org/orekit/orbits/FieldCircularLatitudeArgumentUtilityTest.java b/src/test/java/org/orekit/orbits/FieldCircularLatitudeArgumentUtilityTest.java new file mode 100644 index 0000000000..0b11c04360 --- /dev/null +++ b/src/test/java/org/orekit/orbits/FieldCircularLatitudeArgumentUtilityTest.java @@ -0,0 +1,157 @@ +/* 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.orbits; + +import org.hipparchus.complex.Complex; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; + +class FieldCircularLatitudeArgumentUtilityTest { + + private static final Complex EX = new Complex(0.1, 0.); + private static final Complex EY = new Complex(0.66, 0.); + private static final double TOLERANCE = 1e-10; + + @Test + void testMeanToTrueAndBack() { + // GIVEN + final Complex expectedLatitudeArgument = new Complex(3., 0.); + // WHEN + final Complex intermediateLatitudeArgument = FieldCircularLatitudeArgumentUtility.meanToTrue(EX, EY, + expectedLatitudeArgument); + final Complex actualLatitudeArgument = FieldCircularLatitudeArgumentUtility.trueToMean(EX, EY, + intermediateLatitudeArgument); + // THEN + Assertions.assertEquals(expectedLatitudeArgument.getReal(), actualLatitudeArgument.getReal(), TOLERANCE); + } + + @Test + void testEccentricToTrueAndBack() { + // GIVEN + final Complex expectedLatitudeArgument = new Complex(3., 0.); + // WHEN + final Complex intermediateLatitudeArgument = FieldCircularLatitudeArgumentUtility.eccentricToTrue(EX, EY, + expectedLatitudeArgument); + final Complex actualLatitudeArgument = FieldCircularLatitudeArgumentUtility.trueToEccentric(EX, EY, + intermediateLatitudeArgument); + // THEN + Assertions.assertEquals(expectedLatitudeArgument.getReal(), actualLatitudeArgument.getReal(), TOLERANCE); + } + + @Test + void testEccentricToMeanAndBack() { + // GIVEN + final Complex expectedLatitudeArgument = new Complex(3., 0.); + // WHEN + final Complex intermediateLatitudeArgument = FieldCircularLatitudeArgumentUtility.eccentricToMean(EX, EY, + expectedLatitudeArgument); + final Complex actualLatitudeArgument = FieldCircularLatitudeArgumentUtility.meanToEccentric(EX, EY, + intermediateLatitudeArgument); + // THEN + Assertions.assertEquals(expectedLatitudeArgument.getReal(), actualLatitudeArgument.getReal(), TOLERANCE); + } + + @Test + void testMeanToTrueVersusDouble() { + // GIVEN + final Complex fieldOriginalPositionAngle = new Complex(3., 0.); + // WHEN + final double actualConvertedPositionAngle = FieldCircularLatitudeArgumentUtility.meanToTrue( + EX, EY, fieldOriginalPositionAngle).getReal(); + // THEN + final double expectedPositionAngle = CircularLatitudeArgumentUtility.meanToTrue( + EX.getReal(), EY.getReal(), fieldOriginalPositionAngle.getReal()); + Assertions.assertEquals(expectedPositionAngle, actualConvertedPositionAngle, TOLERANCE); + } + + @Test + void testMeanToEccentricVersusDouble() { + // GIVEN + final Complex fieldOriginalPositionAngle = new Complex(3., 0.); + // WHEN + final double actualConvertedPositionAngle = FieldCircularLatitudeArgumentUtility.meanToEccentric( + EX, EY, fieldOriginalPositionAngle).getReal(); + // THEN + final double expectedPositionAngle = CircularLatitudeArgumentUtility.meanToEccentric( + EX.getReal(), EY.getReal(), fieldOriginalPositionAngle.getReal()); + Assertions.assertEquals(expectedPositionAngle, actualConvertedPositionAngle, TOLERANCE); + } + + @Test + void testTrueToEccentricVersusDouble() { + // GIVEN + final Complex fieldOriginalPositionAngle = new Complex(3., 0.); + // WHEN + final double actualConvertedPositionAngle = FieldCircularLatitudeArgumentUtility.trueToEccentric( + EX, EY, fieldOriginalPositionAngle).getReal(); + // THEN + final double expectedPositionAngle = CircularLatitudeArgumentUtility.trueToEccentric( + EX.getReal(), EY.getReal(), fieldOriginalPositionAngle.getReal()); + Assertions.assertEquals(expectedPositionAngle, actualConvertedPositionAngle, TOLERANCE); + } + + @Test + void testTrueToMeanVersusDouble() { + // GIVEN + final Complex fieldOriginalPositionAngle = new Complex(3., 0.); + // WHEN + final double actualConvertedPositionAngle = FieldCircularLatitudeArgumentUtility.trueToMean( + EX, EY, fieldOriginalPositionAngle).getReal(); + // THEN + final double expectedPositionAngle = CircularLatitudeArgumentUtility.trueToMean( + EX.getReal(), EY.getReal(), fieldOriginalPositionAngle.getReal()); + Assertions.assertEquals(expectedPositionAngle, actualConvertedPositionAngle, TOLERANCE); + } + + @Test + void testEccentricToMeanVersusDouble() { + // GIVEN + final Complex fieldOriginalPositionAngle = new Complex(3., 0.); + // WHEN + final double actualConvertedPositionAngle = FieldCircularLatitudeArgumentUtility.eccentricToMean( + EX, EY, fieldOriginalPositionAngle).getReal(); + // THEN + final double expectedPositionAngle = CircularLatitudeArgumentUtility.eccentricToMean( + EX.getReal(), EY.getReal(), fieldOriginalPositionAngle.getReal()); + Assertions.assertEquals(expectedPositionAngle, actualConvertedPositionAngle, TOLERANCE); + } + + @Test + void testEccentricToTrueVersusDouble() { + // GIVEN + final Complex fieldOriginalPositionAngle = new Complex(3., 0.); + // WHEN + final double actualConvertedPositionAngle = FieldCircularLatitudeArgumentUtility.eccentricToTrue( + EX, EY, fieldOriginalPositionAngle).getReal(); + // THEN + final double expectedPositionAngle = CircularLatitudeArgumentUtility.eccentricToTrue( + EX.getReal(), EY.getReal(), fieldOriginalPositionAngle.getReal()); + Assertions.assertEquals(expectedPositionAngle, actualConvertedPositionAngle, TOLERANCE); + } + + @Test + void testMeanToEccentricException() { + // GIVEN + final Complex fieldNaNPositionAngle = Complex.NaN; + // WHEN & THEN + Assertions.assertThrows(OrekitException.class, () -> FieldCircularLatitudeArgumentUtility.meanToEccentric(EX, + EY, fieldNaNPositionAngle), OrekitMessages.UNABLE_TO_COMPUTE_ECCENTRIC_LATITUDE_ARGUMENT.toString()); + } + +} \ No newline at end of file diff --git a/src/test/java/org/orekit/orbits/FieldCircularOrbitTest.java b/src/test/java/org/orekit/orbits/FieldCircularOrbitTest.java index c284134c3f..99425dca61 100644 --- a/src/test/java/org/orekit/orbits/FieldCircularOrbitTest.java +++ b/src/test/java/org/orekit/orbits/FieldCircularOrbitTest.java @@ -29,6 +29,7 @@ import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.linear.FieldMatrixPreservingVisitor; import org.hipparchus.linear.MatrixUtils; +import org.hipparchus.util.Binary64; import org.hipparchus.util.Binary64Field; import org.hipparchus.util.FastMath; import org.hipparchus.util.MathArrays; @@ -55,7 +56,7 @@ import static org.orekit.OrekitMatchers.relativelyCloseTo; -public class FieldCircularOrbitTest { +class FieldCircularOrbitTest { // Body mu private double mu; @@ -71,144 +72,144 @@ public void setUp() { } @Test - public void testCircularToEquinoc() { + void testCircularToEquinoc() { doTestCircularToEquinoctialEll(Binary64Field.getInstance()); } @Test - public void testCircToEquinoc() { + void testCircToEquinoc() { doTestCircularToEquinoctialCirc(Binary64Field.getInstance()); } @Test - public void testAnomalyCirc() { + void testAnomalyCirc() { doTestAnomalyCirc(Binary64Field.getInstance()); } @Test - public void testAnomalyEll() { + void testAnomalyEll() { doTestAnomalyEll(Binary64Field.getInstance()); } @Test - public void testCircToCart() { + void testCircToCart() { doTestCircularToCartesian(Binary64Field.getInstance()); } @Test - public void testCircToKepl() { + void testCircToKepl() { doTestCircularToKeplerian(Binary64Field.getInstance()); } @Test - public void testGeometryCirc() { + void testGeometryCirc() { doTestGeometryCirc(Binary64Field.getInstance()); } @Test - public void testGeometryEll() { + void testGeometryEll() { doTestGeometryEll(Binary64Field.getInstance()); } @Test - public void testJacobianFinited() { + void testJacobianFinited() { doTestJacobianFinitedifferences(Binary64Field.getInstance()); } @Test - public void testJacoabianReference() { + void testJacoabianReference() { doTestJacobianReference(Binary64Field.getInstance()); } @Test - public void testNumericalIssue25() { + void testNumericalIssue25() { doTestNumericalIssue25(Binary64Field.getInstance()); } @Test - public void testPerfectlyEquatorial() { + void testPerfectlyEquatorial() { doTestPerfectlyEquatorial(Binary64Field.getInstance()); } @Test - public void testPositionVelocityNormsCirc() { + void testPositionVelocityNormsCirc() { doTestPositionVelocityNormsCirc(Binary64Field.getInstance()); } @Test - public void testPositionVelocity() { + void testPositionVelocity() { doTestPositionVelocityNormsEll(Binary64Field.getInstance()); } @Test - public void testSymmetryCir() { + void testSymmetryCir() { doTestSymmetryCir(Binary64Field.getInstance()); } @Test - public void testSymmetryEll() { + void testSymmetryEll() { doTestSymmetryEll(Binary64Field.getInstance()); } @Test - public void testErrors() { + void testErrors() { Assertions.assertThrows(IllegalArgumentException.class, () -> { doTestNonInertialFrame(Binary64Field.getInstance()); }); } @Test - public void testHyperbolic1() { + void testHyperbolic1() { doTestHyperbolic1(Binary64Field.getInstance()); } @Test - public void testHyperbolic2() { + void testHyperbolic2() { doTestHyperbolic2(Binary64Field.getInstance()); } @Test - public void testToOrbitWithoutDerivatives() { + void testToOrbitWithoutDerivatives() { doTestToOrbitWithoutDerivatives(Binary64Field.getInstance()); } @Test - public void testToOrbitWithDerivatives() { + void testToOrbitWithDerivatives() { doTestToOrbitWithDerivatives(Binary64Field.getInstance()); } @Test - public void testDerivativesConversionSymmetry() { + void testDerivativesConversionSymmetry() { doTestDerivativesConversionSymmetry(Binary64Field.getInstance()); } @Test - public void testToString() { + void testToString() { doTestToString(Binary64Field.getInstance()); } @Test - public void testNonKeplerianDerivatives() { + void testNonKeplerianDerivatives() { doTestNonKeplerianDerivatives(Binary64Field.getInstance()); } @Test - public void testPositionAngleDerivatives() { + void testPositionAngleDerivatives() { doTestPositionAngleDerivatives(Binary64Field.getInstance()); } @Test - public void testEquatorialRetrograde() { + void testEquatorialRetrograde() { doTestEquatorialRetrograde(Binary64Field.getInstance()); } @Test - public void testCopyNonKeplerianAcceleration() { + void testCopyNonKeplerianAcceleration() { doTestCopyNonKeplerianAcceleration(Binary64Field.getInstance()); } @Test - public void testNormalize() { + void testNormalize() { doTestNormalize(Binary64Field.getInstance()); } @@ -301,6 +302,124 @@ private > void compareFieldOrbitToOrbit(final } } + @Test + void testCoverageCachedPositionAngleTypeWithRates() { + // GIVEN + final double semiMajorAxis = 1e4; + final double eccentricity = 0.; + final double expectedAlpha = 0.; + final double expectedAlphaDot = 0.; + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final Binary64Field field = Binary64Field.getInstance(); + final Binary64 zero = field.getZero(); + // WHEN & THEN + for (final PositionAngleType inputPositionAngleType: PositionAngleType.values()) { + for (final PositionAngleType cachedPositionAngleType: PositionAngleType.values()) { + final FieldCircularOrbit fieldOrbit = new FieldCircularOrbit<>( + zero.newInstance(semiMajorAxis), zero.newInstance(eccentricity), zero, zero, zero, + zero.newInstance(expectedAlpha), zero, zero, zero, zero, zero, + zero.newInstance(expectedAlphaDot), inputPositionAngleType, cachedPositionAngleType, + FramesFactory.getGCRF(), new FieldAbsoluteDate<>(field, date), zero.newInstance(mu)); + Assertions.assertEquals(cachedPositionAngleType, fieldOrbit.getCachedPositionAngleType()); + Assertions.assertEquals(expectedAlpha, fieldOrbit.getAlphaV().getReal()); + Assertions.assertEquals(expectedAlpha, fieldOrbit.getAlphaM().getReal()); + Assertions.assertEquals(expectedAlpha, fieldOrbit.getAlphaE().getReal()); + Assertions.assertEquals(expectedAlphaDot, fieldOrbit.getAlphaVDot().getReal()); + Assertions.assertEquals(expectedAlphaDot, fieldOrbit.getAlphaMDot().getReal()); + Assertions.assertEquals(expectedAlphaDot, fieldOrbit.getAlphaEDot().getReal()); + } + } + } + + @Test + void testGetAlphaVersusDouble() { + // GIVEN + final double semiMajorAxis = 1e7; + final double eccentricity = 1e-2; + final double expectedAlpha = 4.; + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final Binary64Field field = Binary64Field.getInstance(); + final Binary64 zero = field.getZero(); + // WHEN & THEN + for (final PositionAngleType inputPositionAngleType: PositionAngleType.values()) { + for (final PositionAngleType cachedPositionAngleType: PositionAngleType.values()) { + final FieldCircularOrbit fieldOrbit = new FieldCircularOrbit<>( + zero.newInstance(semiMajorAxis), zero.newInstance(eccentricity), zero, zero, zero, + zero.newInstance(expectedAlpha), inputPositionAngleType, cachedPositionAngleType, + FramesFactory.getGCRF(), new FieldAbsoluteDate<>(field, date), zero.newInstance(mu)); + final CircularOrbit circularOrbit = fieldOrbit.toOrbit(); + Assertions.assertEquals(circularOrbit.getAlphaV(), fieldOrbit.getAlphaV().getReal()); + Assertions.assertEquals(circularOrbit.getAlphaM(), fieldOrbit.getAlphaM().getReal()); + Assertions.assertEquals(circularOrbit.getAlphaE(), fieldOrbit.getAlphaE().getReal()); + } + } + } + + + @Test + @Deprecated + void testTrueToEccentric() { + // GIVEN + final ComplexField field = ComplexField.getInstance(); + final Complex zero = field.getZero(); + final Complex ex = zero.newInstance(1e-2); + final Complex ey = zero.newInstance(1.e-3); + final Complex inputAnomaly = zero.newInstance(1.); + // WHEN + final Complex actualL = FieldCircularOrbit.trueToEccentric(inputAnomaly, ex, ey); + // THEN + final Complex expectedL = FieldCircularLatitudeArgumentUtility.trueToEccentric(ex, ey, inputAnomaly); + Assertions.assertEquals(expectedL.getReal(), actualL.getReal()); + } + + @Test + @Deprecated + void testEccentricToTrue() { + // GIVEN + final ComplexField field = ComplexField.getInstance(); + final Complex zero = field.getZero(); + final Complex ex = zero.newInstance(1e-2); + final Complex ey = zero.newInstance(1.e-3); + final Complex inputAnomaly = zero.newInstance(1.); + // WHEN + final Complex actualL = FieldCircularOrbit.eccentricToTrue(inputAnomaly, ex, ey); + // THEN + final Complex expectedL = FieldCircularLatitudeArgumentUtility.eccentricToTrue(ex, ey, inputAnomaly); + Assertions.assertEquals(expectedL.getReal(), actualL.getReal()); + } + + @Test + @Deprecated + void testMeanToEccentric() { + // GIVEN + final ComplexField field = ComplexField.getInstance(); + final Complex zero = field.getZero(); + final Complex ex = zero.newInstance(1e-2); + final Complex ey = zero.newInstance(1.e-3); + final Complex inputAnomaly = zero.newInstance(1.); + // WHEN + final Complex actualL = FieldCircularOrbit.meanToEccentric(inputAnomaly, ex, ey); + // THEN + final Complex expectedL = FieldCircularLatitudeArgumentUtility.meanToEccentric(ex, ey, inputAnomaly); + Assertions.assertEquals(expectedL.getReal(), actualL.getReal()); + } + + @Test + @Deprecated + void testEccentricToMean() { + // GIVEN + final ComplexField field = ComplexField.getInstance(); + final Complex zero = field.getZero(); + final Complex ex = zero.newInstance(1e-2); + final Complex ey = zero.newInstance(1.e-3); + final Complex inputAnomaly = zero.newInstance(1.); + // WHEN + final Complex actualL = FieldCircularOrbit.eccentricToMean(inputAnomaly, ex, ey); + // THEN + final Complex expectedL = FieldCircularLatitudeArgumentUtility.eccentricToMean(ex, ey, inputAnomaly); + Assertions.assertEquals(expectedL.getReal(), actualL.getReal()); + } + private > void doTestCircularToEquinoctialEll(Field field) { T zero = field.getZero(); @@ -334,7 +453,9 @@ private > void doTestCircularToEquinoctialEll( Assertions.assertEquals(param.getHy().getReal(), circ.getHy().getReal(), Utils.epsilonAngle * FastMath.abs(circ.getI().getReal())); - Assertions.assertEquals(MathUtils.normalizeAngle(param.getLv().getReal(), circ.getLv().getReal()), circ.getLv().getReal(), Utils.epsilonAngle * FastMath.abs(circ.getLv().getReal())); + Assertions.assertEquals(MathUtils.normalizeAngle(param.getLv().getReal(), circ.getLv().getReal()), + circ.getLv().getReal(), + Utils.epsilonAngle * FastMath.abs(circ.getLv().getReal())); Assertions.assertFalse(circ.hasDerivatives()); Assertions.assertNull(circ.getADot()); @@ -404,9 +525,10 @@ private > void doTestToOrbitWithDerivatives(Fi FieldVector3D velocity = new FieldVector3D<>(zero.add(-500.0), zero.add(8000.0), zero.add(1000.0)); T r2 = position.getNormSq(); T r = r2.sqrt(); - FieldPVCoordinates pvCoordinates = new FieldPVCoordinates<>(position, velocity, - new FieldVector3D<>(r.multiply(r2).reciprocal().multiply(zero.add(mu).negate()), - position)); + final FieldVector3D keplerianAcceleration = new FieldVector3D<>(r.multiply(r2).reciprocal().multiply(zero.add(mu).negate()), + position); + final FieldVector3D nonKeplerianAcceleration = keplerianAcceleration.scalarMultiply(1.1); + final FieldPVCoordinates pvCoordinates = new FieldPVCoordinates<>(position, velocity, nonKeplerianAcceleration); FieldCircularOrbit fieldOrbit = new FieldCircularOrbit<>(pvCoordinates, FramesFactory.getEME2000(), date, zero.add(mu)); CircularOrbit orbit = fieldOrbit.toOrbit(); Assertions.assertTrue(orbit.hasDerivatives()); @@ -546,8 +668,9 @@ private > void doTestHyperbolic1(Field fiel T zero = field.getZero(); FieldAbsoluteDate date = new FieldAbsoluteDate<>(field); try { - new FieldCircularOrbit<>(zero.add(42166712.0), zero.add(0.9), zero.add(0.5), zero.add(0.01), zero.add(-0.02), zero.add( 5.300), + new FieldCircularOrbit<>(zero.add(-42166712.0), zero.add(1.9), zero.add(0.5), zero.add(0.01), zero.add(-0.02), zero.add( 5.300), PositionAngleType.MEAN, FramesFactory.getEME2000(), date, zero.add(mu)); + Assertions.fail("an exception should have been thrown"); } catch (OrekitIllegalArgumentException oe) { Assertions.assertEquals(OrekitMessages.HYPERBOLIC_ORBIT_NOT_HANDLED_AS, oe.getSpecifier()); } @@ -556,10 +679,11 @@ private > void doTestHyperbolic1(Field fiel private > void doTestHyperbolic2(Field field) { T zero = field.getZero(); FieldAbsoluteDate date = new FieldAbsoluteDate<>(field); - FieldOrbit orbit = new FieldKeplerianOrbit<>(zero.add(42166712.0), zero.add(0.9), zero.add(0.5), zero.add(0.01), zero.add(-0.02), zero.add( 5.300), + FieldOrbit orbit = new FieldKeplerianOrbit<>(zero.add(-42166712.0), zero.add(1.9), zero.add(0.5), zero.add(0.01), zero.add(-0.02), zero.add( 5.300), PositionAngleType.MEAN, FramesFactory.getEME2000(), date, zero.add(mu)); try { new FieldCircularOrbit<>(orbit.getPVCoordinates(), orbit.getFrame(), orbit.getMu()); + Assertions.fail("an exception should have been thrown"); } catch (OrekitIllegalArgumentException oe) { Assertions.assertEquals(OrekitMessages.HYPERBOLIC_ORBIT_NOT_HANDLED_AS, oe.getSpecifier()); } diff --git a/src/test/java/org/orekit/orbits/FieldEquinoctialLongitudeArgumentUtilityTest.java b/src/test/java/org/orekit/orbits/FieldEquinoctialLongitudeArgumentUtilityTest.java new file mode 100644 index 0000000000..48feb9fe2c --- /dev/null +++ b/src/test/java/org/orekit/orbits/FieldEquinoctialLongitudeArgumentUtilityTest.java @@ -0,0 +1,157 @@ +/* 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.orbits; + +import org.hipparchus.complex.Complex; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; + +class FieldEquinoctialLongitudeArgumentUtilityTest { + + private static final Complex EX = new Complex(0.1, 0.); + private static final Complex EY = new Complex(0.66, 0.); + private static final double TOLERANCE = 1e-10; + + @Test + void testMeanToTrueAndBack() { + // GIVEN + final Complex expectedLatitudeArgument = new Complex(3., 0.); + // WHEN + final Complex intermediateLatitudeArgument = FieldEquinoctialLongitudeArgumentUtility.meanToTrue( + EX, EY, expectedLatitudeArgument); + final Complex actualLatitudeArgument = FieldEquinoctialLongitudeArgumentUtility.trueToMean( + EX, EY, intermediateLatitudeArgument); + // THEN + Assertions.assertEquals(expectedLatitudeArgument.getReal(), actualLatitudeArgument.getReal(), TOLERANCE); + } + + @Test + void testEccentricToTrueAndBack() { + // GIVEN + final Complex expectedLatitudeArgument = new Complex(3., 0.); + // WHEN + final Complex intermediateLatitudeArgument = FieldEquinoctialLongitudeArgumentUtility + .eccentricToTrue(EX, EY, expectedLatitudeArgument); + final Complex actualLatitudeArgument = FieldEquinoctialLongitudeArgumentUtility + .trueToEccentric(EX, EY, intermediateLatitudeArgument); + // THEN + Assertions.assertEquals(expectedLatitudeArgument.getReal(), actualLatitudeArgument.getReal(), TOLERANCE); + } + + @Test + void testEccentricToMeanAndBack() { + // GIVEN + final Complex expectedLatitudeArgument = new Complex(3., 0.); + // WHEN + final Complex intermediateLatitudeArgument = FieldEquinoctialLongitudeArgumentUtility + .eccentricToMean(EX, EY, expectedLatitudeArgument); + final Complex actualLatitudeArgument = FieldEquinoctialLongitudeArgumentUtility + .meanToEccentric(EX, EY, intermediateLatitudeArgument); + // THEN + Assertions.assertEquals(expectedLatitudeArgument.getReal(), actualLatitudeArgument.getReal(), TOLERANCE); + } + + @Test + void testMeanToTrueVersusDouble() { + // GIVEN + final Complex fieldOriginalPositionAngle = new Complex(3., 0.); + // WHEN + final double actualConvertedPositionAngle = FieldEquinoctialLongitudeArgumentUtility.meanToTrue( + EX, EY, fieldOriginalPositionAngle).getReal(); + // THEN + final double expectedPositionAngle = CircularLatitudeArgumentUtility.meanToTrue( + EX.getReal(), EY.getReal(), fieldOriginalPositionAngle.getReal()); + Assertions.assertEquals(expectedPositionAngle, actualConvertedPositionAngle, TOLERANCE); + } + + @Test + void testMeanToEccentricVersusDouble() { + // GIVEN + final Complex fieldOriginalPositionAngle = new Complex(3., 0.); + // WHEN + final double actualConvertedPositionAngle = FieldEquinoctialLongitudeArgumentUtility.meanToEccentric( + EX, EY, fieldOriginalPositionAngle).getReal(); + // THEN + final double expectedPositionAngle = CircularLatitudeArgumentUtility.meanToEccentric( + EX.getReal(), EY.getReal(), fieldOriginalPositionAngle.getReal()); + Assertions.assertEquals(expectedPositionAngle, actualConvertedPositionAngle, TOLERANCE); + } + + @Test + void testTrueToEccentricVersusDouble() { + // GIVEN + final Complex fieldOriginalPositionAngle = new Complex(3., 0.); + // WHEN + final double actualConvertedPositionAngle = FieldEquinoctialLongitudeArgumentUtility.trueToEccentric( + EX, EY, fieldOriginalPositionAngle).getReal(); + // THEN + final double expectedPositionAngle = CircularLatitudeArgumentUtility.trueToEccentric( + EX.getReal(), EY.getReal(), fieldOriginalPositionAngle.getReal()); + Assertions.assertEquals(expectedPositionAngle, actualConvertedPositionAngle, TOLERANCE); + } + + @Test + void testTrueToMeanVersusDouble() { + // GIVEN + final Complex fieldOriginalPositionAngle = new Complex(3., 0.); + // WHEN + final double actualConvertedPositionAngle = FieldEquinoctialLongitudeArgumentUtility.trueToMean( + EX, EY, fieldOriginalPositionAngle).getReal(); + // THEN + final double expectedPositionAngle = CircularLatitudeArgumentUtility.trueToMean( + EX.getReal(), EY.getReal(), fieldOriginalPositionAngle.getReal()); + Assertions.assertEquals(expectedPositionAngle, actualConvertedPositionAngle, TOLERANCE); + } + + @Test + void testEccentricToMeanVersusDouble() { + // GIVEN + final Complex fieldOriginalPositionAngle = new Complex(3., 0.); + // WHEN + final double actualConvertedPositionAngle = FieldEquinoctialLongitudeArgumentUtility.eccentricToMean( + EX, EY, fieldOriginalPositionAngle).getReal(); + // THEN + final double expectedPositionAngle = CircularLatitudeArgumentUtility.eccentricToMean( + EX.getReal(), EY.getReal(), fieldOriginalPositionAngle.getReal()); + Assertions.assertEquals(expectedPositionAngle, actualConvertedPositionAngle, TOLERANCE); + } + + @Test + void testEccentricToTrueVersusDouble() { + // GIVEN + final Complex fieldOriginalPositionAngle = new Complex(3., 0.); + // WHEN + final double actualConvertedPositionAngle = FieldEquinoctialLongitudeArgumentUtility.eccentricToTrue( + EX, EY, fieldOriginalPositionAngle).getReal(); + // THEN + final double expectedPositionAngle = CircularLatitudeArgumentUtility.eccentricToTrue( + EX.getReal(), EY.getReal(), fieldOriginalPositionAngle.getReal()); + Assertions.assertEquals(expectedPositionAngle, actualConvertedPositionAngle, TOLERANCE); + } + + @Test + void testMeanToEccentricException() { + // GIVEN + final Complex fieldNaNPositionAngle = Complex.NaN; + // WHEN & THEN + Assertions.assertThrows(OrekitException.class, () -> FieldEquinoctialLongitudeArgumentUtility.meanToEccentric(EX, + EY, fieldNaNPositionAngle), OrekitMessages.UNABLE_TO_COMPUTE_ECCENTRIC_LONGITUDE_ARGUMENT.toString()); + } + +} \ No newline at end of file diff --git a/src/test/java/org/orekit/orbits/FieldEquinoctialOrbitTest.java b/src/test/java/org/orekit/orbits/FieldEquinoctialOrbitTest.java index 1a91ad70e7..66f6e6abc6 100644 --- a/src/test/java/org/orekit/orbits/FieldEquinoctialOrbitTest.java +++ b/src/test/java/org/orekit/orbits/FieldEquinoctialOrbitTest.java @@ -28,6 +28,7 @@ import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.linear.FieldMatrixPreservingVisitor; import org.hipparchus.linear.MatrixUtils; +import org.hipparchus.util.Binary64; import org.hipparchus.util.Binary64Field; import org.hipparchus.util.FastMath; import org.hipparchus.util.MathArrays; @@ -36,6 +37,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.orekit.Utils; +import org.orekit.errors.OrekitIllegalArgumentException; +import org.orekit.errors.OrekitMessages; import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; import org.orekit.frames.Transform; @@ -52,7 +55,7 @@ import static org.orekit.OrekitMatchers.relativelyCloseTo; -public class FieldEquinoctialOrbitTest { +class FieldEquinoctialOrbitTest { // Body mu private double mu; @@ -67,121 +70,124 @@ public void setUp() { } @Test - public void testEquinoctialToEquinoctialEll() { + void testEquinoctialToEquinoctialEll() { doTestEquinoctialToEquinoctialEll(Binary64Field.getInstance()); } @Test - public void testEquinoctialToEquinoctialCirc() { + void testEquinoctialToEquinoctialCirc() { doTestEquinoctialToEquinoctialCirc(Binary64Field.getInstance()); } @Test - public void testEquinoctialToCartesian() { + void testEquinoctialToCartesian() { doTestEquinoctialToCartesian(Binary64Field.getInstance()); } @Test - public void testEquinoctialToKeplerian() { + void testEquinoctialToKeplerian() { doTestEquinoctialToKeplerian(Binary64Field.getInstance()); } @Test - public void testNumericalIssue25() { + void testNumericalIssue25() { doTestNumericalIssue25(Binary64Field.getInstance()); } @Test - public void testAnomaly() { + void testAnomaly() { doTestAnomaly(Binary64Field.getInstance()); } @Test - public void testPositionVelocityNorms() { + void testPositionVelocityNorms() { doTestPositionVelocityNorms(Binary64Field.getInstance()); } @Test - public void testGeometry() { + void testGeometry() { doTestGeometry(Binary64Field.getInstance()); } @Test - public void testRadiusOfCurvature() { + void testRadiusOfCurvature() { doTestRadiusOfCurvature(Binary64Field.getInstance()); } @Test - public void testSymmetry() { + void testSymmetry() { doTestSymmetry(Binary64Field.getInstance()); } @Test - public void testJacobianReference() { + void testJacobianReference() { doTestJacobianReference(Binary64Field.getInstance()); } @Test - public void testJacobianFinitedifferences() { + void testJacobianFinitedifferences() { doTestJacobianFinitedifferences(Binary64Field.getInstance()); } @Test - public void testHyperbolic() { - Assertions.assertThrows(IllegalArgumentException.class, () -> { - doTestHyperbolic(Binary64Field.getInstance()); - }); + void testHyperbolic1() { + doTestHyperbolic1(Binary64Field.getInstance()); } @Test - public void testToOrbitWithoutDerivatives() { + void testHyperbolic2() { + doTestHyperbolic2(Binary64Field.getInstance()); + } + + @Test + void testToOrbitWithoutDerivatives() { doTestToOrbitWithoutDerivatives(Binary64Field.getInstance()); } @Test - public void testToOrbitWithDerivatives() { + void testToOrbitWithDerivatives() { doTestToOrbitWithDerivatives(Binary64Field.getInstance()); } @Test - public void testDerivativesConversionSymmetry() { + void testDerivativesConversionSymmetry() { doTestDerivativesConversionSymmetry(Binary64Field.getInstance()); } @Test - public void testToString() { + void testToString() { doTestToString(Binary64Field.getInstance()); } @Test - public void testNonInertialFrame() { + void testNonInertialFrame() { Assertions.assertThrows(IllegalArgumentException.class, () -> { doTestNonInertialFrame(Binary64Field.getInstance()); }); } @Test - public void testNonKeplerianDerivatives() { + void testNonKeplerianDerivatives() { doTestNonKeplerianDerivatives(Binary64Field.getInstance()); } @Test - public void testPositionAngleDerivatives() { + void testPositionAngleDerivatives() { doTestPositionAngleDerivatives(Binary64Field.getInstance()); } @Test - public void testEquatorialRetrograde() { + void testEquatorialRetrograde() { doTestEquatorialRetrograde(Binary64Field.getInstance()); } @Test - public void testCopyNonKeplerianAcceleration() { + void testCopyNonKeplerianAcceleration() { doTestCopyNonKeplerianAcceleration(Binary64Field.getInstance()); } @Test - public void testNormalize() { + void testNormalize() { doTestNormalize(Binary64Field.getInstance()); } @@ -243,6 +249,36 @@ private EquinoctialOrbit createOrbitTestFromEquinoctialOrbit(final boolean withD } } + @Test + void testCoverageCachedPositionAngleTypeWithRates() { + // GIVEN + final double semiMajorAxis = 1e4; + final double ex = 0.; + final double ey = 0.; + final double expectedL = 0.; + final double expectedLDot = 0.; + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final Binary64Field field = Binary64Field.getInstance(); + final Binary64 zero = field.getZero(); + // WHEN & THEN + for (final PositionAngleType inputPositionAngleType: PositionAngleType.values()) { + for (final PositionAngleType cachedPositionAngleType: PositionAngleType.values()) { + final FieldEquinoctialOrbit fieldOrbit = new FieldEquinoctialOrbit<>( + zero.newInstance(semiMajorAxis), zero.newInstance(ex), zero.newInstance(ey), zero, zero, + zero.newInstance(expectedL), zero, zero, zero, zero, zero, zero.newInstance(expectedLDot), + inputPositionAngleType, cachedPositionAngleType, + FramesFactory.getGCRF(), new FieldAbsoluteDate<>(field, date), zero.newInstance(mu)); + Assertions.assertEquals(cachedPositionAngleType, fieldOrbit.getCachedPositionAngleType()); + Assertions.assertEquals(expectedL, fieldOrbit.getLv().getReal()); + Assertions.assertEquals(expectedL, fieldOrbit.getLM().getReal()); + Assertions.assertEquals(expectedL, fieldOrbit.getLE().getReal()); + Assertions.assertEquals(expectedLDot, fieldOrbit.getLvDot().getReal()); + Assertions.assertEquals(expectedLDot, fieldOrbit.getLMDot().getReal()); + Assertions.assertEquals(expectedLDot, fieldOrbit.getLEDot().getReal()); + } + } + } + private > void compareFieldOrbitToOrbit(final FieldEquinoctialOrbit fieldOrbit, final EquinoctialOrbit orbit) { Assertions.assertEquals(orbit.getFrame(), fieldOrbit.getFrame()); @@ -272,6 +308,95 @@ private > void compareFieldOrbitToOrbit(final } } + @Test + @Deprecated + void testTrueToEccentric() { + // GIVEN + final ComplexField field = ComplexField.getInstance(); + final Complex zero = field.getZero(); + final Complex ex = zero.newInstance(1e-2); + final Complex ey = zero.newInstance(1.e-3); + final Complex inputAnomaly = zero.newInstance(1.); + // WHEN + final Complex actualL = FieldEquinoctialOrbit.trueToEccentric(inputAnomaly, ex, ey); + // THEN + final Complex expectedL = FieldEquinoctialLongitudeArgumentUtility.trueToEccentric(ex, ey, inputAnomaly); + Assertions.assertEquals(expectedL.getReal(), actualL.getReal()); + } + + @Test + @Deprecated + void testEccentricToTrue() { + // GIVEN + final ComplexField field = ComplexField.getInstance(); + final Complex zero = field.getZero(); + final Complex ex = zero.newInstance(1e-2); + final Complex ey = zero.newInstance(1.e-3); + final Complex inputAnomaly = zero.newInstance(1.); + // WHEN + final Complex actualL = FieldEquinoctialOrbit.eccentricToTrue(inputAnomaly, ex, ey); + // THEN + final Complex expectedL = FieldEquinoctialLongitudeArgumentUtility.eccentricToTrue(ex, ey, inputAnomaly); + Assertions.assertEquals(expectedL.getReal(), actualL.getReal()); + } + + @Test + @Deprecated + void testMeanToEccentric() { + // GIVEN + final ComplexField field = ComplexField.getInstance(); + final Complex zero = field.getZero(); + final Complex ex = zero.newInstance(1e-2); + final Complex ey = zero.newInstance(1.e-3); + final Complex inputAnomaly = zero.newInstance(1.); + // WHEN + final Complex actualL = FieldEquinoctialOrbit.meanToEccentric(inputAnomaly, ex, ey); + // THEN + final Complex expectedL = FieldEquinoctialLongitudeArgumentUtility.meanToEccentric(ex, ey, inputAnomaly); + Assertions.assertEquals(expectedL.getReal(), actualL.getReal()); + } + + @Test + @Deprecated + void testEccentricToMean() { + // GIVEN + final ComplexField field = ComplexField.getInstance(); + final Complex zero = field.getZero(); + final Complex ex = zero.newInstance(1e-2); + final Complex ey = zero.newInstance(1.e-3); + final Complex inputAnomaly = zero.newInstance(1.); + // WHEN + final Complex actualL = FieldEquinoctialOrbit.eccentricToMean(inputAnomaly, ex, ey); + // THEN + final Complex expectedL = FieldEquinoctialLongitudeArgumentUtility.eccentricToMean(ex, ey, inputAnomaly); + Assertions.assertEquals(expectedL.getReal(), actualL.getReal()); + } + + @Test + void testGetLVersusDouble() { + // GIVEN + final double semiMajorAxis = 1e7; + final double ex = 1e-2; + final double ey = 1e-3; + final double expectedL = 2; + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final Binary64Field field = Binary64Field.getInstance(); + final Binary64 zero = field.getZero(); + // WHEN & THEN + for (final PositionAngleType inputPositionAngleType: PositionAngleType.values()) { + for (final PositionAngleType cachedPositionAngleType: PositionAngleType.values()) { + final FieldEquinoctialOrbit fieldOrbit = new FieldEquinoctialOrbit<>( + zero.newInstance(semiMajorAxis), zero.newInstance(ex), zero.newInstance(ey), zero, zero, + zero.newInstance(expectedL), inputPositionAngleType, cachedPositionAngleType, + FramesFactory.getGCRF(), new FieldAbsoluteDate<>(field, date), zero.newInstance(mu)); + final EquinoctialOrbit equinoctialOrbit = fieldOrbit.toOrbit(); + Assertions.assertEquals(equinoctialOrbit.getLE(), fieldOrbit.getLE().getReal()); + Assertions.assertEquals(equinoctialOrbit.getLv(), fieldOrbit.getLv().getReal()); + Assertions.assertEquals(equinoctialOrbit.getLM(), fieldOrbit.getLM().getReal()); + } + } + } + private > void doTestEquinoctialToEquinoctialEll(Field field) { T zero = field.getZero(); FieldAbsoluteDate date = new FieldAbsoluteDate<>(field); @@ -303,7 +428,7 @@ private > void doTestEquinoctialToEquinoctialE Assertions.assertEquals(param.getHy().getReal(), equi.getHy().getReal(), Utils.epsilonAngle * FastMath.abs(equi.getI().getReal())); Assertions.assertEquals(MathUtils.normalizeAngle(param.getLv().getReal(), equi.getLv().getReal()), equi.getLv().getReal(), - Utils.epsilonAngle * FastMath.abs(equi.getLv().getReal())); + Utils.epsilonAngle * FastMath.abs(equi.getLv().getReal())); } @@ -414,11 +539,30 @@ private > void doTestEquinoctialToKeplerian(Fi } - private > void doTestHyperbolic(Field field) { - T zero = field.getZero(); - FieldAbsoluteDate date = new FieldAbsoluteDate<>(field); - new FieldEquinoctialOrbit<>(zero.add(42166712.0), zero.add(0.9), zero.add(0.5), zero.add(0.01), zero.add(-0.02), zero.add(5.300), - PositionAngleType.MEAN, FramesFactory.getEME2000(), date, zero.add(mu)); + private > void doTestHyperbolic1(Field field) { + try { + T zero = field.getZero(); + FieldAbsoluteDate date = new FieldAbsoluteDate<>(field); + new FieldEquinoctialOrbit<>(zero.add(-42166712.0), zero.add(1.9), zero.add(0.5), zero.add(0.01), zero.add(-0.02), zero.add(5.300), + PositionAngleType.MEAN, FramesFactory.getEME2000(), date, zero.add(mu)); + Assertions.fail("an exception should have been thrown"); + } catch (OrekitIllegalArgumentException oe) { + Assertions.assertEquals(OrekitMessages.HYPERBOLIC_ORBIT_NOT_HANDLED_AS, oe.getSpecifier()); + } + } + + private > void doTestHyperbolic2(Field field) { + T zero = field.getZero(); + FieldAbsoluteDate date = new FieldAbsoluteDate<>(field); + FieldOrbit orbit = new FieldKeplerianOrbit<>(zero.add(-42166712.0), zero.add(1.9), zero.add(0.5), + zero.add(0.01), zero.add(-0.02), zero.add(5.300), + PositionAngleType.MEAN, FramesFactory.getEME2000(), date, zero.add(mu)); + try { + new FieldEquinoctialOrbit<>(orbit.getPVCoordinates(), orbit.getFrame(), orbit.getMu()); + Assertions.fail("an exception should have been thrown"); + } catch (OrekitIllegalArgumentException oe) { + Assertions.assertEquals(OrekitMessages.HYPERBOLIC_ORBIT_NOT_HANDLED_AS, oe.getSpecifier()); + } } private > void doTestToOrbitWithoutDerivatives(Field field) { @@ -460,10 +604,11 @@ private > void doTestToOrbitWithDerivatives(Fi FieldVector3D velocity = new FieldVector3D<>(zero.add(-500.0), zero.add(8000.0), zero.add(1000.0)); T r2 = position.getNormSq(); T r = r2.sqrt(); - FieldPVCoordinates pvCoordinates = new FieldPVCoordinates<>(position, velocity, - new FieldVector3D<>(r.multiply(r2).reciprocal().multiply(-mu), - position)); - FieldEquinoctialOrbit fieldOrbit = new FieldEquinoctialOrbit<>(pvCoordinates, FramesFactory.getEME2000(), date, zero.add(mu)); + final FieldVector3D keplerianAcceleration = new FieldVector3D<>(r.multiply(r2).reciprocal().multiply(zero.add(mu).negate()), + position); + final FieldVector3D nonKeplerianAcceleration = keplerianAcceleration.scalarMultiply(1.1); + final FieldPVCoordinates pvCoordinates = new FieldPVCoordinates<>(position, velocity, nonKeplerianAcceleration); + final FieldEquinoctialOrbit fieldOrbit = new FieldEquinoctialOrbit<>(pvCoordinates, FramesFactory.getEME2000(), date, zero.add(mu)); EquinoctialOrbit orbit = fieldOrbit.toOrbit(); Assertions.assertTrue(orbit.hasDerivatives()); MatcherAssert.assertThat(orbit.getA(), relativelyCloseTo(fieldOrbit.getA().getReal(), 0)); @@ -1314,7 +1459,7 @@ private > void doTestNormalize(Field field) Assertions.assertNull(normalized1.getEquinoctialEyDot()); Assertions.assertNull(normalized1.getHxDot()); Assertions.assertNull(normalized1.getHyDot()); - Assertions.assertNull(normalized1.getLvDot()); + Assertions.assertNull(normalized1.getLDot(normalized1.getCachedPositionAngleType())); T[] p = MathArrays.buildArray(field, 6); T[] pDot = MathArrays.buildArray(field, 6); diff --git a/src/test/java/org/orekit/orbits/FieldKeplerianOrbitTest.java b/src/test/java/org/orekit/orbits/FieldKeplerianOrbitTest.java index 6b55252670..1a3f1bcf5d 100644 --- a/src/test/java/org/orekit/orbits/FieldKeplerianOrbitTest.java +++ b/src/test/java/org/orekit/orbits/FieldKeplerianOrbitTest.java @@ -28,6 +28,7 @@ import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.linear.FieldMatrixPreservingVisitor; import org.hipparchus.linear.MatrixUtils; +import org.hipparchus.util.Binary64; import org.hipparchus.util.Binary64Field; import org.hipparchus.util.FastMath; import org.hipparchus.util.MathArrays; @@ -70,186 +71,186 @@ public void setUp() { } @Test - public void testKepToKep() { + void testKepToKep() { doTestKeplerianToKeplerian(Binary64Field.getInstance()); } @Test - public void testKepToCart() { + void testKepToCart() { doTestKeplerianToCartesian(Binary64Field.getInstance()); } @Test - public void testKepToEquin() { + void testKepToEquin() { doTestKeplerianToEquinoctial(Binary64Field.getInstance()); } @Test - public void testAnomaly() { + void testAnomaly() { doTestAnomaly(Binary64Field.getInstance()); } @Test - public void testPositionVelocityNorms() { + void testPositionVelocityNorms() { doTestPositionVelocityNorms(Binary64Field.getInstance()); } @Test - public void testGeometry() { + void testGeometry() { doTestGeometry(Binary64Field.getInstance()); } @Test - public void testSymmetry() { + void testSymmetry() { doTestSymmetry(Binary64Field.getInstance()); } @Test - public void testNonInertialFrame() { + void testNonInertialFrame() { Assertions.assertThrows(IllegalArgumentException.class, () -> { doTestNonInertialFrame(Binary64Field.getInstance()); }); } @Test - public void testPeriod() { + void testPeriod() { doTestPeriod(Binary64Field.getInstance()); } @Test - public void testHyperbola1() { + void testHyperbola1() { doTestHyperbola1(Binary64Field.getInstance()); } @Test - public void testHyperbola2() { + void testHyperbola2() { doTestHyperbola2(Binary64Field.getInstance()); } @Test - public void testToOrbitWithoutDerivatives() { + void testToOrbitWithoutDerivatives() { doTestToOrbitWithoutDerivatives(Binary64Field.getInstance()); } @Test - public void testToOrbitWithDerivatives() { + void testToOrbitWithDerivatives() { doTestToOrbitWithDerivatives(Binary64Field.getInstance()); } @Test - public void testDerivativesConversionSymmetry() { + void testDerivativesConversionSymmetry() { doTestDerivativesConversionSymmetry(Binary64Field.getInstance()); } @Test - public void testDerivativesConversionSymmetryHyperbolic() { + void testDerivativesConversionSymmetryHyperbolic() { doTestDerivativesConversionSymmetryHyperbolic(Binary64Field.getInstance()); } @Test - public void testToString() { + void testToString() { doTestToString(Binary64Field.getInstance()); } @Test - public void testInconsistentHyperbola() { + void testInconsistentHyperbola() { doTestInconsistentHyperbola(Binary64Field.getInstance()); } @Test - public void testVeryLargeEccentricity() { + void testVeryLargeEccentricity() { doTestVeryLargeEccentricity(Binary64Field.getInstance()); } @Test - public void testKeplerEquation() { + void testKeplerEquation() { doTestKeplerEquation(Binary64Field.getInstance()); } @Test - public void testNumericalIssue() { + void testNumericalIssue() { doTestNumericalIssue25(Binary64Field.getInstance()); } @Test - public void testPerfectlyEquatorial() { + void testPerfectlyEquatorial() { doTestPerfectlyEquatorial(Binary64Field.getInstance()); } @Test - public void testJacobianReferenceEllipse() { + void testJacobianReferenceEllipse() { doTestJacobianReferenceEllipse(Binary64Field.getInstance()); } @Test - public void testJacobianFinitedDiff() { + void testJacobianFinitedDiff() { doTestJacobianFinitedifferencesEllipse(Binary64Field.getInstance()); } @Test - public void testJacobianReferenceHyperbola() { + void testJacobianReferenceHyperbola() { doTestJacobianReferenceHyperbola(Binary64Field.getInstance()); } @Test - public void testJacobianFinitDiffHyperbola() { + void testJacobianFinitDiffHyperbola() { doTestJacobianFinitedifferencesHyperbola(Binary64Field.getInstance()); } @Test - public void testKeplerianDerivatives() { + void testKeplerianDerivatives() { doTestKeplerianDerivatives(Binary64Field.getInstance()); } @Test - public void testNonKeplerianEllipticDerivatives() { + void testNonKeplerianEllipticDerivatives() { doTestNonKeplerianEllipticDerivatives(Binary64Field.getInstance()); } @Test - public void testNonKeplerianHyperbolicDerivatives() { + void testNonKeplerianHyperbolicDerivatives() { doTestNonKeplerianHyperbolicDerivatives(Binary64Field.getInstance()); } @Test - public void testPositionAngleDerivatives() { + void testPositionAngleDerivatives() { doTestPositionAngleDerivatives(Binary64Field.getInstance()); } @Test - public void testPositionAngleHyperbolicDerivatives() { + void testPositionAngleHyperbolicDerivatives() { doTestPositionAngleHyperbolicDerivatives(Binary64Field.getInstance()); } @Test - public void testEquatorialRetrograde() { + void testEquatorialRetrograde() { doTestEquatorialRetrograde(Binary64Field.getInstance()); } @Test - public void testOutOfRangeV() { + void testOutOfRangeV() { Assertions.assertThrows(IllegalArgumentException.class, () -> { doTestOutOfRangeV(Binary64Field.getInstance()); }); } @Test - public void testPerfectlyEquatorialConversion() { + void testPerfectlyEquatorialConversion() { doTestPerfectlyEquatorialConversion(Binary64Field.getInstance()); } @Test - public void testCopyNonKeplerianAcceleration() { + void testCopyNonKeplerianAcceleration() { doTestCopyNonKeplerianAcceleration(Binary64Field.getInstance()); } @Test - public void testIssue674() { + void testIssue674() { doTestIssue674(Binary64Field.getInstance()); } @Test - public void testNormalize() { + void testNormalize() { doTestNormalize(Binary64Field.getInstance()); } @@ -343,6 +344,105 @@ private > void compareFieldOrbitToOrbit(final } } + @Test + void testCoverageCachedPositionAngleTypeWithRates() { + // GIVEN + final double semiMajorAxis = 1e4; + final double eccentricity = 0.; + final double expectedAnomaly = 0.; + final double expectedAnomalyDot = 0.; + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final Binary64Field field = Binary64Field.getInstance(); + final Binary64 zero = field.getZero(); + // WHEN & THEN + for (final PositionAngleType inputPositionAngleType: PositionAngleType.values()) { + for (final PositionAngleType cachedPositionAngleType: PositionAngleType.values()) { + final FieldKeplerianOrbit fieldOrbit = new FieldKeplerianOrbit<>( + zero.newInstance(semiMajorAxis), zero.newInstance(eccentricity), zero, zero, zero, + zero.newInstance(expectedAnomaly), zero, zero, zero, zero, zero, + zero.newInstance(expectedAnomalyDot), inputPositionAngleType, cachedPositionAngleType, + FramesFactory.getGCRF(), new FieldAbsoluteDate<>(field, date), zero.newInstance(mu)); + Assertions.assertEquals(cachedPositionAngleType, fieldOrbit.getCachedPositionAngleType()); + Assertions.assertEquals(expectedAnomaly, fieldOrbit.getTrueAnomaly().getReal()); + Assertions.assertEquals(expectedAnomaly, fieldOrbit.getEccentricAnomaly().getReal()); + Assertions.assertEquals(expectedAnomaly, fieldOrbit.getMeanAnomaly().getReal()); + Assertions.assertEquals(expectedAnomalyDot, fieldOrbit.getTrueAnomalyDot().getReal()); + Assertions.assertEquals(expectedAnomalyDot, fieldOrbit.getEccentricAnomalyDot().getReal()); + Assertions.assertEquals(expectedAnomalyDot, fieldOrbit.getMeanAnomalyDot().getReal()); + } + } + } + + @Test + void testGetAnomalyVersusDouble() { + // GIVEN + final double semiMajorAxis = 1e6; + final double eccentricity = 1e-2; + final double expectedAnomaly = 3.; + // WHE & THEN + compareAnomalies(semiMajorAxis, eccentricity, expectedAnomaly); + } + + @Test + void testGetAnomalyVersusDoubleHyperbolic() { + // GIVEN + final double semiMajorAxis = -1e6; + final double eccentricity = 1.5; + final double expectedAnomaly = 1.; + // WHEN & THEN + compareAnomalies(semiMajorAxis, eccentricity, expectedAnomaly); + } + + private void compareAnomalies(final double semiMajorAxis, final double eccentricity, final double anomaly) { + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final Binary64Field field = Binary64Field.getInstance(); + final Binary64 zero = field.getZero(); + final double tolerance = 1e-10; + for (final PositionAngleType inputPositionAngleType: PositionAngleType.values()) { + for (final PositionAngleType cachedPositionAngleType: PositionAngleType.values()) { + final FieldKeplerianOrbit fieldOrbit = new FieldKeplerianOrbit<>( + zero.newInstance(semiMajorAxis), zero.newInstance(eccentricity), zero, zero, zero, + zero.newInstance(anomaly), inputPositionAngleType, cachedPositionAngleType, + FramesFactory.getGCRF(), new FieldAbsoluteDate<>(field, date), zero.newInstance(mu)); + final KeplerianOrbit keplerianOrbit = fieldOrbit.toOrbit(); + Assertions.assertEquals(keplerianOrbit.getTrueAnomaly(), fieldOrbit.getTrueAnomaly().getReal(), + tolerance); + Assertions.assertEquals(keplerianOrbit.getEccentricAnomaly(), fieldOrbit.getEccentricAnomaly().getReal(), + tolerance); + Assertions.assertEquals(keplerianOrbit.getMeanAnomaly(), fieldOrbit.getMeanAnomaly().getReal(), + tolerance); + } + } + } + + @Test + void testGetAnomalyDotVersusDoubleHyperbolic() { + final double semiMajorAxis = -1e6; + final double eccentricity = 1.5; + final double anomaly = 1.; + final double anomalyDot = 0.1; + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final Binary64Field field = Binary64Field.getInstance(); + final Binary64 zero = field.getZero(); + final double tolerance = 1e-10; + for (final PositionAngleType inputPositionAngleType: PositionAngleType.values()) { + for (final PositionAngleType cachedPositionAngleType: PositionAngleType.values()) { + final FieldKeplerianOrbit fieldOrbit = new FieldKeplerianOrbit<>( + zero.newInstance(semiMajorAxis), zero.newInstance(eccentricity), zero, zero, zero, + zero.newInstance(anomaly), zero, zero, zero, zero, zero, zero.newInstance(anomalyDot), + inputPositionAngleType, cachedPositionAngleType, + FramesFactory.getGCRF(), new FieldAbsoluteDate<>(field, date), zero.newInstance(mu)); + final KeplerianOrbit keplerianOrbit = fieldOrbit.toOrbit(); + Assertions.assertEquals(keplerianOrbit.getTrueAnomaly(), fieldOrbit.getTrueAnomaly().getReal(), + tolerance); + Assertions.assertEquals(keplerianOrbit.getEccentricAnomaly(), fieldOrbit.getEccentricAnomaly().getReal(), + tolerance); + Assertions.assertEquals(keplerianOrbit.getMeanAnomaly(), fieldOrbit.getMeanAnomaly().getReal(), + tolerance); + } + } + } + private > void doTestKeplerianToKeplerian(final Field field) { FieldAbsoluteDate date = new FieldAbsoluteDate<>(field); @@ -887,9 +987,10 @@ private > void doTestToOrbitWithDerivatives(Fi FieldVector3D velocity = new FieldVector3D<>(zero.add(-500.0), zero.add(8000.0), zero.add(1000.0)); T r2 = position.getNormSq(); T r = r2.sqrt(); - FieldPVCoordinates pvCoordinates = new FieldPVCoordinates<>(position, velocity, - new FieldVector3D<>(r.multiply(r2).reciprocal().multiply(-mu), - position)); + final FieldVector3D keplerianAcceleration = new FieldVector3D<>(r.multiply(r2).reciprocal().multiply(zero.add(mu).negate()), + position); + final FieldVector3D nonKeplerianAcceleration = keplerianAcceleration.scalarMultiply(1.1); + final FieldPVCoordinates pvCoordinates = new FieldPVCoordinates<>(position, velocity, nonKeplerianAcceleration); FieldKeplerianOrbit fieldOrbit = new FieldKeplerianOrbit<>(pvCoordinates, FramesFactory.getEME2000(), date, field.getZero().add(mu)); KeplerianOrbit orbit = fieldOrbit.toOrbit(); Assertions.assertTrue(orbit.hasDerivatives()); @@ -900,13 +1001,6 @@ private > void doTestToOrbitWithDerivatives(Fi MatcherAssert.assertThat(orbit.getRightAscensionOfAscendingNode(), relativelyCloseTo(fieldOrbit.getRightAscensionOfAscendingNode().getReal(), 0)); MatcherAssert.assertThat(orbit.getTrueAnomaly(), relativelyCloseTo(fieldOrbit.getTrueAnomaly().getReal(), 0)); MatcherAssert.assertThat(orbit.getADot(), relativelyCloseTo(fieldOrbit.getADot().getReal(), 0)); - MatcherAssert.assertThat(orbit.getEquinoctialExDot(), relativelyCloseTo(fieldOrbit.getEquinoctialExDot().getReal(), 0)); - MatcherAssert.assertThat(orbit.getEquinoctialEyDot(), relativelyCloseTo(fieldOrbit.getEquinoctialEyDot().getReal(), 0)); - MatcherAssert.assertThat(orbit.getHxDot(), relativelyCloseTo(fieldOrbit.getHxDot().getReal(), 0)); - MatcherAssert.assertThat(orbit.getHyDot(), relativelyCloseTo(fieldOrbit.getHyDot().getReal(), 0)); - MatcherAssert.assertThat(orbit.getLvDot(), relativelyCloseTo(fieldOrbit.getLvDot().getReal(), 0)); - MatcherAssert.assertThat(orbit.getLEDot(), relativelyCloseTo(fieldOrbit.getLEDot().getReal(), 0)); - MatcherAssert.assertThat(orbit.getLMDot(), relativelyCloseTo(fieldOrbit.getLMDot().getReal(), 0)); MatcherAssert.assertThat(orbit.getEDot(), relativelyCloseTo(fieldOrbit.getEDot().getReal(), 0)); MatcherAssert.assertThat(orbit.getIDot(), relativelyCloseTo(fieldOrbit.getIDot().getReal(), 0)); MatcherAssert.assertThat(orbit.getPerigeeArgumentDot(), relativelyCloseTo(fieldOrbit.getPerigeeArgumentDot().getReal(), 0)); diff --git a/src/test/java/org/orekit/orbits/FieldOrbitTest.java b/src/test/java/org/orekit/orbits/FieldOrbitTest.java index e2ba831b5a..37f2d1556b 100644 --- a/src/test/java/org/orekit/orbits/FieldOrbitTest.java +++ b/src/test/java/org/orekit/orbits/FieldOrbitTest.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2024 Romain Serra +/* 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. @@ -16,20 +16,71 @@ */ package org.orekit.orbits; +import org.hipparchus.CalculusFieldElement; +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.util.Binary64; import org.hipparchus.util.MathUtils; 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.FieldAbsoluteDate; -import org.orekit.time.FieldTimeStamped; +import org.orekit.utils.Constants; +import org.orekit.utils.FieldPVCoordinates; +import org.orekit.utils.PVCoordinates; import org.orekit.utils.TimeStampedFieldPVCoordinates; class FieldOrbitTest { + @Test + void testGetPosition() { + // GIVEN + final TestFieldOrbit testFieldOrbit = new TestFieldOrbit(1.); + final FieldAbsoluteDate date = testFieldOrbit.getDate().shiftedBy(0.); + final Frame frame = testFieldOrbit.getFrame(); + // WHEN + final FieldVector3D actualPosition = testFieldOrbit.getPosition(date, frame); + // THEN + final FieldVector3D expectedPosition = testFieldOrbit.getPVCoordinates(date, frame).getPosition(); + Assertions.assertEquals(expectedPosition, actualPosition); + } + + @Test + void testHasNonKeplerianAccelerationDerivative() { + // GIVEN + final GradientField field = GradientField.getField(0); + final Gradient mu = field.getZero().newInstance(Constants.EGM96_EARTH_MU); + final FieldPVCoordinates fieldPVCoordinates = createFieldTPVWithKeplerianAcceleration(mu); + // WHEN + final boolean actualResult = FieldOrbit.hasNonKeplerianAcceleration(fieldPVCoordinates, mu); + // THEN + Assertions.assertFalse(actualResult); + } + + @Test + void testIssue1344() { + // GIVEN + final Binary64 mu = Binary64.ZERO.newInstance(Constants.EGM96_EARTH_MU); + final FieldPVCoordinates fieldPVCoordinates = createFieldTPVWithKeplerianAcceleration(mu); + // WHEN + final boolean actualResult = FieldOrbit.hasNonKeplerianAcceleration(fieldPVCoordinates, mu); + // THEN + Assertions.assertFalse(actualResult); + } + + private static > FieldPVCoordinates createFieldTPVWithKeplerianAcceleration(final T mu) { + final Vector3D position = new Vector3D(1e6, 0, 0); + final Vector3D keplerianAcceleration = new Vector3D(-mu.getReal() / position.getNormSq() / position.getNorm(), + position); + return new FieldPVCoordinates<>(mu.getField(), new PVCoordinates(position, Vector3D.ZERO, keplerianAcceleration)); + } + @Test void testKeplerianMeanMotionAndPeriod() { // GIVEN @@ -64,20 +115,13 @@ private void templateTestIsElliptical(final double aIn) { Assertions.assertEquals(expectedValue, actualValue); } - private static Frame mockInertialFrame() { - final Frame frame = Mockito.mock(Frame.class); - Mockito.when(frame.isPseudoInertial()).thenReturn(true); - return frame; - } - - @SuppressWarnings("unchecked") private static class TestFieldOrbit extends FieldOrbit { final Complex a; protected TestFieldOrbit(final double aIn) throws IllegalArgumentException { - super(mockInertialFrame(), Mockito.mock(FieldAbsoluteDate.class), Complex.ONE); + super(FramesFactory.getGCRF(), FieldAbsoluteDate.getArbitraryEpoch(ComplexField.getInstance()), Complex.ONE); a = new Complex(aIn, 0.); } @@ -198,22 +242,24 @@ public boolean hasDerivatives() { @Override protected FieldVector3D initPosition() { - return null; + return new FieldVector3D<>(getField(), new Vector3D(a.getReal(), 0., 0.)); } @Override protected TimeStampedFieldPVCoordinates initPVCoordinates() { - return null; + final FieldPVCoordinates fieldPVCoordinates = new FieldPVCoordinates<>(initPosition(), + FieldVector3D.getZero(getField())); + return new TimeStampedFieldPVCoordinates<>(getDate(), fieldPVCoordinates); } @Override public FieldOrbit shiftedBy(Complex dt) { - return null; + return shiftedBy(dt.getReal()); } @Override public FieldOrbit shiftedBy(double dt) { - return null; + return new TestFieldOrbit(a.getReal()); } @Override @@ -236,10 +282,6 @@ public void addKeplerContribution(PositionAngleType type, Complex gm, Complex[] } - @Override - public FieldVector3D getPosition(FieldAbsoluteDate date, Frame frame) { - return super.getPosition(date, frame); - } } -} \ No newline at end of file +} diff --git a/src/test/java/org/orekit/orbits/KeplerianMotionCartesianUtilityTest.java b/src/test/java/org/orekit/orbits/KeplerianMotionCartesianUtilityTest.java new file mode 100644 index 0000000000..a3d15656ee --- /dev/null +++ b/src/test/java/org/orekit/orbits/KeplerianMotionCartesianUtilityTest.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.orbits; + +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.frames.FramesFactory; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.FieldPVCoordinates; +import org.orekit.utils.PVCoordinates; + +class KeplerianMotionCartesianUtilityTest { + + private static final double TOLERANCE_DISTANCE = 1e-14; + private static final double TOLERANCE_SPEED = 1e-15; + + @Test + void testPredictPositionVelocityElliptic() { + // GIVEN + final double mu = 1.; + final Vector3D position = new Vector3D(10., 1., 0.); + final Vector3D velocity = new Vector3D(0., 0.1, 0.2); + // WHEN & THEN + for (double dt = -10.; dt <= 10.; dt += 0.1) { + final PVCoordinates actualPV = KeplerianMotionCartesianUtility.predictPositionVelocity(dt, position, + velocity, mu); + final CartesianOrbit orbit = new CartesianOrbit(new PVCoordinates(position, velocity), FramesFactory.getGCRF(), + AbsoluteDate.ARBITRARY_EPOCH, mu); + final EquinoctialOrbit equinoctialOrbit = new EquinoctialOrbit(orbit); + final PVCoordinates expectedPV = equinoctialOrbit.shiftedBy(dt).getPVCoordinates(); + comparePV(expectedPV, actualPV); + } + } + + @Test + void testPredictPositionVelocityHyperbolic() { + // GIVEN + final double mu = 1.; + final Vector3D position = new Vector3D(10., 1., 0.); + final Vector3D velocity = new Vector3D(0., 0.1, 1.); + // WHEN & THEN + for (double dt = -10.; dt <= 10.; dt += 0.1) { + final PVCoordinates actualPV = KeplerianMotionCartesianUtility.predictPositionVelocity(dt, position, + velocity, mu); + final CartesianOrbit orbit = new CartesianOrbit(new PVCoordinates(position, velocity), FramesFactory.getGCRF(), + AbsoluteDate.ARBITRARY_EPOCH, mu); + final KeplerianOrbit keplerianOrbit = new KeplerianOrbit(orbit); + final PVCoordinates expectedPV = keplerianOrbit.shiftedBy(dt).getPVCoordinates(); + comparePV(expectedPV, actualPV); + } + } + + private void comparePV(final PVCoordinates pvCoordinates, final PVCoordinates otherPVCoordinates) { + final double expectedValue = 0.; + final PVCoordinates relativePV = new PVCoordinates(pvCoordinates, otherPVCoordinates); + Assertions.assertEquals(expectedValue, relativePV.getPosition().getNorm(), TOLERANCE_DISTANCE); + Assertions.assertEquals(expectedValue, relativePV.getVelocity().getNorm(), TOLERANCE_SPEED); + } + + @Test + void testPredictPositionVelocityEllipticField() { + // GIVEN + final ComplexField field = ComplexField.getInstance(); + final double mu = 1.; + final Vector3D position = new Vector3D(6., 0., 1.); + final Vector3D velocity = new Vector3D(0.01, 0.1, 0.); + final Complex fieldMu = new Complex(mu); + final FieldVector3D fieldPosition = new FieldVector3D<>(field, position); + final FieldVector3D fieldVelocity = new FieldVector3D<>(field, velocity); + // WHEN & THEN + for (double dt = -10.; dt <= 10.; dt += 0.1) { + final FieldPVCoordinates actualPV = KeplerianMotionCartesianUtility.predictPositionVelocity(new Complex(dt), + fieldPosition, fieldVelocity, fieldMu); + final PVCoordinates expectedPV = KeplerianMotionCartesianUtility.predictPositionVelocity(dt, position, + velocity, mu); + comparePV(expectedPV, actualPV.toPVCoordinates()); + } + } + + @Test + void testPredictPositionVelocityHyperbolicField() { + // GIVEN + final ComplexField field = ComplexField.getInstance(); + final double mu = 1.; + final Vector3D position = new Vector3D(6., 0., 2.); + final Vector3D velocity = new Vector3D(0.01, 1, 0.); + final Complex fieldMu = new Complex(mu); + final FieldVector3D fieldPosition = new FieldVector3D<>(field, position); + final FieldVector3D fieldVelocity = new FieldVector3D<>(field, velocity); + // WHEN & THEN + for (double dt = -10.; dt <= 10.; dt += 0.1) { + final FieldPVCoordinates actualPV = KeplerianMotionCartesianUtility.predictPositionVelocity(new Complex(dt), + fieldPosition, fieldVelocity, fieldMu); + final PVCoordinates expectedPV = KeplerianMotionCartesianUtility.predictPositionVelocity(dt, position, + velocity, mu); + comparePV(expectedPV, actualPV.toPVCoordinates()); + } + } + +} diff --git a/src/test/java/org/orekit/orbits/KeplerianOrbitTest.java b/src/test/java/org/orekit/orbits/KeplerianOrbitTest.java index 98ad05fc85..f9b473337a 100644 --- a/src/test/java/org/orekit/orbits/KeplerianOrbitTest.java +++ b/src/test/java/org/orekit/orbits/KeplerianOrbitTest.java @@ -52,7 +52,7 @@ import static org.orekit.OrekitMatchers.relativelyCloseTo; -public class KeplerianOrbitTest { +class KeplerianOrbitTest { // Computation date private AbsoluteDate date; @@ -61,7 +61,7 @@ public class KeplerianOrbitTest { private double mu; @Test - public void testKeplerianToKeplerian() { + void testKeplerianToKeplerian() { // elliptic orbit KeplerianOrbit kep = @@ -120,7 +120,7 @@ public void testKeplerianToKeplerian() { } @Test - public void testKeplerianToCartesian() { + void testKeplerianToCartesian() { KeplerianOrbit kep = new KeplerianOrbit(24464560.0, 0.7311, 0.122138, 3.10686, 1.00681, @@ -139,7 +139,7 @@ public void testKeplerianToCartesian() { } @Test - public void testKeplerianToEquinoctial() { + void testKeplerianToEquinoctial() { KeplerianOrbit kep = new KeplerianOrbit(24464560.0, 0.7311, 0.122138, 3.10686, 1.00681, @@ -155,7 +155,7 @@ public void testKeplerianToEquinoctial() { } @Test - public void testAnomaly() { + void testAnomaly() { Vector3D position = new Vector3D(7.0e6, 1.0e6, 4.0e6); Vector3D velocity = new Vector3D(-500.0, 8000.0, 1000.0); @@ -237,7 +237,7 @@ public void testAnomaly() { } @Test - public void testPositionVelocityNorms() { + void testPositionVelocityNorms() { double mu = 3.9860047e14; // elliptic and non equatorial orbit @@ -293,7 +293,7 @@ public void testPositionVelocityNorms() { } @Test - public void testGeometry() { + void testGeometry() { double mu = 3.9860047e14; // elliptic and non equatorial orbit @@ -397,7 +397,7 @@ public void testGeometry() { } @Test - public void testSymmetry() { + void testSymmetry() { // elliptic and non equatorial orbit Vector3D position = new Vector3D(-4947831., -3765382., -3708221.); @@ -428,7 +428,7 @@ public void testSymmetry() { } @Test - public void testNonInertialFrame() throws IllegalArgumentException { + void testNonInertialFrame() throws IllegalArgumentException { Assertions.assertThrows(IllegalArgumentException.class, () -> { Vector3D position = new Vector3D(-4947831., -3765382., -3708221.); Vector3D velocity = new Vector3D(-2079., 5291., -7842.); @@ -440,7 +440,7 @@ public void testNonInertialFrame() throws IllegalArgumentException { } @Test - public void testPeriod() { + void testPeriod() { KeplerianOrbit orbit = new KeplerianOrbit(7654321.0, 0.1, 0.2, 0, 0, 0, PositionAngleType.TRUE, FramesFactory.getEME2000(), AbsoluteDate.J2000_EPOCH, @@ -450,7 +450,7 @@ public void testPeriod() { } @Test - public void testHyperbola1() { + void testHyperbola1() { KeplerianOrbit orbit = new KeplerianOrbit(-10000000.0, 2.5, 0.3, 0, 0, 0.0, PositionAngleType.TRUE, FramesFactory.getEME2000(), AbsoluteDate.J2000_EPOCH, @@ -472,7 +472,7 @@ public void testHyperbola1() { } @Test - public void testHyperbola2() { + void testHyperbola2() { KeplerianOrbit orbit = new KeplerianOrbit(-10000000.0, 1.2, 0.3, 0, 0, -1.75, PositionAngleType.MEAN, FramesFactory.getEME2000(), AbsoluteDate.J2000_EPOCH, @@ -497,7 +497,7 @@ public void testHyperbola2() { } @Test - public void testInconsistentHyperbola() { + void testInconsistentHyperbola() { try { new KeplerianOrbit(+10000000.0, 2.5, 0.3, 0, 0, 0.0, PositionAngleType.TRUE, @@ -512,7 +512,7 @@ public void testInconsistentHyperbola() { } @Test - public void testVeryLargeEccentricity() { + void testVeryLargeEccentricity() { final Frame eme2000 = FramesFactory.getEME2000(); final double meanAnomaly = 1.; @@ -543,7 +543,7 @@ public void testVeryLargeEccentricity() { } @Test - public void testKeplerEquation() { + void testKeplerEquation() { for (double M = -6 * FastMath.PI; M < 6 * FastMath.PI; M += 0.01) { KeplerianOrbit pElliptic = @@ -568,7 +568,7 @@ public void testKeplerEquation() { } @Test - public void testOutOfRangeV() { + void testOutOfRangeV() { Assertions.assertThrows(IllegalArgumentException.class, () -> { new KeplerianOrbit(-7000434.460140012, 1.1999785407363386, 1.3962787004479158, 1.3962320168955138, 0.3490728321331678, -2.55593407037698, @@ -579,7 +579,7 @@ public void testOutOfRangeV() { } @Test - public void testNumericalIssue25() { + void testNumericalIssue25() { Vector3D position = new Vector3D(3782116.14107698, 416663.11924914, 5875541.62103057); Vector3D velocity = new Vector3D(-6349.7848910501, 288.4061811651, 4066.9366759691); KeplerianOrbit orbit = new KeplerianOrbit(new PVCoordinates(position, velocity), @@ -591,7 +591,7 @@ public void testNumericalIssue25() { } @Test - public void testPerfectlyEquatorial() { + void testPerfectlyEquatorial() { Vector3D position = new Vector3D(6957904.3624652653594, 766529.11411558074507, 0); Vector3D velocity = new Vector3D(-7538.2817012412102845, 342.38751001881413381, 0.); KeplerianOrbit orbit = new KeplerianOrbit(new PVCoordinates(position, velocity), @@ -604,7 +604,7 @@ public void testPerfectlyEquatorial() { } @Test - public void testJacobianReferenceEllipse() { + void testJacobianReferenceEllipse() { AbsoluteDate dateTca = new AbsoluteDate(2000, 04, 01, 0, 0, 0.000, TimeScalesFactory.getUTC()); double mu = 3.986004415e+14; @@ -685,7 +685,7 @@ public void testJacobianReferenceEllipse() { } @Test - public void testJacobianFinitedifferencesEllipse() { + void testJacobianFinitedifferencesEllipse() { AbsoluteDate dateTca = new AbsoluteDate(2000, 04, 01, 0, 0, 0.000, TimeScalesFactory.getUTC()); double mu = 3.986004415e+14; @@ -730,7 +730,7 @@ public double end() { } @Test - public void testJacobianReferenceHyperbola() { + void testJacobianReferenceHyperbola() { AbsoluteDate dateTca = new AbsoluteDate(2000, 04, 01, 0, 0, 0.000, TimeScalesFactory.getUTC()); double mu = 3.986004415e+14; @@ -815,7 +815,7 @@ public void testJacobianReferenceHyperbola() { } @Test - public void testJacobianFinitedifferencesHyperbola() { + void testJacobianFinitedifferencesHyperbola() { AbsoluteDate dateTca = new AbsoluteDate(2000, 04, 01, 0, 0, 0.000, TimeScalesFactory.getUTC()); double mu = 3.986004415e+14; @@ -950,7 +950,7 @@ private void fillColumn(PositionAngleType type, int i, KeplerianOrbit orbit, dou } @Test - public void testPerfectlyEquatorialConversion() { + void testPerfectlyEquatorialConversion() { KeplerianOrbit initial = new KeplerianOrbit(13378000.0, 0.05, 0.0, 0.0, FastMath.PI, 0.0, PositionAngleType.MEAN, FramesFactory.getEME2000(), date, @@ -964,7 +964,7 @@ public void testPerfectlyEquatorialConversion() { } @Test - public void testKeplerianDerivatives() { + void testKeplerianDerivatives() { final KeplerianOrbit orbit = new KeplerianOrbit(new PVCoordinates(new Vector3D(-4947831., -3765382., -3708221.), new Vector3D(-2079., 5291., -7842.)), @@ -1046,7 +1046,7 @@ public double value(double dt) { } @Test - public void testNonKeplerianEllipticDerivatives() { + void testNonKeplerianEllipticDerivatives() { final AbsoluteDate date = new AbsoluteDate("2003-05-01T00:00:20.000", TimeScalesFactory.getUTC()); final Vector3D position = new Vector3D(6896874.444705, 1956581.072644, -147476.245054); final Vector3D velocity = new Vector3D(166.816407662, -1106.783301861, -7372.745712770); @@ -1114,7 +1114,7 @@ public void testNonKeplerianEllipticDerivatives() { } @Test - public void testNonKeplerianHyperbolicDerivatives() { + void testNonKeplerianHyperbolicDerivatives() { final AbsoluteDate date = new AbsoluteDate("2003-05-01T00:00:20.000", TimeScalesFactory.getUTC()); final Vector3D position = new Vector3D(224267911.905821, 290251613.109399, 45534292.777492); final Vector3D velocity = new Vector3D(-1494.068165293, 1124.771027677, 526.915286134); @@ -1194,7 +1194,7 @@ public double value(double dt) { } @Test - public void testPositionAngleDerivatives() { + void testPositionAngleDerivatives() { final AbsoluteDate date = new AbsoluteDate("2003-05-01T00:00:20.000", TimeScalesFactory.getUTC()); final Vector3D position = new Vector3D(6896874.444705, 1956581.072644, -147476.245054); final Vector3D velocity = new Vector3D(166.816407662, -1106.783301861, -7372.745712770); @@ -1237,7 +1237,7 @@ public void testPositionAngleDerivatives() { } @Test - public void testPositionAngleHyperbolicDerivatives() { + void testPositionAngleHyperbolicDerivatives() { final AbsoluteDate date = new AbsoluteDate("2003-05-01T00:00:20.000", TimeScalesFactory.getUTC()); final Vector3D position = new Vector3D(224267911.905821, 290251613.109399, 45534292.777492); final Vector3D velocity = new Vector3D(-1494.068165293, 1124.771027677, 526.915286134); @@ -1280,7 +1280,7 @@ public void testPositionAngleHyperbolicDerivatives() { } @Test - public void testSerialization() + void testSerialization() throws IOException, ClassNotFoundException { Vector3D position = new Vector3D(-29536113.0, 30329259.0, -100125.0); Vector3D velocity = new Vector3D(-2194.0, -2141.0, -8.0); @@ -1292,8 +1292,7 @@ public void testSerialization() ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(orbit); - Assertions.assertTrue(bos.size() > 280); - Assertions.assertTrue(bos.size() < 330); + Assertions.assertEquals(bos.size(), 461); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); @@ -1317,7 +1316,7 @@ public void testSerialization() } @Test - public void testSerializationWithDerivatives() + void testSerializationWithDerivatives() throws IOException, ClassNotFoundException { Vector3D position = new Vector3D(-29536113.0, 30329259.0, -100125.0); Vector3D velocity = new Vector3D(-2194.0, -2141.0, -8.0); @@ -1333,8 +1332,7 @@ public void testSerializationWithDerivatives() ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(orbit); - Assertions.assertTrue(bos.size() > 330); - Assertions.assertTrue(bos.size() < 380); + Assertions.assertEquals(bos.size(), 509); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); @@ -1358,7 +1356,7 @@ public void testSerializationWithDerivatives() } @Test - public void testEquatorialRetrograde() { + void testEquatorialRetrograde() { Vector3D position = new Vector3D(10000000.0, 0.0, 0.0); Vector3D velocity = new Vector3D(0.0, -6500.0, 1.0e-10); double r2 = position.getNormSq(); @@ -1380,7 +1378,7 @@ public void testEquatorialRetrograde() { } @Test - public void testDerivativesConversionSymmetry() { + void testDerivativesConversionSymmetry() { final AbsoluteDate date = new AbsoluteDate("2003-05-01T00:01:20.000", TimeScalesFactory.getUTC()); Vector3D position = new Vector3D(6893443.400234382, 1886406.1073757345, -589265.1150359757); Vector3D velocity = new Vector3D(-281.1261461082365, -1231.6165642450928, -7348.756363469432); @@ -1410,7 +1408,7 @@ public void testDerivativesConversionSymmetry() { } @Test - public void testDerivativesConversionSymmetryHyperbolic() { + void testDerivativesConversionSymmetryHyperbolic() { final AbsoluteDate date = new AbsoluteDate("2003-05-01T00:00:20.000", TimeScalesFactory.getUTC()); final Vector3D position = new Vector3D(224267911.905821, 290251613.109399, 45534292.777492); final Vector3D velocity = new Vector3D(-1494.068165293, 1124.771027677, 526.915286134); @@ -1439,7 +1437,7 @@ public void testDerivativesConversionSymmetryHyperbolic() { } @Test - public void testToString() { + void testToString() { Vector3D position = new Vector3D(-29536113.0, 30329259.0, -100125.0); Vector3D velocity = new Vector3D(-2194.0, -2141.0, -8.0); PVCoordinates pvCoordinates = new PVCoordinates(position, velocity); @@ -1473,7 +1471,7 @@ void testRemoveRates() { } @Test - public void testCopyNonKeplerianAcceleration() { + void testCopyNonKeplerianAcceleration() { final Frame eme2000 = FramesFactory.getEME2000(); @@ -1504,7 +1502,7 @@ public void testCopyNonKeplerianAcceleration() { } @Test - public void testIssue674() { + void testIssue674() { try { new KeplerianOrbit(24464560.0, -0.7311, 0.122138, 3.10686, 1.00681, 0.048363, PositionAngleType.MEAN, @@ -1520,7 +1518,7 @@ public void testIssue674() { } @Test - public void testNormalize() { + void testNormalize() { KeplerianOrbit withoutDerivatives = new KeplerianOrbit(42166712.0, 0.005, 1.6, -0.3, 1.25, 0.4, PositionAngleType.MEAN, @@ -1571,7 +1569,7 @@ public void testNormalize() { } @Test - public void testKeplerianToPvToKeplerian() { + void testKeplerianToPvToKeplerian() { // setup Frame eci = FramesFactory.getGCRF(); AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; @@ -1592,6 +1590,79 @@ public void testKeplerianToPvToKeplerian() { } + @Test + void testCoverageCachedPositionAngleTypeElliptic() { + testCoverageCachedPositionAngleType(1e4, 0.5); + } + + @Test + void testCoverageCachedPositionAngleTypeHyperbolic() { + testCoverageCachedPositionAngleType(-1e4, 2); + } + + private void testCoverageCachedPositionAngleType(final double a, final double e) { + // GIVEN + final double expectedAnomaly = 0.; + // WHEN & THEN + for (final PositionAngleType inputPositionAngleType: PositionAngleType.values()) { + for (final PositionAngleType cachedPositionAngleType: PositionAngleType.values()) { + final KeplerianOrbit keplerianOrbit = new KeplerianOrbit(a, e, 0., 0., 0., + expectedAnomaly, inputPositionAngleType, cachedPositionAngleType, FramesFactory.getGCRF(), date, mu); + Assertions.assertEquals(expectedAnomaly, keplerianOrbit.getTrueAnomaly()); + Assertions.assertEquals(expectedAnomaly, keplerianOrbit.getEccentricAnomaly()); + Assertions.assertEquals(expectedAnomaly, keplerianOrbit.getMeanAnomaly()); + Assertions.assertTrue(Double.isNaN(keplerianOrbit.getTrueAnomalyDot())); + Assertions.assertTrue(Double.isNaN(keplerianOrbit.getEccentricAnomalyDot())); + Assertions.assertTrue(Double.isNaN(keplerianOrbit.getMeanAnomalyDot())); + } + } + } + + @Test + void testCoverageCachedPositionAngleTypeWithRates() { + // GIVEN + final double semiMajorAxis = 1e4; + final double eccentricity = 0.; + final double expectedAnomaly = 0.; + final double expectedAnomalyDot = 0.; + // WHEN & THEN + for (final PositionAngleType inputPositionAngleType: PositionAngleType.values()) { + for (final PositionAngleType cachedPositionAngleType: PositionAngleType.values()) { + final KeplerianOrbit keplerianOrbit = new KeplerianOrbit(semiMajorAxis, eccentricity, 0., 0., 0., + expectedAnomaly, 0., 0., 0., 0., 0., expectedAnomalyDot, + inputPositionAngleType, cachedPositionAngleType, FramesFactory.getGCRF(), date, mu); + Assertions.assertEquals(expectedAnomaly, keplerianOrbit.getTrueAnomaly()); + Assertions.assertEquals(expectedAnomaly, keplerianOrbit.getEccentricAnomaly()); + Assertions.assertEquals(expectedAnomaly, keplerianOrbit.getMeanAnomaly()); + Assertions.assertEquals(expectedAnomalyDot, keplerianOrbit.getTrueAnomalyDot()); + Assertions.assertEquals(expectedAnomalyDot, keplerianOrbit.getEccentricAnomalyDot()); + Assertions.assertEquals(expectedAnomalyDot, keplerianOrbit.getMeanAnomalyDot()); + } + } + } + + @Test + void testCoverageCachedPositionAngleTypeWithRatesHyperbolic() { + // GIVEN + final double semiMajorAxis = -1e4; + final double eccentricity = 2.; + final double expectedAnomaly = 0.; + final double expectedAnomalyDot = 0.; + // WHEN & THEN + for (final PositionAngleType inputPositionAngleType: PositionAngleType.values()) { + for (final PositionAngleType cachedPositionAngleType: PositionAngleType.values()) { + final KeplerianOrbit keplerianOrbit = new KeplerianOrbit(semiMajorAxis, eccentricity, 0., 0., 0., + expectedAnomaly, 0., 0., 0., 0., 0., expectedAnomalyDot, + inputPositionAngleType, cachedPositionAngleType, FramesFactory.getGCRF(), date, mu); + Assertions.assertEquals(expectedAnomaly, keplerianOrbit.getTrueAnomaly()); + Assertions.assertEquals(expectedAnomaly, keplerianOrbit.getEccentricAnomaly()); + Assertions.assertEquals(expectedAnomaly, keplerianOrbit.getMeanAnomaly()); + Assertions.assertEquals(expectedAnomalyDot, keplerianOrbit.getTrueAnomalyDot()); + Assertions.assertEquals(expectedAnomalyDot, keplerianOrbit.getEccentricAnomalyDot()); + Assertions.assertEquals(expectedAnomalyDot, keplerianOrbit.getMeanAnomalyDot()); + } + } + } @BeforeEach public void setUp() { diff --git a/src/test/java/org/orekit/orbits/OrbitBlenderTest.java b/src/test/java/org/orekit/orbits/OrbitBlenderTest.java index fe6c5a02c8..c2c637638d 100644 --- a/src/test/java/org/orekit/orbits/OrbitBlenderTest.java +++ b/src/test/java/org/orekit/orbits/OrbitBlenderTest.java @@ -225,6 +225,31 @@ void testKeplerianQuadraticBlendingOnSergeiCase() { final AbstractAnalyticalPropagator propagator = new KeplerianPropagator(sergeiOrbit); final OrbitBlender orbitBlender = new OrbitBlender(quadratic, propagator, sergeiFrame); + final TimeInterpolator stateInterpolator = + new SpacecraftStateInterpolator(2, 1.0e-3, sergeiFrame, orbitBlender, null, null, null, null); + + // When & Then + doTestInterpolation(stateInterpolator, DEFAULT_SERGEI_PROPAGATION_TIME, DEFAUTL_SERGEI_TABULATED_TIMESTEP, + 0.05185755740700528, + 0.08169252246167892, + 0.05262772652596856, + 0.08349987869494085, + 0.10151652739088853, + 0.14827634525717634, + 1e-12, false); + } + + @SuppressWarnings("deprecation") + @Test + @DisplayName("non regression test on Keplerian quadratic orbit blending on full force model test case from : " + + "TANYGIN, Sergei. Efficient covariance interpolation using blending of approximate covariance propagations. " + + "The Journal of the Astronautical Sciences, 2014, vol. 61, no 1, p. 107-132.") + void testKeplerianQuadraticBlendingOnSergeiCaseDeprecated() { + // Given + final SmoothStepFactory.SmoothStepFunction quadratic = SmoothStepFactory.getQuadratic(); + final AbstractAnalyticalPropagator propagator = new KeplerianPropagator(sergeiOrbit); + final OrbitBlender orbitBlender = new OrbitBlender(quadratic, propagator, sergeiFrame); + final TimeInterpolator stateInterpolator = new SpacecraftStateInterpolator(sergeiFrame, orbitBlender, null, null, null, null); @@ -236,7 +261,7 @@ void testKeplerianQuadraticBlendingOnSergeiCase() { 0.08349987869494085, 0.10151652739088853, 0.14827634525717634, - 1e-17, false); + 1e-12, false); } @Test @@ -257,6 +282,39 @@ void testBrouwerLyddaneQuadraticBlendingOnSergeiCase() { Constants.EIGEN5C_EARTH_C50, 0); final OrbitBlender orbitBlender = new OrbitBlender(quadratic, propagator, sergeiFrame); + final TimeInterpolator stateInterpolator = + new SpacecraftStateInterpolator(2, 1.0e-3, sergeiFrame, orbitBlender, null, null, null, null); + + // When & Then + doTestInterpolation(stateInterpolator, DEFAULT_SERGEI_PROPAGATION_TIME, DEFAUTL_SERGEI_TABULATED_TIMESTEP, + 0.05106377388516059, + 0.03671310671380644, + 0.05451875412478483, + 0.03654640625064279, + 0.09412869297314610, + 0.06642996306635666, + 1e-13, false); + } + + @SuppressWarnings("deprecation") + @Test + @DisplayName("non regression test on Brouwer-Lyddane quadratic orbit blending on full force model test case from : " + + "TANYGIN, Sergei. Efficient covariance interpolation using blending of approximate covariance propagations. " + + "The Journal of the Astronautical Sciences, 2014, vol. 61, no 1, p. 107-132.") + void testBrouwerLyddaneQuadraticBlendingOnSergeiCaseDeprecated() { + // Given + final SpacecraftState sergeiState = StateCovarianceKeplerianHermiteInterpolatorTest.generateSergeiReferenceState(); + final Frame sergeiFrame = sergeiState.getFrame(); + + final SmoothStepFactory.SmoothStepFunction quadratic = SmoothStepFactory.getQuadratic(); + final AbstractAnalyticalPropagator propagator = + new BrouwerLyddanePropagator(sergeiState.getOrbit(), sergeiState.getMass(), + Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS, + Constants.EIGEN5C_EARTH_MU, Constants.EIGEN5C_EARTH_C20, + Constants.EIGEN5C_EARTH_C30, Constants.EIGEN5C_EARTH_C40, + Constants.EIGEN5C_EARTH_C50, 0); + final OrbitBlender orbitBlender = new OrbitBlender(quadratic, propagator, sergeiFrame); + final TimeInterpolator stateInterpolator = new SpacecraftStateInterpolator(sergeiFrame, orbitBlender, null, null, null, null); @@ -268,7 +326,7 @@ void testBrouwerLyddaneQuadraticBlendingOnSergeiCase() { 0.03654640625064279, 0.09412869297314610, 0.06642996306635666, - 1e-17, false); + 1e-13, false); } @Test @@ -286,6 +344,37 @@ void testEcksteinHechlerQuadraticBlendingOnSergeiCase() { Constants.EIGEN5C_EARTH_C50, Constants.EIGEN5C_EARTH_C60); final OrbitBlender orbitBlender = new OrbitBlender(quadratic, propagator, sergeiFrame); + final TimeInterpolator stateInterpolator = + new SpacecraftStateInterpolator(2, 1.0e-3, sergeiFrame, orbitBlender, null, null, null, null); + + // When & Then + doTestInterpolation(stateInterpolator, DEFAULT_SERGEI_PROPAGATION_TIME, DEFAUTL_SERGEI_TABULATED_TIMESTEP, + 0.00854503692536256, + 0.01192593187393609, + 0.00895077301610845, + 0.01299681289409554, + 0.01600030634518512, + 0.01743228687362160, + 1e-17, false); + + } + + @SuppressWarnings("deprecation") + @Test + @DisplayName("non regression test on Eckstein-Hechler quadratic orbit blending on full force model test case from : " + + "TANYGIN, Sergei. Efficient covariance interpolation using blending of approximate covariance propagations. " + + "The Journal of the Astronautical Sciences, 2014, vol. 61, no 1, p. 107-132.") + void testEcksteinHechlerQuadraticBlendingOnSergeiCaseDeprecated() { + // Given + final SmoothStepFactory.SmoothStepFunction quadratic = SmoothStepFactory.getQuadratic(); + final AbstractAnalyticalPropagator propagator = + new EcksteinHechlerPropagator(sergeiState.getOrbit(), sergeiState.getMass(), + Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS, + Constants.EIGEN5C_EARTH_MU, Constants.EIGEN5C_EARTH_C20, + Constants.EIGEN5C_EARTH_C30, Constants.EIGEN5C_EARTH_C40, + Constants.EIGEN5C_EARTH_C50, Constants.EIGEN5C_EARTH_C60); + final OrbitBlender orbitBlender = new OrbitBlender(quadratic, propagator, sergeiFrame); + final TimeInterpolator stateInterpolator = new SpacecraftStateInterpolator(sergeiFrame, orbitBlender, null, null, null, null); diff --git a/src/test/java/org/orekit/orbits/OrbitTest.java b/src/test/java/org/orekit/orbits/OrbitTest.java index 70282f9116..f3e089534d 100644 --- a/src/test/java/org/orekit/orbits/OrbitTest.java +++ b/src/test/java/org/orekit/orbits/OrbitTest.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2024 Romain Serra +/* 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. @@ -20,14 +20,27 @@ import org.hipparchus.util.MathUtils; 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.utils.PVCoordinates; import org.orekit.utils.TimeStampedPVCoordinates; class OrbitTest { + @Test + void testGetPosition() { + // GIVEN + final double aIn = 1.; + final TestOrbit testOrbit = new TestOrbit(aIn); + final AbsoluteDate date = testOrbit.getDate().shiftedBy(0.); + // WHEN + final Vector3D actualPosition = testOrbit.getPosition(date, testOrbit.getFrame()); + // THEN + final Vector3D expectedPosition = testOrbit.getPVCoordinates(date, testOrbit.getFrame()).getPosition(); + Assertions.assertEquals(expectedPosition, actualPosition); + } + @Test void testKeplerianMeanMotionAndPeriod() { // GIVEN @@ -62,19 +75,13 @@ private void templateTestIsElliptical(final double aIn) { Assertions.assertEquals(expectedValue, actualValue); } - private static Frame mockInertialFrame() { - final Frame frame = Mockito.mock(Frame.class); - Mockito.when(frame.isPseudoInertial()).thenReturn(true); - return frame; - } - private static class TestOrbit extends Orbit { private static final long serialVersionUID = 5921352039485286603L; final double a; protected TestOrbit(final double aIn) throws IllegalArgumentException { - super(mockInertialFrame(), Mockito.mock(AbsoluteDate.class), 1.); + super(FramesFactory.getGCRF(), AbsoluteDate.ARBITRARY_EPOCH, 1.); this.a = aIn; } @@ -185,17 +192,17 @@ public double getIDot() { @Override protected Vector3D initPosition() { - return null; + return new Vector3D(a, 0., 0.); } @Override protected TimeStampedPVCoordinates initPVCoordinates() { - return null; + return new TimeStampedPVCoordinates(getDate(), new PVCoordinates(initPosition(), Vector3D.ZERO)); } @Override public Orbit shiftedBy(double dt) { - return null; + return new TestOrbit(a); } @Override diff --git a/src/test/java/org/orekit/orbits/OrbitTypeTest.java b/src/test/java/org/orekit/orbits/OrbitTypeTest.java index d5a399478e..dcee238741 100644 --- a/src/test/java/org/orekit/orbits/OrbitTypeTest.java +++ b/src/test/java/org/orekit/orbits/OrbitTypeTest.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2024 Romain Serra +/* 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. diff --git a/src/test/java/org/orekit/orbits/WalkerConstellationTest.java b/src/test/java/org/orekit/orbits/WalkerConstellationTest.java new file mode 100644 index 0000000000..2bc77f23fe --- /dev/null +++ b/src/test/java/org/orekit/orbits/WalkerConstellationTest.java @@ -0,0 +1,162 @@ +/* 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.orbits; + +import org.hipparchus.util.FastMath; +import org.hipparchus.util.MathUtils; +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.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.frames.FramesFactory; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.Constants; + +import java.util.List; + +public class WalkerConstellationTest { + + @Test + public void testContainer() { + final WalkerConstellation w = new WalkerConstellation(60, 15, 1); + Assertions.assertEquals(60, w.getT()); + Assertions.assertEquals(15, w.getP()); + Assertions.assertEquals( 1, w.getF()); + } + @Test + public void testInconsistentPlanes() { + try { + new WalkerConstellation(60, 14, 1); + Assertions.fail("an exception should have been thrown"); + } catch (OrekitException oe) { + Assertions.assertEquals(OrekitMessages.WALKER_INCONSISTENT_PLANES, oe.getSpecifier()); + Assertions.assertEquals(14, ((Integer) oe.getParts()[0]).intValue()); + Assertions.assertEquals(60, ((Integer) oe.getParts()[1]).intValue()); + } + } + + @Test + public void testRegularPhasing() { + final CircularOrbit reference = new CircularOrbit(29600000.0, 1.0e-3, 1.2e-4, + FastMath.toRadians(56.0), FastMath.toRadians(0), + FastMath.toRadians(0), PositionAngleType.MEAN, + FramesFactory.getEME2000(), + AbsoluteDate.J2000_EPOCH, + Constants.EIGEN5C_EARTH_MU); + final WalkerConstellation w = new WalkerConstellation(24, 3, 1); + final List>> all = w.buildRegularSlots(reference); + Assertions.assertEquals(3, all.size()); + for (int i = 0; i < 3; ++i) { + final List> l = all.get(i); + Assertions.assertEquals(8, l.size()); + for (int j = 0; j < 8; ++j) { + final WalkerConstellationSlot s = l.get(j); + Assertions.assertSame(w, s.getConstellation()); + Assertions.assertEquals(i, s.getPlane()); + Assertions.assertEquals(j, s.getSatellite(), 1.0e-15); + final CircularOrbit c = s.getOrbit(); + Assertions.assertEquals(reference.getA(), c.getA(), 4.0e-8); + Assertions.assertEquals(reference.getCircularEx(), c.getCircularEx(), 1.0e-15); + Assertions.assertEquals(reference.getCircularEy(), c.getCircularEy(), 1.0e-15); + Assertions.assertEquals(FastMath.toDegrees(reference.getI()), + FastMath.toDegrees(c.getI()), + 1.0e-14); + Assertions.assertEquals(i * 120.0, + FastMath.toDegrees(MathUtils.normalizeAngle(c.getRightAscensionOfAscendingNode(), + FastMath.PI)), + 9.0e-14); + Assertions.assertEquals(i * 15.0 + j * 45.0, + FastMath.toDegrees( + MathUtils.normalizeAngle(c.getAlphaM(), FastMath.PI)), + 6.0e-14); + } + } + } + + @Test + public void testInOrbitSpares() { + final CircularOrbit reference = new CircularOrbit(29600000.0, 1.0e-3, 1.2e-4, + FastMath.toRadians(56.0), FastMath.toRadians(0), + FastMath.toRadians(0), PositionAngleType.MEAN, + FramesFactory.getEME2000(), + AbsoluteDate.J2000_EPOCH, + Constants.EIGEN5C_EARTH_MU); + final WalkerConstellation w = new WalkerConstellation(24, 3, 1); + final List>> regular = w.buildRegularSlots(reference); + Assertions.assertEquals(3, regular.size()); + final WalkerConstellationSlot slot00 = regular.get(0).get(0); + + final WalkerConstellationSlot spare0 = w.buildSlot(slot00, 0, 4.5); + Assertions.assertEquals(0, spare0.getPlane()); + Assertions.assertEquals(4.5, spare0.getSatellite(), 1.0e-15); + Assertions.assertEquals(reference.getA(), spare0.getOrbit().getA(), 4.0e-8); + Assertions.assertEquals(reference.getCircularEx(), spare0.getOrbit().getCircularEx(), 1.0e-15); + Assertions.assertEquals(reference.getCircularEy(), spare0.getOrbit().getCircularEy(), 1.0e-15); + Assertions.assertEquals(FastMath.toDegrees(reference.getI()), + FastMath.toDegrees(spare0.getOrbit().getI()), + 1.0e-14); + Assertions.assertEquals(0.0, + FastMath.toDegrees(MathUtils.normalizeAngle(spare0.getOrbit().getRightAscensionOfAscendingNode(), + FastMath.PI)), + 9.0e-14); + Assertions.assertEquals(202.5, + FastMath.toDegrees(MathUtils.normalizeAngle(spare0.getOrbit().getAlphaM(), FastMath.PI)), + 6.0e-14); + + final WalkerConstellationSlot spare1 = w.buildSlot(slot00, 1, 3.5); + Assertions.assertEquals(1, spare1.getPlane()); + Assertions.assertEquals(3.5, spare1.getSatellite(), 1.0e-15); + Assertions.assertEquals(reference.getA(), spare1.getOrbit().getA(), 4.0e-8); + Assertions.assertEquals(reference.getCircularEx(), spare1.getOrbit().getCircularEx(), 1.0e-15); + Assertions.assertEquals(reference.getCircularEy(), spare1.getOrbit().getCircularEy(), 1.0e-15); + Assertions.assertEquals(FastMath.toDegrees(reference.getI()), + FastMath.toDegrees(spare1.getOrbit().getI()), + 1.0e-14); + Assertions.assertEquals(120.0, + FastMath.toDegrees(MathUtils.normalizeAngle(spare1.getOrbit().getRightAscensionOfAscendingNode(), + FastMath.PI)), + 9.0e-14); + Assertions.assertEquals(172.5, + FastMath.toDegrees(MathUtils.normalizeAngle(spare1.getOrbit().getAlphaM(), FastMath.PI)), + 6.0e-14); + + final WalkerConstellationSlot spare2 = w.buildSlot(slot00, 2, 1.5); + Assertions.assertEquals(2, spare2.getPlane()); + Assertions.assertEquals(1.5, spare2.getSatellite(), 1.0e-15); + Assertions.assertEquals(reference.getA(), spare2.getOrbit().getA(), 4.0e-8); + Assertions.assertEquals(reference.getCircularEx(), spare2.getOrbit().getCircularEx(), 1.0e-15); + Assertions.assertEquals(reference.getCircularEy(), spare2.getOrbit().getCircularEy(), 1.0e-15); + Assertions.assertEquals(FastMath.toDegrees(reference.getI()), + FastMath.toDegrees(spare2.getOrbit().getI()), + 1.0e-14); + Assertions.assertEquals(240.0, + FastMath.toDegrees(MathUtils.normalizeAngle(spare2.getOrbit().getRightAscensionOfAscendingNode(), + FastMath.PI)), + 9.0e-14); + Assertions.assertEquals(97.5, + FastMath.toDegrees(MathUtils.normalizeAngle(spare2.getOrbit().getAlphaM(), FastMath.PI)), + 6.0e-14); + + } + + @BeforeEach + public void setUp() { + Utils.setDataRoot("regular-data"); + } +} diff --git a/src/test/java/org/orekit/propagation/AdditionalStateProviderTest.java b/src/test/java/org/orekit/propagation/AdditionalStateProviderTest.java index 39a30ec780..4a5e7cd372 100644 --- a/src/test/java/org/orekit/propagation/AdditionalStateProviderTest.java +++ b/src/test/java/org/orekit/propagation/AdditionalStateProviderTest.java @@ -16,13 +16,16 @@ */ package org.orekit.propagation; +import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.ode.nonstiff.AdaptiveStepsizeIntegrator; import org.hipparchus.ode.nonstiff.DormandPrince853Integrator; +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.Attitude; import org.orekit.forces.gravity.potential.GravityFieldFactory; import org.orekit.forces.gravity.potential.SHMFormatReader; import org.orekit.frames.FramesFactory; @@ -37,7 +40,6 @@ public class AdditionalStateProviderTest { - private double mu; private AbsoluteDate initDate; private SpacecraftState initialState; private AdaptiveStepsizeIntegrator integrator; @@ -46,7 +48,7 @@ public class AdditionalStateProviderTest { public void setUp() { Utils.setDataRoot("regular-data:potential/shm-format"); GravityFieldFactory.addPotentialCoefficientsReader(new SHMFormatReader("^eigen_cg03c_coef$", false)); - mu = GravityFieldFactory.getUnnormalizedProvider(0, 0).getMu(); + final double mu = GravityFieldFactory.getUnnormalizedProvider(0, 0).getMu(); final Vector3D position = new Vector3D(7.0e6, 1.0e6, 4.0e6); final Vector3D velocity = new Vector3D(-500.0, 8000.0, 1000.0); initDate = AbsoluteDate.J2000_EPOCH; @@ -58,6 +60,31 @@ public void setUp() { integrator.setInitialStepSize(60); } + @Test + public void testModifyMainState() { + + // Create propagator + final NumericalPropagator propagator = new NumericalPropagator(integrator); + propagator.setInitialState(initialState); + + // Create state modifier + final MainStateModifier modifier = new MainStateModifier(); + + // Add the provider to the propagator + propagator.addAdditionalStateProvider(modifier); + + // Propagate + final double dt = 600.0; + final SpacecraftState propagated = propagator.propagate(initDate.shiftedBy(dt)); + + // Verify + Assertions.assertEquals(2 * SpacecraftState.DEFAULT_MASS, propagated.getMass(), 1.0e-12); + Assertions.assertEquals(FastMath.PI, + propagated.getAttitude().getRotation().getAngle(), + 1.0e-15); + + } + @Test public void testIssue900Numerical() { @@ -133,9 +160,23 @@ public void testIssue900BrouwerLyddane() { } + private static class MainStateModifier extends AbstractStateModifier { + /** {@inheritDoc} */ + @Override + public SpacecraftState change(final SpacecraftState state) { + return new SpacecraftState(state.getOrbit(), + new Attitude(state.getDate(), + state.getFrame(), + new Rotation(0, 0, 0, 1, false), + Vector3D.ZERO, + Vector3D.ZERO), + 2 * SpacecraftState.DEFAULT_MASS); + } + } + private static class TimeDifferenceProvider implements AdditionalStateProvider { - private String name; + private final String name; private boolean called; private double dt; diff --git a/src/test/java/org/orekit/propagation/FieldAdditionalStateProviderTest.java b/src/test/java/org/orekit/propagation/FieldAdditionalStateProviderTest.java index f400975067..622cac02a6 100644 --- a/src/test/java/org/orekit/propagation/FieldAdditionalStateProviderTest.java +++ b/src/test/java/org/orekit/propagation/FieldAdditionalStateProviderTest.java @@ -18,15 +18,18 @@ import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; +import org.hipparchus.geometry.euclidean.threed.FieldRotation; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; import org.hipparchus.ode.nonstiff.AdaptiveStepsizeFieldIntegrator; import org.hipparchus.ode.nonstiff.DormandPrince54FieldIntegrator; 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.orekit.Utils; +import org.orekit.attitudes.FieldAttitude; import org.orekit.forces.gravity.potential.GravityFieldFactory; import org.orekit.forces.gravity.potential.SHMFormatReader; import org.orekit.frames.FramesFactory; @@ -69,6 +72,37 @@ private > AdaptiveStepsizeFieldIntegrator c return integrator; } + @Test + public void testModifyMainState() { + doTestModifyMainState(Binary64Field.getInstance()); + } + + private > void doTestModifyMainState(final Field field) { + + // Create propagator + final FieldSpacecraftState state = createState(field); + final AdaptiveStepsizeFieldIntegrator integrator = createIntegrator(field, state); + final FieldNumericalPropagator propagator = new FieldNumericalPropagator<>(field, integrator); + propagator.setInitialState(state); + + // Create state modifier + final MainStateModifier modifier = new MainStateModifier<>(); + + // Add the provider to the propagator + propagator.addAdditionalStateProvider(modifier); + + // Propagate + final double dt = 600.0; + final FieldSpacecraftState propagated = propagator.propagate(state.getDate().shiftedBy(dt)); + + // Verify + Assertions.assertEquals(2 * SpacecraftState.DEFAULT_MASS, propagated.getMass().getReal(), 1.0e-12); + Assertions.assertEquals(FastMath.PI, + propagated.getAttitude().getRotation().getAngle().getReal(), + 1.0e-15); + + } + @Test public void testIssue900Numerical() { doTestIssue900Numerical(Binary64Field.getInstance()); @@ -136,6 +170,25 @@ public void testIssue900BrouwerLyddane() { doTestIssue900BrouwerLyddane(Binary64Field.getInstance()); } + private static class MainStateModifier> extends FieldAbstractStateModifier { + /** {@inheritDoc} */ + @Override + public FieldSpacecraftState change(final FieldSpacecraftState state) { + final Field field = state.getDate().getField(); + return new FieldSpacecraftState<>(state.getOrbit(), + new FieldAttitude<>(state.getDate(), + state.getFrame(), + new FieldRotation<>(field.getZero(), + field.getZero(), + field.getZero(), + field.getOne(), + false), + FieldVector3D.getZero(field), + FieldVector3D.getZero(field)), + field.getZero().newInstance(2 * SpacecraftState.DEFAULT_MASS)); + } + } + private > void doTestIssue900BrouwerLyddane(final Field field) { // Create propagator @@ -163,10 +216,10 @@ private > void doTestIssue900BrouwerLyddane(fi private static class TimeDifferenceProvider> implements FieldAdditionalStateProvider { - private String name; + private final String name; private boolean called; private T dt; - private Field field; + private final Field field; public TimeDifferenceProvider(final String name, final Field field) { this.name = name; diff --git a/src/test/java/org/orekit/propagation/FieldPropagatorTest.java b/src/test/java/org/orekit/propagation/FieldPropagatorTest.java new file mode 100644 index 0000000000..d944a1a368 --- /dev/null +++ b/src/test/java/org/orekit/propagation/FieldPropagatorTest.java @@ -0,0 +1,172 @@ +/* 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; + +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.attitudes.AttitudeProvider; +import org.orekit.frames.Frame; +import org.orekit.frames.FramesFactory; +import org.orekit.propagation.events.FieldEventDetector; +import org.orekit.propagation.sampling.FieldStepHandlerMultiplexer; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldPVCoordinates; +import org.orekit.utils.PVCoordinates; +import org.orekit.utils.TimeStampedFieldPVCoordinates; +import org.orekit.utils.TimeStampedPVCoordinates; + +import java.util.Collection; +import java.util.List; + +class FieldPropagatorTest { + + @Test + void testGetPosition() { + // GIVEN + final TestFieldPropagator testPropagator = new TestFieldPropagator(); + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final FieldAbsoluteDate fieldDate = new FieldAbsoluteDate<>(ComplexField.getInstance(), date); + final Frame frame = FramesFactory.getGCRF(); + // WHEN + final FieldVector3D actualPosition = testPropagator.getPosition(fieldDate, frame); + // THEN + final FieldPVCoordinates expectedState = testPropagator.propagate(fieldDate).getPVCoordinates(frame); + Assertions.assertEquals(expectedState.getPosition().toVector3D(), actualPosition.toVector3D()); + } + + @Test + void testGetPVCoordinates() { + // GIVEN + final TestFieldPropagator testPropagator = new TestFieldPropagator(); + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final FieldAbsoluteDate fieldDate = new FieldAbsoluteDate<>(ComplexField.getInstance(), date); + final Frame frame = FramesFactory.getGCRF(); + // WHEN + final FieldPVCoordinates actualState = testPropagator.getPVCoordinates(fieldDate, frame); + // THEN + final FieldPVCoordinates expectedState = testPropagator.propagate(fieldDate).getPVCoordinates(frame); + Assertions.assertEquals(expectedState.getPosition().toVector3D(), actualState.getPosition().toVector3D()); + Assertions.assertEquals(expectedState.getVelocity().toVector3D(), actualState.getVelocity().toVector3D()); + } + + @SuppressWarnings("unchecked") + private static FieldSpacecraftState mockFieldSpacecraftState(final FieldAbsoluteDate date) { + final FieldSpacecraftState mockedFieldSpacecraftState = Mockito.mock(FieldSpacecraftState.class); + Mockito.when(mockedFieldSpacecraftState.getDate()).thenReturn(date); + final ComplexField complexField = ComplexField.getInstance(); + final PVCoordinates pvCoordinates = new PVCoordinates(Vector3D.MINUS_I, Vector3D.PLUS_K); + final TimeStampedPVCoordinates tspvc = new TimeStampedPVCoordinates(date.toAbsoluteDate(), pvCoordinates); + final TimeStampedFieldPVCoordinates fieldPVCoordinates = new TimeStampedFieldPVCoordinates<>( + complexField, tspvc); + Mockito.when(mockedFieldSpacecraftState.getPVCoordinates()).thenReturn(fieldPVCoordinates); + Mockito.when(mockedFieldSpacecraftState.getPVCoordinates(Mockito.any(Frame.class))) + .thenReturn(fieldPVCoordinates); + Mockito.when(mockedFieldSpacecraftState.getPosition(Mockito.any(Frame.class))) + .thenReturn(fieldPVCoordinates.getPosition()); + return mockedFieldSpacecraftState; + } + + private static class TestFieldPropagator implements FieldPropagator { + + @Override + public FieldStepHandlerMultiplexer getMultiplexer() { + return null; + } + + @Override + public FieldEphemerisGenerator getEphemerisGenerator() { + return null; + } + + @Override + public FieldSpacecraftState getInitialState() { + return null; + } + + @Override + public void resetInitialState(FieldSpacecraftState state) { + // not used in test + } + + @Override + public void addAdditionalStateProvider(FieldAdditionalStateProvider additionalStateProvider) { + // not used in test + } + + @Override + public List> getAdditionalStateProviders() { + return null; + } + + @Override + public boolean isAdditionalStateManaged(String name) { + return false; + } + + @Override + public String[] getManagedAdditionalStates() { + return new String[0]; + } + + @Override + public > void addEventDetector(D detector) { + // not used in test + } + + @Override + public Collection> getEventsDetectors() { + return null; + } + + @Override + public void clearEventsDetectors() { + // not used in test + } + + @Override + public AttitudeProvider getAttitudeProvider() { + return null; + } + + @Override + public void setAttitudeProvider(AttitudeProvider attitudeProvider) { + // not used in test + } + + @Override + public Frame getFrame() { + return null; + } + + @Override + public FieldSpacecraftState propagate(FieldAbsoluteDate target) { + return mockFieldSpacecraftState(target); + } + + @Override + public FieldSpacecraftState propagate(FieldAbsoluteDate start, FieldAbsoluteDate target) { + return null; + } + } + +} \ No newline at end of file diff --git a/src/test/java/org/orekit/propagation/FieldSpacecraftStateInterpolatorTest.java b/src/test/java/org/orekit/propagation/FieldSpacecraftStateInterpolatorTest.java index 5e26c3dc41..9367b9ccaf 100644 --- a/src/test/java/org/orekit/propagation/FieldSpacecraftStateInterpolatorTest.java +++ b/src/test/java/org/orekit/propagation/FieldSpacecraftStateInterpolatorTest.java @@ -153,10 +153,10 @@ public void testOrbitInterpolation() new FieldSpacecraftStateInterpolator<>(2, inertialFrame); final FieldSpacecraftStateInterpolator interpolator2 = - new FieldSpacecraftStateInterpolator<>(3, inertialFrame); + new FieldSpacecraftStateInterpolator<>(3, SpacecraftStateInterpolator.DEFAULT_EXTRAPOLATION_THRESHOLD_SEC, inertialFrame); final FieldSpacecraftStateInterpolator interpolator3 = - new FieldSpacecraftStateInterpolator<>(4, inertialFrame); + new FieldSpacecraftStateInterpolator<>(4, SpacecraftStateInterpolator.DEFAULT_EXTRAPOLATION_THRESHOLD_SEC, inertialFrame, inertialFrame); // When & Then checkStandardInterpolationError(2, 106.46533, 0.40709287, 169847806.33e-9, 0.0, 450 * 450, 450 * 450, interpolator1); @@ -188,6 +188,43 @@ public void testErrorThrownWhenOneInterpolatorIsNotConsistentWithSampleSize() { Mockito.when(massInterpolator.getSubInterpolators()).thenReturn(Collections.singletonList(massInterpolator)); Mockito.when(massInterpolator.getNbInterpolationPoints()).thenReturn(2); + final FieldSpacecraftStateInterpolator stateInterpolator = + new FieldSpacecraftStateInterpolator<>(2, 1.0e-3, outputFrame, orbitInterpolator, absPVInterpolator, massInterpolator, + null, null); + + // WHEN & THEN + OrekitIllegalArgumentException thrown = Assertions.assertThrows(OrekitIllegalArgumentException.class, () -> + AbstractFieldTimeInterpolator.checkInterpolatorCompatibilityWithSampleSize(stateInterpolator, 2)); + + Assertions.assertEquals(OrekitMessages.NOT_ENOUGH_DATA, thrown.getSpecifier()); + Assertions.assertEquals(2, ((Integer) thrown.getParts()[0]).intValue()); + + } + + @SuppressWarnings("deprecation") + @Test + public void testErrorThrownWhenOneInterpolatorIsNotConsistentWithSampleSizeDeprecated() { + // GIVEN + final Frame outputFrame = Mockito.mock(Frame.class); + + @SuppressWarnings("unchecked") + final FieldTimeInterpolator, Binary64> orbitInterpolator = + Mockito.mock(FieldTimeInterpolator.class); + Mockito.when(orbitInterpolator.getSubInterpolators()).thenReturn(Collections.singletonList(orbitInterpolator)); + Mockito.when(orbitInterpolator.getNbInterpolationPoints()).thenReturn(2); + + @SuppressWarnings("unchecked") + final FieldTimeInterpolator, Binary64> absPVInterpolator = + Mockito.mock(FieldTimeInterpolator.class); + Mockito.when(absPVInterpolator.getSubInterpolators()).thenReturn(Collections.singletonList(absPVInterpolator)); + Mockito.when(absPVInterpolator.getNbInterpolationPoints()).thenReturn(4); + + @SuppressWarnings("unchecked") + final FieldTimeInterpolator, Binary64> massInterpolator = + Mockito.mock(FieldTimeInterpolator.class); + Mockito.when(massInterpolator.getSubInterpolators()).thenReturn(Collections.singletonList(massInterpolator)); + Mockito.when(massInterpolator.getNbInterpolationPoints()).thenReturn(2); + final FieldSpacecraftStateInterpolator stateInterpolator = new FieldSpacecraftStateInterpolator<>(outputFrame, orbitInterpolator, absPVInterpolator, massInterpolator, null, null); @@ -540,6 +577,59 @@ void testGetNbInterpolationsWithMultipleSubInterpolators() { Mockito.when(attitudeInterpolator.getSubInterpolators()).thenReturn(Collections.singletonList(attitudeInterpolator)); Mockito.when(additionalStateInterpolator.getSubInterpolators()).thenReturn(Collections.singletonList(additionalStateInterpolator)); + final FieldSpacecraftStateInterpolator stateInterpolator = + new FieldSpacecraftStateInterpolator<>(2, 1.0e-3, frame, orbitInterpolator, absPVAInterpolator, massInterpolator, + attitudeInterpolator, additionalStateInterpolator); + + // WHEN + final int returnedNbInterpolationPoints = stateInterpolator.getNbInterpolationPoints(); + + // THEN + Assertions.assertEquals(AdditionalStateNbInterpolationPoints, returnedNbInterpolationPoints); + } + + @SuppressWarnings("deprecation") + @Test + void testGetNbInterpolationsWithMultipleSubInterpolatorsDeprecated() { + // GIVEN + // Create mock interpolators + final Frame frame = Mockito.mock(Frame.class); + + @SuppressWarnings("unchecked") + final FieldTimeInterpolator, Binary64> orbitInterpolator = + Mockito.mock(FieldOrbitHermiteInterpolator.class); + @SuppressWarnings("unchecked") + final FieldTimeInterpolator, Binary64> absPVAInterpolator = + Mockito.mock(FieldAbsolutePVCoordinatesHermiteInterpolator.class); + @SuppressWarnings("unchecked") + final FieldTimeInterpolator, Binary64> massInterpolator = + Mockito.mock(TimeStampedFieldHermiteInterpolator.class); + @SuppressWarnings("unchecked") + final FieldTimeInterpolator, Binary64> attitudeInterpolator = + Mockito.mock(FieldAttitudeInterpolator.class); + @SuppressWarnings("unchecked") + final FieldTimeInterpolator, Binary64> additionalStateInterpolator = + Mockito.mock(TimeStampedFieldHermiteInterpolator.class); + + // Implement mocks behaviours + final int orbitNbInterpolationPoints = 2; + final int absPVANbInterpolationPoints = 3; + final int massNbInterpolationPoints = 4; + final int AttitudeNbInterpolationPoints = 5; + final int AdditionalStateNbInterpolationPoints = 6; + + Mockito.when(orbitInterpolator.getNbInterpolationPoints()).thenReturn(orbitNbInterpolationPoints); + Mockito.when(absPVAInterpolator.getNbInterpolationPoints()).thenReturn(absPVANbInterpolationPoints); + Mockito.when(massInterpolator.getNbInterpolationPoints()).thenReturn(massNbInterpolationPoints); + Mockito.when(attitudeInterpolator.getNbInterpolationPoints()).thenReturn(AttitudeNbInterpolationPoints); + Mockito.when(additionalStateInterpolator.getNbInterpolationPoints()).thenReturn(AdditionalStateNbInterpolationPoints); + + Mockito.when(orbitInterpolator.getSubInterpolators()).thenReturn(Collections.singletonList(orbitInterpolator)); + Mockito.when(absPVAInterpolator.getSubInterpolators()).thenReturn(Collections.singletonList(absPVAInterpolator)); + Mockito.when(massInterpolator.getSubInterpolators()).thenReturn(Collections.singletonList(massInterpolator)); + Mockito.when(attitudeInterpolator.getSubInterpolators()).thenReturn(Collections.singletonList(attitudeInterpolator)); + Mockito.when(additionalStateInterpolator.getSubInterpolators()).thenReturn(Collections.singletonList(additionalStateInterpolator)); + final FieldSpacecraftStateInterpolator stateInterpolator = new FieldSpacecraftStateInterpolator<>(frame, orbitInterpolator, absPVAInterpolator, massInterpolator, attitudeInterpolator, additionalStateInterpolator); @@ -599,6 +689,24 @@ void testErrorThrownWhenGivingNoInterpolatorForState() { final Frame inertialFrameMock = Mockito.mock(Frame.class); Mockito.when(inertialFrameMock.isPseudoInertial()).thenReturn(true); + // When & Then + Exception thrown = Assertions.assertThrows(OrekitIllegalArgumentException.class, + () -> new FieldSpacecraftStateInterpolator<>(2, 1.0e-3, inertialFrameMock, + null, null, null, null, + null)); + + Assertions.assertEquals("creating a spacecraft state interpolator requires at least one orbit interpolator or an " + + "absolute position-velocity-acceleration interpolator", thrown.getMessage()); + } + + @SuppressWarnings("deprecation") + @Test + @DisplayName("test error thrown when using no interpolator for state") + void testErrorThrownWhenGivingNoInterpolatorForStateDeprecated() { + // Given + final Frame inertialFrameMock = Mockito.mock(Frame.class); + Mockito.when(inertialFrameMock.isPseudoInertial()).thenReturn(true); + // When & Then Exception thrown = Assertions.assertThrows(OrekitIllegalArgumentException.class, () -> new FieldSpacecraftStateInterpolator<>(inertialFrameMock, @@ -621,6 +729,36 @@ void testErrorThrownWhenGivingEmptySample() { final List> states = new ArrayList<>(); + // Create interpolator + @SuppressWarnings("unchecked") + final FieldTimeInterpolator, Binary64> orbitInterpolatorMock = + Mockito.mock(FieldTimeInterpolator.class); + + final FieldTimeInterpolator, Binary64> interpolator = + new FieldSpacecraftStateInterpolator<>(2, 1.0e-3, inertialFrame, orbitInterpolatorMock, null, null, null, null); + + // When & Then + OrekitIllegalArgumentException thrown = Assertions.assertThrows(OrekitIllegalArgumentException.class, () -> + interpolator.interpolate(interpolationDate, states)); + + Assertions.assertEquals(OrekitMessages.NOT_ENOUGH_DATA, thrown.getSpecifier()); + Assertions.assertEquals(0, ((Integer) thrown.getParts()[0]).intValue()); + + } + + @SuppressWarnings("deprecation") + @Test + @DisplayName("test error thrown when giving empty sample") + void testErrorThrownWhenGivingEmptySampleDeprecated() { + // Given + + @SuppressWarnings("unchecked") + final FieldAbsoluteDate interpolationDate = Mockito.mock(FieldAbsoluteDate.class); + + final Frame inertialFrame = FramesFactory.getEME2000(); + + final List> states = new ArrayList<>(); + // Create interpolator @SuppressWarnings("unchecked") final FieldTimeInterpolator, Binary64> orbitInterpolatorMock = @@ -644,6 +782,49 @@ void testFieldSpacecraftStateInterpolatorCreation() { final Frame inertialFrameMock = Mockito.mock(Frame.class); Mockito.when(inertialFrameMock.isPseudoInertial()).thenReturn(true); + @SuppressWarnings("unchecked") + final FieldTimeInterpolator, Binary64> orbitInterpolatorMock = + Mockito.mock(FieldTimeInterpolator.class); + + @SuppressWarnings("unchecked") + final FieldTimeInterpolator, Binary64> absPVInterpolatorMock = + Mockito.mock(FieldTimeInterpolator.class); + + @SuppressWarnings("unchecked") + final FieldTimeInterpolator, Binary64> massInterpolatorMock = + Mockito.mock(FieldTimeInterpolator.class); + + @SuppressWarnings("unchecked") + final FieldTimeInterpolator, Binary64> attitudeInterpolatorMock = + Mockito.mock(FieldTimeInterpolator.class); + + @SuppressWarnings("unchecked") + final FieldTimeInterpolator, Binary64> additionalInterpolatorMock = + Mockito.mock(FieldTimeInterpolator.class); + + // When + final FieldSpacecraftStateInterpolator interpolator = + new FieldSpacecraftStateInterpolator<>(2, 1.0e-3, inertialFrameMock, orbitInterpolatorMock, absPVInterpolatorMock, + massInterpolatorMock, attitudeInterpolatorMock, + additionalInterpolatorMock); + + // Then + Assertions.assertEquals(inertialFrameMock, interpolator.getOutputFrame()); + Assertions.assertEquals(orbitInterpolatorMock, interpolator.getOrbitInterpolator().get()); + Assertions.assertEquals(absPVInterpolatorMock, interpolator.getAbsPVAInterpolator().get()); + Assertions.assertEquals(massInterpolatorMock, interpolator.getMassInterpolator().get()); + Assertions.assertEquals(attitudeInterpolatorMock, interpolator.getAttitudeInterpolator().get()); + Assertions.assertEquals(additionalInterpolatorMock, interpolator.getAdditionalStateInterpolator().get()); + + } + + @SuppressWarnings("deprecation") + @Test + void testFieldSpacecraftStateInterpolatorCreationDeprecated() { + // Given + final Frame inertialFrameMock = Mockito.mock(Frame.class); + Mockito.when(inertialFrameMock.isPseudoInertial()).thenReturn(true); + @SuppressWarnings("unchecked") final FieldTimeInterpolator, Binary64> orbitInterpolatorMock = Mockito.mock(FieldTimeInterpolator.class); diff --git a/src/test/java/org/orekit/propagation/FieldSpacecraftStateTest.java b/src/test/java/org/orekit/propagation/FieldSpacecraftStateTest.java index 7846a49856..68fa45e490 100644 --- a/src/test/java/org/orekit/propagation/FieldSpacecraftStateTest.java +++ b/src/test/java/org/orekit/propagation/FieldSpacecraftStateTest.java @@ -45,10 +45,15 @@ import org.orekit.errors.OrekitIllegalArgumentException; import org.orekit.errors.OrekitMessages; import org.orekit.frames.FieldStaticTransform; +import org.orekit.frames.FieldTransform; import org.orekit.frames.FramesFactory; -import org.orekit.frames.StaticTransform; import org.orekit.frames.Transform; -import org.orekit.orbits.*; +import org.orekit.orbits.FieldCartesianOrbit; +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.propagation.analytical.FieldEcksteinHechlerPropagator; import org.orekit.propagation.analytical.FieldKeplerianPropagator; import org.orekit.propagation.events.FieldDateDetector; @@ -60,102 +65,135 @@ import org.orekit.time.FieldAbsoluteDate; import org.orekit.time.TimeComponents; import org.orekit.time.TimeScalesFactory; -import org.orekit.utils.*; +import org.orekit.utils.Constants; +import org.orekit.utils.FieldAbsolutePVCoordinates; +import org.orekit.utils.FieldArrayDictionary; +import org.orekit.utils.FieldPVCoordinates; +import org.orekit.utils.IERSConventions; +import org.orekit.utils.PVCoordinates; +import org.orekit.utils.TimeStampedAngularCoordinates; +import org.orekit.utils.TimeStampedFieldAngularCoordinates; -public class FieldSpacecraftStateTest { +class FieldSpacecraftStateTest { @Test - public void testFieldVSReal() { + void testFieldVSReal() { doTestFieldVsReal(Binary64Field.getInstance()); } @Test - public void testShiftVsEcksteinHechlerError() { + void testShiftVsEcksteinHechlerError() { doTestShiftVsEcksteinHechlerError(Binary64Field.getInstance()); } @Test - public void testDatesConsistency() { + void testDatesConsistency() { Assertions.assertThrows(IllegalArgumentException.class, () -> { doTestDatesConsistency(Binary64Field.getInstance()); }); } @Test - public void testDateConsistencyClose() { + void testDateConsistencyClose() { doTestDateConsistencyClose(Binary64Field.getInstance()); } @Test - public void testFramesConsistency() { + void testFramesConsistency() { Assertions.assertThrows(IllegalArgumentException.class, () -> { doTestFramesConsistency(Binary64Field.getInstance()); }); } @Test - public void testTransform() { + void testTransform() { doTestTransform(Binary64Field.getInstance()); } @Test - public void testAdditionalStates() { + void testAdditionalStates() { doTestAdditionalStates(Binary64Field.getInstance()); } @Test - public void testAdditionalStatesDerivatives() { + void testAdditionalStatesDerivatives() { doTestAdditionalStatesDerivatives(Binary64Field.getInstance()); } @Test - public void testFieldVSRealAbsPV() { + void testFieldVSRealAbsPV() { doTestFieldVsRealAbsPV(Binary64Field.getInstance()); } @Test - public void testDateConsistencyCloseAbsPV() { + void testDateConsistencyCloseAbsPV() { doTestDateConsistencyCloseAbsPV(Binary64Field.getInstance()); } @Test - public void testFramesConsistencyAbsPV() { + void testFramesConsistencyAbsPV() { Assertions.assertThrows(IllegalArgumentException.class, () -> { doTestFramesConsistencyAbsPV(Binary64Field.getInstance()); }); } @Test - public void testAdditionalStatesAbsPV() { + void testAdditionalStatesAbsPV() { doTestAdditionalStatesAbsPV(Binary64Field.getInstance()); } @Test - public void testAdditionalStatesDerivativesAbsPV() { + void testAdditionalStatesDerivativesAbsPV() { doTestAdditionalStatesDerivativesAbsPV(Binary64Field.getInstance()); } @Test - public void testResetOnEventAnalytical() { + void testResetOnEventAnalytical() { doTestAdditionalTestResetOnEventAnalytical(Binary64Field.getInstance()); } @Test - public void testResetOnEventNumerical() { + void testResetOnEventNumerical() { doTestAdditionalTestResetOnEventNumerical(Binary64Field.getInstance()); } @Test - public void testShiftAdditionalDerivativesDouble() { + void testShiftAdditionalDerivativesDouble() { doTestShiftAdditionalDerivativesDouble(Binary64Field.getInstance()); } @Test - public void testShiftAdditionalDerivativesField() { + void testShiftAdditionalDerivativesField() { doTestShiftAdditionalDerivativesField(Binary64Field.getInstance()); } + @Test + void testToTransform() { + // GIVEN + final ComplexField field = ComplexField.getInstance(); + final FieldOrbit orbit = new FieldCartesianOrbit<>(field, rOrbit); + final TimeStampedAngularCoordinates angularCoordinates = new TimeStampedAngularCoordinates( + orbit.getDate().toAbsoluteDate(), Rotation.IDENTITY, Vector3D.ZERO, Vector3D.ZERO); + final FieldAttitude attitude = new FieldAttitude<>(orbit.getFrame(), + new TimeStampedFieldAngularCoordinates<>(field, angularCoordinates)); + final FieldSpacecraftState state = new FieldSpacecraftState<>(orbit, attitude); + // WHEN + final FieldTransform fieldTransform = state.toTransform(); + // THEN + final Transform expectedTransform = state.toSpacecraftState().toTransform(); + Assertions.assertEquals(expectedTransform.getDate(), fieldTransform.getDate()); + final double tolerance = 1e-10; + Assertions.assertEquals(expectedTransform.getTranslation().getX(), + fieldTransform.getTranslation().getX().getReal(), tolerance); + Assertions.assertEquals(expectedTransform.getTranslation().getY(), + fieldTransform.getTranslation().getY().getReal(), tolerance); + Assertions.assertEquals(expectedTransform.getTranslation().getZ(), + fieldTransform.getTranslation().getZ().getReal(), tolerance); + Assertions.assertEquals(0., Rotation.distance(expectedTransform.getRotation(), + fieldTransform.getRotation().toRotation())); + } + @Test void testToStaticTransform() { // GIVEN @@ -169,7 +207,7 @@ void testToStaticTransform() { // WHEN final FieldStaticTransform actualStaticTransform = state.toStaticTransform(); // THEN - final FieldStaticTransform expectedStaticTransform = state.toTransform().toStaticTransform(); + final FieldStaticTransform expectedStaticTransform = state.toTransform(); Assertions.assertEquals(expectedStaticTransform.getDate(), actualStaticTransform.getDate()); final double tolerance = 1e-10; Assertions.assertEquals(expectedStaticTransform.getTranslation().getX().getReal(), @@ -993,7 +1031,6 @@ private > void doTestAdditionalTestResetOnEven // Create date detector and handler FieldAbsoluteDate changeDate = date0.shiftedBy(3); - @SuppressWarnings("unchecked") FieldDateDetector dateDetector = new FieldDateDetector<>(field, changeDate). withHandler(new FieldEventHandler() { @@ -1045,7 +1082,6 @@ private > void doTestAdditionalTestResetOnEven // Create date detector and handler FieldAbsoluteDate changeDate = date0.shiftedBy(3); - @SuppressWarnings("unchecked") FieldDateDetector dateDetector = new FieldDateDetector<>(field, changeDate). withHandler(new FieldEventHandler() { diff --git a/src/test/java/org/orekit/propagation/PropagatorTest.java b/src/test/java/org/orekit/propagation/PropagatorTest.java new file mode 100644 index 0000000000..4bc432d34a --- /dev/null +++ b/src/test/java/org/orekit/propagation/PropagatorTest.java @@ -0,0 +1,159 @@ +/* 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; + +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.attitudes.AttitudeProvider; +import org.orekit.frames.Frame; +import org.orekit.frames.FramesFactory; +import org.orekit.propagation.events.EventDetector; +import org.orekit.propagation.sampling.StepHandlerMultiplexer; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.PVCoordinates; +import org.orekit.utils.TimeStampedPVCoordinates; + +import java.util.Collection; +import java.util.List; + +class PropagatorTest { + + @Test + void testGetPosition() { + // GIVEN + final TestPropagator testPropagator = new TestPropagator(); + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final Frame frame = FramesFactory.getGCRF(); + // WHEN + final Vector3D actualPosition = testPropagator.getPosition(date, frame); + // THEN + final PVCoordinates expectedState = testPropagator.propagate(date).getPVCoordinates(frame); + Assertions.assertEquals(expectedState.getPosition(), actualPosition); + } + + @Test + void testGetPVCoordinates() { + // GIVEN + final TestPropagator testPropagator = new TestPropagator(); + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final Frame frame = FramesFactory.getGCRF(); + // WHEN + final PVCoordinates actualState = testPropagator.getPVCoordinates(date, frame); + // THEN + final PVCoordinates expectedState = testPropagator.propagate(date).getPVCoordinates(frame); + Assertions.assertEquals(expectedState.getPosition(), actualState.getPosition()); + Assertions.assertEquals(expectedState.getVelocity(), actualState.getVelocity()); + } + + private static SpacecraftState mockSpacecraftState(final AbsoluteDate date) { + final SpacecraftState mockedSpacecraftState = Mockito.mock(SpacecraftState.class); + Mockito.when(mockedSpacecraftState.getDate()).thenReturn(date); + final PVCoordinates pvCoordinates = new PVCoordinates(Vector3D.MINUS_I, Vector3D.PLUS_K); + final TimeStampedPVCoordinates tspvc = new TimeStampedPVCoordinates(date, pvCoordinates); + Mockito.when(mockedSpacecraftState.getPVCoordinates()).thenReturn(tspvc); + Mockito.when(mockedSpacecraftState.getPVCoordinates(Mockito.any(Frame.class))).thenReturn(tspvc); + Mockito.when(mockedSpacecraftState.getPosition(Mockito.any(Frame.class))) + .thenReturn(pvCoordinates.getPosition()); + return mockedSpacecraftState; + } + + private static class TestPropagator implements Propagator { + + @Override + public StepHandlerMultiplexer getMultiplexer() { + return null; + } + + @Override + public EphemerisGenerator getEphemerisGenerator() { + return null; + } + + @Override + public SpacecraftState getInitialState() { + return null; + } + + @Override + public void resetInitialState(SpacecraftState state) { + // not used in test + } + + @Override + public void addAdditionalStateProvider(AdditionalStateProvider additionalStateProvider) { + // not used in test + } + + @Override + public List getAdditionalStateProviders() { + return null; + } + + @Override + public boolean isAdditionalStateManaged(String name) { + return false; + } + + @Override + public String[] getManagedAdditionalStates() { + return new String[0]; + } + + @Override + public void addEventDetector(T detector) { + // not used in test + } + + @Override + public Collection getEventsDetectors() { + return null; + } + + @Override + public void clearEventsDetectors() { + // not used in test + } + + @Override + public AttitudeProvider getAttitudeProvider() { + return null; + } + + @Override + public void setAttitudeProvider(AttitudeProvider attitudeProvider) { + // not used in test + } + + @Override + public Frame getFrame() { + return null; + } + + @Override + public SpacecraftState propagate(AbsoluteDate target) { + return mockSpacecraftState(target); + } + + @Override + public SpacecraftState propagate(AbsoluteDate start, AbsoluteDate target) { + return null; + } + } + +} \ No newline at end of file diff --git a/src/test/java/org/orekit/propagation/SpacecraftStateInterpolatorTest.java b/src/test/java/org/orekit/propagation/SpacecraftStateInterpolatorTest.java index 774ea1f334..eaee4c6b06 100644 --- a/src/test/java/org/orekit/propagation/SpacecraftStateInterpolatorTest.java +++ b/src/test/java/org/orekit/propagation/SpacecraftStateInterpolatorTest.java @@ -147,10 +147,10 @@ public void testOrbitInterpolation() new SpacecraftStateInterpolator(interpolationPoints1, intertialFrame, intertialFrame); final SpacecraftStateInterpolator interpolator2 = - new SpacecraftStateInterpolator(interpolationPoints2, intertialFrame, intertialFrame); + new SpacecraftStateInterpolator(interpolationPoints2, SpacecraftStateInterpolator.DEFAULT_EXTRAPOLATION_THRESHOLD_SEC, intertialFrame, intertialFrame); final SpacecraftStateInterpolator interpolator3 = - new SpacecraftStateInterpolator(interpolationPoints3, intertialFrame, intertialFrame); + new SpacecraftStateInterpolator(interpolationPoints3, SpacecraftStateInterpolator.DEFAULT_EXTRAPOLATION_THRESHOLD_SEC, intertialFrame); // When & Then checkInterpolationError(interpolationPoints1, 106.46533, 0.40709287, 169847806.33e-9, 0.0, 450 * 450, 450 * 450, @@ -509,6 +509,56 @@ void testGetNbInterpolationsWithMultipleSubInterpolators() { Mockito.when(attitudeInterpolator.getSubInterpolators()).thenReturn(Collections.singletonList(attitudeInterpolator)); Mockito.when(additionalStateInterpolator.getSubInterpolators()).thenReturn(Collections.singletonList(additionalStateInterpolator)); + final SpacecraftStateInterpolator stateInterpolator = + new SpacecraftStateInterpolator(2, 1.0e-3, frame, orbitInterpolator, absPVAInterpolator, massInterpolator, + attitudeInterpolator, additionalStateInterpolator); + + // WHEN + final int returnedNbInterpolationPoints = stateInterpolator.getNbInterpolationPoints(); + + // THEN + Assertions.assertEquals(AdditionalStateNbInterpolationPoints, returnedNbInterpolationPoints); + } + + @SuppressWarnings("deprecation") + @Test + void testGetNbInterpolationsWithMultipleSubInterpolatorsDeprecated() { + // GIVEN + // Create mock interpolators + final Frame frame = Mockito.mock(Frame.class); + + final TimeInterpolator orbitInterpolator = Mockito.mock(OrbitHermiteInterpolator.class); + + final TimeInterpolator absPVAInterpolator = + Mockito.mock(AbsolutePVCoordinatesHermiteInterpolator.class); + + final TimeInterpolator massInterpolator = + Mockito.mock(TimeStampedDoubleHermiteInterpolator.class); + + final TimeInterpolator attitudeInterpolator = Mockito.mock(AttitudeInterpolator.class); + + final TimeInterpolator additionalStateInterpolator = + Mockito.mock(TimeStampedDoubleHermiteInterpolator.class); + + // Implement mocks behaviours + final int orbitNbInterpolationPoints = 2; + final int absPVANbInterpolationPoints = 3; + final int massNbInterpolationPoints = 4; + final int AttitudeNbInterpolationPoints = 5; + final int AdditionalStateNbInterpolationPoints = 6; + + Mockito.when(orbitInterpolator.getNbInterpolationPoints()).thenReturn(orbitNbInterpolationPoints); + Mockito.when(absPVAInterpolator.getNbInterpolationPoints()).thenReturn(absPVANbInterpolationPoints); + Mockito.when(massInterpolator.getNbInterpolationPoints()).thenReturn(massNbInterpolationPoints); + Mockito.when(attitudeInterpolator.getNbInterpolationPoints()).thenReturn(AttitudeNbInterpolationPoints); + Mockito.when(additionalStateInterpolator.getNbInterpolationPoints()).thenReturn(AdditionalStateNbInterpolationPoints); + + Mockito.when(orbitInterpolator.getSubInterpolators()).thenReturn(Collections.singletonList(orbitInterpolator)); + Mockito.when(absPVAInterpolator.getSubInterpolators()).thenReturn(Collections.singletonList(absPVAInterpolator)); + Mockito.when(massInterpolator.getSubInterpolators()).thenReturn(Collections.singletonList(massInterpolator)); + Mockito.when(attitudeInterpolator.getSubInterpolators()).thenReturn(Collections.singletonList(attitudeInterpolator)); + Mockito.when(additionalStateInterpolator.getSubInterpolators()).thenReturn(Collections.singletonList(additionalStateInterpolator)); + final SpacecraftStateInterpolator stateInterpolator = new SpacecraftStateInterpolator(frame, orbitInterpolator, absPVAInterpolator, massInterpolator, attitudeInterpolator, additionalStateInterpolator); diff --git a/src/test/java/org/orekit/propagation/SpacecraftStateTest.java b/src/test/java/org/orekit/propagation/SpacecraftStateTest.java index e060b13d5e..1b532ca76f 100644 --- a/src/test/java/org/orekit/propagation/SpacecraftStateTest.java +++ b/src/test/java/org/orekit/propagation/SpacecraftStateTest.java @@ -68,10 +68,10 @@ import org.orekit.utils.PVCoordinates; -public class SpacecraftStateTest { +class SpacecraftStateTest { @Test - public void testShiftVsEcksteinHechlerError() + void testShiftVsEcksteinHechlerError() throws ParseException, OrekitException { @@ -132,7 +132,7 @@ public void testShiftVsEcksteinHechlerError() } @Test - public void testDatesConsistency() { + void testDatesConsistency() { Assertions.assertThrows(IllegalArgumentException.class, () -> { new SpacecraftState(orbit, attitudeLaw.getAttitude(orbit.shiftedBy(10.0), orbit.getDate().shiftedBy(10.0), orbit.getFrame())); @@ -144,7 +144,7 @@ public void testDatesConsistency() { * FixedRate attitude provider. */ @Test - public void testDateConsistencyClose() { + void testDateConsistencyClose() { //setup Orbit orbit10Shifts = orbit; for (int i = 0; i < 10; i++) { @@ -165,7 +165,7 @@ public void testDateConsistencyClose() { } @Test - public void testFramesConsistency() { + void testFramesConsistency() { Assertions.assertThrows(IllegalArgumentException.class, () -> { new SpacecraftState(orbit, new Attitude(orbit.getDate(), @@ -175,7 +175,7 @@ public void testFramesConsistency() { } @Test - public void testTransform() + void testTransform() throws ParseException, OrekitException { double maxDP = 0; @@ -199,7 +199,7 @@ public void testTransform() } @Test - public void testAdditionalStates() { + void testAdditionalStates() { final SpacecraftState state = propagator.propagate(orbit.getDate().shiftedBy(60)); final SpacecraftState extended = state. @@ -257,7 +257,7 @@ public void testAdditionalStates() { } @Test - public void testAdditionalStatesDerivatives() { + void testAdditionalStatesDerivatives() { final SpacecraftState state = propagator.propagate(orbit.getDate().shiftedBy(60)); final SpacecraftState extended = state. @@ -310,7 +310,7 @@ public void testAdditionalStatesDerivatives() { } @Test - public void testAdditionalTestResetOnEventAnalytical() { + void testAdditionalTestResetOnEventAnalytical() { // Build orbit AbsoluteDate date0 = new AbsoluteDate(2000, 1, 1, TimeScalesFactory.getUTC()); @@ -359,7 +359,7 @@ public SpacecraftState resetState(EventDetector detector, SpacecraftState oldSta } @Test - public void testAdditionalTestResetOnEventNumerical() { + void testAdditionalTestResetOnEventNumerical() { // Build orbit AbsoluteDate date0 = new AbsoluteDate(2000, 1, 1, TimeScalesFactory.getUTC()); @@ -409,7 +409,7 @@ public SpacecraftState resetState(EventDetector detector, SpacecraftState oldSta } @Test - public void testSerialization() + void testSerialization() throws IOException, ClassNotFoundException, OrekitException { propagator.resetInitialState(propagator.getInitialState(). @@ -465,7 +465,7 @@ public void testSerialization() } @Test - public void testSerializationWithAbsPV() + void testSerializationWithAbsPV() throws IOException, ClassNotFoundException, OrekitException { final NumericalPropagator numPropagator = new NumericalPropagator(new ClassicalRungeKuttaIntegrator(10.0)); @@ -527,7 +527,7 @@ public void testSerializationWithAbsPV() } @Test - public void testAdditionalStatesAbsPV() { + void testAdditionalStatesAbsPV() { double x_f = 0.8; double y_f = 0.2; @@ -614,7 +614,7 @@ public void testAdditionalStatesAbsPV() { } @Test - public void testAdditionalStatesDerivativesAbsPV() { + void testAdditionalStatesDerivativesAbsPV() { double x_f = 0.8; double y_f = 0.2; @@ -695,7 +695,7 @@ public void testAdditionalStatesDerivativesAbsPV() { } @Test - public void testShiftAdditionalDerivatives() { + void testShiftAdditionalDerivatives() { final String valueAndDerivative = "value-and-derivative"; final String valueOnly = "value-only"; @@ -733,7 +733,7 @@ void testToStaticTransform() { // WHEN final StaticTransform actualStaticTransform = state.toStaticTransform(); // THEN - final StaticTransform expectedStaticTransform = state.toTransform().toStaticTransform(); + final StaticTransform expectedStaticTransform = state.toTransform(); Assertions.assertEquals(expectedStaticTransform.getDate(), actualStaticTransform.getDate()); Assertions.assertEquals(expectedStaticTransform.getTranslation(), actualStaticTransform.getTranslation()); Assertions.assertEquals(0., Rotation.distance(expectedStaticTransform.getRotation(), diff --git a/src/test/java/org/orekit/propagation/StateCovarianceBlenderTest.java b/src/test/java/org/orekit/propagation/StateCovarianceBlenderTest.java index b2a19a9e7b..e2f5312985 100644 --- a/src/test/java/org/orekit/propagation/StateCovarianceBlenderTest.java +++ b/src/test/java/org/orekit/propagation/StateCovarianceBlenderTest.java @@ -16,9 +16,6 @@ */ 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; @@ -47,6 +44,9 @@ import org.orekit.time.TimeStampedPair; import org.orekit.utils.Constants; +import java.util.Locale; +import java.util.Map; + class StateCovarianceBlenderTest { private static SpacecraftState sergeiState; @@ -97,7 +97,7 @@ private void doTestBlending(final double propagationHorizon, final double tabula // Create state interpolator final TimeInterpolator stateInterpolator = - new SpacecraftStateInterpolator(sergeiFrame, orbitInterpolator, null, null, null, null); + new SpacecraftStateInterpolator(2, 1.0e-3, sergeiFrame, orbitInterpolator, null, null, null, null); // When final DescriptiveStatistics[] relativeRMSSigmaError = @@ -140,7 +140,7 @@ private void doTestBlending(final double propagationHorizon, final double tabula void testKeplerianQuadraticBlending() { // Given final boolean showResults = false; // Show results? - final double tolerance = 1.e-16; + final double tolerance = 1.e-11; // Create state covariance interpolator final SmoothStepFactory.SmoothStepFunction blendingFunction = SmoothStepFactory.getQuadratic(); @@ -148,12 +148,12 @@ void testKeplerianQuadraticBlending() { // When & Then doTestBlending(DEFAULT_SERGEI_PROPAGATION_TIME, DEFAUTL_SERGEI_TABULATED_TIMESTEP, blendingFunction, new KeplerianPropagator(sergeiOrbit), - 0.1133302045524815, - 0.2351882330196802, - 0.1111607965207670, - 0.2621621038900783, - 0.2687823732965123, - 0.4022217682474207, + 0.11333019740, + 0.23518823987, + 0.11116079511, + 0.26216208074, + 0.268782359940, + 0.402221757858, tolerance, showResults); @@ -180,7 +180,58 @@ void testKeplerianQuadraticBlending() { void testBrouwerLyddaneQuadraticBlending() { // Given final boolean showResults = false; // Show results? - final double tolerance = 1.e-16; + final double tolerance = 1.e-11; + + // Create state covariance interpolator + final SmoothStepFactory.SmoothStepFunction blendingFunction = SmoothStepFactory.getQuadratic(); + + final AbstractAnalyticalPropagator propagator = new BrouwerLyddanePropagator(sergeiOrbit, + sergeiState.getMass(), + Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS, + Constants.EIGEN5C_EARTH_MU, + Constants.EIGEN5C_EARTH_C20, + Constants.EIGEN5C_EARTH_C30, + Constants.EIGEN5C_EARTH_C40, + Constants.EIGEN5C_EARTH_C50, + 0); + + // 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, + tolerance, + showResults); + + // Results obtained when using Sergei reference date + /* Assertions.assertEquals(0.07645785479359624, relativeRMSSigmaError[0].getMean(), 1e-17); + Assertions.assertEquals(0.17941792898602038, relativeRMSSigmaError[1].getMean(), 1e-17); + Assertions.assertEquals(0.08259069655149026, relativeRMSSigmaError[0].getPercentile(50), 1e-17); + Assertions.assertEquals(0.18352413417267063, relativeRMSSigmaError[1].getPercentile(50), 1e-17); + Assertions.assertEquals(0.13164670404592496, relativeRMSSigmaError[0].getMax(), 1e-17); + Assertions.assertEquals(0.32564919981018114, relativeRMSSigmaError[1].getMax(), 1e-16);*/ + } + + /** + * Test based on the full force model test case from TANYGIN, Sergei. Efficient covariance interpolation using blending + * of approximate covariance propagations. The Journal of the Astronautical Sciences, 2014, vol. 61, no 1, p. 107-132. + *

          + * This instance of the test aims to test the Quadratic Blending method using Brouwer Lyddane propagation. This is a + * non-regression test. + */ + @Deprecated + @Test + @DisplayName("test Brouwer Lyddane quadratic blending interpolation on full force model test case from: " + + "TANYGIN, Sergei. Efficient covariance interpolation using blending of approximate covariance propagations. " + + "The Journal of the Astronautical Sciences, 2014, vol. 61, no 1, p. 107-132.") + void testBrouwerLyddaneQuadraticBlendingDeprecated() { + // Given + final boolean showResults = false; // Show results? + final double tolerance = 1.e-11; // Create state covariance interpolator final SmoothStepFactory.SmoothStepFunction blendingFunction = SmoothStepFactory.getQuadratic(); @@ -198,12 +249,12 @@ void testBrouwerLyddaneQuadraticBlending() { // When & Then doTestBlending(DEFAULT_SERGEI_PROPAGATION_TIME, DEFAUTL_SERGEI_TABULATED_TIMESTEP, blendingFunction, propagator, - 0.0823442943514750, - 0.1976467714037895, - 0.0888590153696339, - 0.2020905044160413, - 0.1446159909426887, - 0.3894610461790040, + 0.082344296100, + 0.197646775666, + 0.088859013862, + 0.202090523492, + 0.144616020330, + 0.389461090177, tolerance, showResults); @@ -230,7 +281,7 @@ void testBrouwerLyddaneQuadraticBlending() { void testEksteinHechlerQuadraticBlending() { // Given final boolean showResults = false; // Show results? - final double tolerance = 1.e-16; + final double tolerance = 1.e-11; // Create state covariance interpolator final SmoothStepFactory.SmoothStepFunction blendingFunction = SmoothStepFactory.getQuadratic(); @@ -248,12 +299,12 @@ void testEksteinHechlerQuadraticBlending() { // When & Then doTestBlending(DEFAULT_SERGEI_PROPAGATION_TIME, DEFAUTL_SERGEI_TABULATED_TIMESTEP, blendingFunction, propagator, - 0.0920227696921130, - 0.1753289773020222, - 0.0857543808467324, - 0.1933199872752203, - 0.1693483908858563, - 0.3473020830532024, + 0.092022772752, + 0.175328981233, + 0.085754375965, + 0.19331997037, + 0.169348422249, + 0.347302066034, tolerance, showResults); @@ -287,7 +338,7 @@ void testEksteinHechlerQuadraticBlending() { void testLOFKeplerianBlending() { // Given final boolean showResults = false; // Show results? - final double tolerance = 1.e-16; + final double tolerance = 1.e-9; // Create state covariance interpolator final SmoothStepFactory.SmoothStepFunction blendingFunction = SmoothStepFactory.getQuadratic(); @@ -318,12 +369,12 @@ void testLOFKeplerianBlending() { } // Results obtained when using modified orbit date to use truncated JPL test resource file - Assertions.assertEquals( 0.1190324153130111, relativeRMSSigmaError[0].getMean(), tolerance); - Assertions.assertEquals( 7.2800694213600920, relativeRMSSigmaError[1].getMean(), tolerance); - Assertions.assertEquals( 0.1221432756730858, relativeRMSSigmaError[0].getPercentile(50), tolerance); - Assertions.assertEquals( 7.4882845444744826, relativeRMSSigmaError[1].getPercentile(50), tolerance); - Assertions.assertEquals( 0.2282143889000270, relativeRMSSigmaError[0].getMax(), tolerance); - Assertions.assertEquals(16.0468464408560370, relativeRMSSigmaError[1].getMax(), tolerance); + Assertions.assertEquals( 0.1190324127, relativeRMSSigmaError[0].getMean(), tolerance); + Assertions.assertEquals( 7.2800701738, relativeRMSSigmaError[1].getMean(), tolerance); + Assertions.assertEquals( 0.1221432731, relativeRMSSigmaError[0].getPercentile(50), tolerance); + Assertions.assertEquals( 7.4882858178, relativeRMSSigmaError[1].getPercentile(50), tolerance); + Assertions.assertEquals( 0.2282143786, relativeRMSSigmaError[0].getMax(), tolerance); + Assertions.assertEquals(16.0468503672, relativeRMSSigmaError[1].getMax(), tolerance); // Assert getters as well Assertions.assertNull(covarianceInterpolator.getOutFrame()); diff --git a/src/test/java/org/orekit/propagation/StateCovarianceKeplerianHermiteInterpolatorTest.java b/src/test/java/org/orekit/propagation/StateCovarianceKeplerianHermiteInterpolatorTest.java index fac52d1645..82890d58ec 100644 --- a/src/test/java/org/orekit/propagation/StateCovarianceKeplerianHermiteInterpolatorTest.java +++ b/src/test/java/org/orekit/propagation/StateCovarianceKeplerianHermiteInterpolatorTest.java @@ -16,11 +16,6 @@ */ package org.orekit.propagation; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.Map; - import org.hipparchus.analysis.polynomials.SmoothStepFactory; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.linear.BlockRealMatrix; @@ -72,6 +67,7 @@ import org.orekit.propagation.analytical.KeplerianPropagator; import org.orekit.propagation.numerical.NumericalPropagator; import org.orekit.time.AbsoluteDate; +import org.orekit.time.AbstractTimeInterpolator; import org.orekit.time.TimeInterpolator; import org.orekit.time.TimeScalesFactory; import org.orekit.time.TimeStampedPair; @@ -80,6 +76,11 @@ import org.orekit.utils.IERSConventions; import org.orekit.utils.PVCoordinates; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; + public class StateCovarianceKeplerianHermiteInterpolatorTest { private static Orbit sergeiOrbit; private static Frame sergeiFrame; @@ -435,7 +436,7 @@ public static void configurePropagatorForSergeiCase(final NumericalPropagator pr final IsotropicDrag dragSensitive = new IsotropicDrag(crossSection, cd); final Atmosphere atmosphere = new NRLMSISE00(new CssiSpaceWeatherData(CssiSpaceWeatherData.DEFAULT_SUPPORTED_NAMES), sun, earthShape); - final DragForce dragForce = new DragForce(atmosphere, dragSensitive); + final DragForce dragForce = new DragForce(atmosphere, dragSensitive, false); propagator.addForceModel(dragForce); // Third body @@ -554,12 +555,16 @@ private void doTestInterpolation(final TimeInterpolator stateIn // Then if (showResults) { - System.out.format(Locale.US, "%35s = %20.16f%n", "relativeRMSSigmaError[0].getMean", relativeRMSSigmaError[0].getMean()); - System.out.format(Locale.US, "%35s = %20.16f%n", "relativeRMSSigmaError[1].getMean", relativeRMSSigmaError[1].getMean()); - System.out.format(Locale.US, "%35s = %20.16f%n", "relativeRMSSigmaError[0].getMedian", relativeRMSSigmaError[0].getPercentile(50)); - System.out.format(Locale.US, "%35s = %20.16f%n", "relativeRMSSigmaError[1].getMedian", relativeRMSSigmaError[1].getPercentile(50)); - System.out.format(Locale.US, "%35s = %20.16f%n", "relativeRMSSigmaError[0].getMax", relativeRMSSigmaError[0].getMax()); - System.out.format(Locale.US, "%35s = %20.16f%n", "relativeRMSSigmaError[1].getMax", relativeRMSSigmaError[1].getMax()); + // Significant number to display + final int nDigits = (int) FastMath.log10(1/tolerance); + final String fmt = String.format(Locale.US, "%%35s = %%%2d.%2df%%n", nDigits + 4, nDigits); + + System.out.format(Locale.US, fmt, "relativeRMSSigmaError[0].getMean", relativeRMSSigmaError[0].getMean()); + System.out.format(Locale.US, fmt, "relativeRMSSigmaError[1].getMean", relativeRMSSigmaError[1].getMean()); + System.out.format(Locale.US, fmt, "relativeRMSSigmaError[0].getMedian", relativeRMSSigmaError[0].getPercentile(50)); + System.out.format(Locale.US, fmt, "relativeRMSSigmaError[1].getMedian", relativeRMSSigmaError[1].getPercentile(50)); + System.out.format(Locale.US, fmt, "relativeRMSSigmaError[0].getMax", relativeRMSSigmaError[0].getMax()); + System.out.format(Locale.US, fmt, "relativeRMSSigmaError[1].getMax", relativeRMSSigmaError[1].getMax()); } // Results obtained when using modified orbit date to use truncated JPL test resource file @@ -591,7 +596,7 @@ void testQuinticKeplerianInterpolation() { // Given final boolean showResults = false; // Show results? - final double tolerance = 1.e-16; + final double tolerance = 1.e-9; // Create state covariance interpolator final SmoothStepFactory.SmoothStepFunction blendingFunction = SmoothStepFactory.getQuadratic(); @@ -605,17 +610,19 @@ void testQuinticKeplerianInterpolation() { // Create state interpolator final TimeInterpolator stateInterpolator = - new SpacecraftStateInterpolator(sergeiFrame, orbitInterpolator, null, null, null, null); + new SpacecraftStateInterpolator(AbstractTimeInterpolator.DEFAULT_INTERPOLATION_POINTS, AbstractTimeInterpolator.DEFAULT_EXTRAPOLATION_THRESHOLD_SEC, + sergeiFrame, orbitInterpolator, + null, null, null, null); // When & Then doTestInterpolation(stateInterpolator, covarianceInterpolator, DEFAULT_SERGEI_PROPAGATION_TIME, DEFAULT_SERGEI_TABULATED_TIMESTEP, - 0.0646887955936730, - 0.1870011267826034, - 0.0605252722806762, - 0.2090092562980378, - 0.1722559416492755, - 0.3756010728001388, + 0.06468878849, + 0.18700110863, + 0.06052527006, + 0.20900926133, + 0.17225595779, + 0.37560104200, tolerance, showResults); @@ -647,7 +654,7 @@ void testCubicKeplerianInterpolation() { // Given final boolean showResults = false; // Show results? - final double tolerance = 1.e-16; + final double tolerance = 1.e-9; // Create state covariance interpolator final SmoothStepFactory.SmoothStepFunction blendingFunction = SmoothStepFactory.getQuadratic(); @@ -664,17 +671,18 @@ void testCubicKeplerianInterpolation() { // Create state interpolator final TimeInterpolator stateInterpolator = - new SpacecraftStateInterpolator(sergeiFrame, orbitInterpolator, null, null, null, null); + new SpacecraftStateInterpolator(AbstractTimeInterpolator.DEFAULT_INTERPOLATION_POINTS, AbstractTimeInterpolator.DEFAULT_EXTRAPOLATION_THRESHOLD_SEC, + sergeiFrame, orbitInterpolator, null, null, null, null); // When & then doTestInterpolation(stateInterpolator, covarianceInterpolator, DEFAULT_SERGEI_PROPAGATION_TIME, DEFAULT_SERGEI_TABULATED_TIMESTEP, - 0.0687107241065522, - 0.1727658843435031, - 0.0696685213697581, - 0.1788064054703819, - 0.1702870226981246, - 0.3841670380381794, + 0.0687107434, + 0.1727658634, + 0.06966854473, + 0.17880639263, + 0.17028706967, + 0.3841670162, tolerance, showResults); @@ -705,7 +713,7 @@ void testLinearKeplerianInterpolation() { // Given final boolean showResults = false; // Show results? - final double tolerance = 1.e-16; + final double tolerance = 1.e-9; // Create state covariance interpolator final SmoothStepFactory.SmoothStepFunction blendingFunction = SmoothStepFactory.getQuadratic(); @@ -722,18 +730,19 @@ void testLinearKeplerianInterpolation() { // Create state interpolator final TimeInterpolator stateInterpolator = - new SpacecraftStateInterpolator(sergeiFrame, orbitInterpolator, null, null, null, null); + new SpacecraftStateInterpolator(AbstractTimeInterpolator.DEFAULT_INTERPOLATION_POINTS, AbstractTimeInterpolator.DEFAULT_EXTRAPOLATION_THRESHOLD_SEC, + sergeiFrame, orbitInterpolator, null, null, null, null); // When & Then doTestInterpolation(stateInterpolator, covarianceInterpolator, DEFAULT_SERGEI_PROPAGATION_TIME, DEFAULT_SERGEI_TABULATED_TIMESTEP, - 0.1967531616991254, - 0.1744809570174334, - 0.2201299654842542, - 0.1501037774167836, - 0.3115607775141900, - 0.4912990230073768, - tolerance, + 0.1967532898, + 0.174480997, + 0.220130098, + 0.150103817, + 0.311560976, + 0.491299092, + tolerance, showResults); // Results obtained when using Sergei reference date @@ -770,7 +779,7 @@ void testLOFInterpolation() { // Given final boolean showResults = false; // Show results? - final double tolerance = 1.e-16; + final double tolerance = 1.e-9; // Default orbit case final Orbit orbit = generateSergeiReferenceOrbit(); @@ -795,20 +804,19 @@ void testLOFInterpolation() { // Then if (showResults) { - System.out.format(Locale.US, "%35s = %20.16f%n", "relativeRMSSigmaError[0].getMean", relativeRMSSigmaError[0].getMean()); - System.out.format(Locale.US, "%35s = %20.16f%n", "relativeRMSSigmaError[1].getMean", relativeRMSSigmaError[1].getMean()); - System.out.format(Locale.US, "%35s = %20.16f%n", "relativeRMSSigmaError[0].getMedian", relativeRMSSigmaError[0].getPercentile(50)); - System.out.format(Locale.US, "%35s = %20.16f%n", "relativeRMSSigmaError[1].getMedian", relativeRMSSigmaError[1].getPercentile(50)); - System.out.format(Locale.US, "%35s = %20.16f%n", "relativeRMSSigmaError[0].getMax", relativeRMSSigmaError[0].getMax()); - System.out.format(Locale.US, "%35s = %20.16f%n", "relativeRMSSigmaError[1].getMax", relativeRMSSigmaError[1].getMax()); + System.out.format(Locale.US, "%35s = %20.12f%n", "relativeRMSSigmaError[0].getMean", relativeRMSSigmaError[0].getMean()); + System.out.format(Locale.US, "%35s = %20.12f%n", "relativeRMSSigmaError[1].getMean", relativeRMSSigmaError[1].getMean()); + System.out.format(Locale.US, "%35s = %20.12f%n", "relativeRMSSigmaError[0].getMedian", relativeRMSSigmaError[0].getPercentile(50)); + System.out.format(Locale.US, "%35s = %20.12f%n", "relativeRMSSigmaError[1].getMedian", relativeRMSSigmaError[1].getPercentile(50)); + System.out.format(Locale.US, "%35s = %20.12f%n", "relativeRMSSigmaError[0].getMax", relativeRMSSigmaError[0].getMax()); + System.out.format(Locale.US, "%35s = %20.12f%n", "relativeRMSSigmaError[1].getMax", relativeRMSSigmaError[1].getMax()); } - Assertions.assertEquals( 0.0678893939532068, relativeRMSSigmaError[0].getMean(), tolerance); - Assertions.assertEquals( 7.3610159507701730, relativeRMSSigmaError[1].getMean(), tolerance); - Assertions.assertEquals( 0.0649252237750957, relativeRMSSigmaError[0].getPercentile(50), tolerance); - Assertions.assertEquals( 7.7054187147650770, relativeRMSSigmaError[1].getPercentile(50), tolerance); - Assertions.assertEquals( 0.1405955596105993, relativeRMSSigmaError[0].getMax(), tolerance); - Assertions.assertEquals(16.0051089451628240, relativeRMSSigmaError[1].getMax(), tolerance); - + Assertions.assertEquals( 0.067889396, relativeRMSSigmaError[0].getMean(), tolerance); + Assertions.assertEquals( 7.361016578, relativeRMSSigmaError[1].getMean(), tolerance); + Assertions.assertEquals( 0.064925239, relativeRMSSigmaError[0].getPercentile(50), tolerance); + Assertions.assertEquals( 7.705418959, relativeRMSSigmaError[1].getPercentile(50), tolerance); + Assertions.assertEquals( 0.140595553, relativeRMSSigmaError[0].getMax(), tolerance); + Assertions.assertEquals(16.005112864, relativeRMSSigmaError[1].getMax(), tolerance); } } diff --git a/src/test/java/org/orekit/propagation/analytical/AggregateBoundedPropagatorTest.java b/src/test/java/org/orekit/propagation/analytical/AggregateBoundedPropagatorTest.java index 2c51758c30..3b5de9fef6 100644 --- a/src/test/java/org/orekit/propagation/analytical/AggregateBoundedPropagatorTest.java +++ b/src/test/java/org/orekit/propagation/analytical/AggregateBoundedPropagatorTest.java @@ -16,29 +16,37 @@ */ package org.orekit.propagation.analytical; -import java.util.Arrays; -import java.util.Collections; -import java.util.Map; -import java.util.NavigableMap; -import java.util.TreeMap; - import org.hamcrest.CoreMatchers; import org.hamcrest.MatcherAssert; +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.mockito.Mockito; import org.orekit.OrekitMatchers; import org.orekit.Utils; +import org.orekit.attitudes.FrameAlignedProvider; +import org.orekit.attitudes.LofOffset; import org.orekit.errors.OrekitException; import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; +import org.orekit.frames.LOFType; import org.orekit.orbits.KeplerianOrbit; +import org.orekit.orbits.Orbit; import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.BoundedPropagator; import org.orekit.propagation.EphemerisGenerator; import org.orekit.propagation.SpacecraftState; import org.orekit.time.AbsoluteDate; import org.orekit.utils.Constants; +import org.orekit.utils.TimeSpanMap; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.NavigableMap; +import java.util.TreeMap; /** * Tests for {@link AggregateBoundedPropagator}. @@ -58,11 +66,9 @@ public void setUp() { /** * Check {@link AggregateBoundedPropagator#propagateOrbit(AbsoluteDate)} when the * constituent propagators are exactly adjacent. - * - * @throws Exception on error. */ @Test - public void testAdjacent() throws Exception { + public void testAdjacent() { // setup AbsoluteDate date = AbsoluteDate.CCSDS_EPOCH; BoundedPropagator p1 = createPropagator(date, date.shiftedBy(10), 0); @@ -92,21 +98,23 @@ public void testAdjacent() throws Exception { actual.propagate(date.shiftedBy(20)).getPVCoordinates(), OrekitMatchers.pvCloseTo(p2.propagate(date.shiftedBy(20)).getPVCoordinates(), ulps)); - Assertions.assertEquals(2, actual.getPropagators().size()); - for (final Map.Entry entry : actual.getPropagators().entrySet()) { - Assertions.assertEquals(entry.getKey(), entry.getValue().getMinDate()); + for (TimeSpanMap.Span span = actual.getPropagatorsMap().getFirstNonNullSpan(); + span != null; + span = span.next()) { + Assertions.assertEquals(span.getStart(), span.getData().getMinDate()); } + // test deprecated method + Assertions.assertEquals(2, actual.getPropagators().size()); + } /** * Check {@link AggregateBoundedPropagator#propagateOrbit(AbsoluteDate)} when the * constituent propagators overlap. - * - * @throws Exception on error. */ @Test - public void testOverlap() throws Exception { + public void testOverlap() { // setup AbsoluteDate date = AbsoluteDate.CCSDS_EPOCH; BoundedPropagator p1 = createPropagator(date, date.shiftedBy(25), 0); @@ -140,11 +148,9 @@ public void testOverlap() throws Exception { /** * Check {@link AggregateBoundedPropagator#propagateOrbit(AbsoluteDate)} with a gap * between the constituent propagators. - * - * @throws Exception on error. */ @Test - public void testGap() throws Exception { + public void testGap() { // setup AbsoluteDate date = AbsoluteDate.CCSDS_EPOCH; BoundedPropagator p1 = createPropagator(date, date.shiftedBy(1), 0); @@ -181,7 +187,7 @@ public void testGap() throws Exception { } @Test - public void testOutsideBounds() throws Exception { + public void testOutsideBounds() { // setup AbsoluteDate date = AbsoluteDate.CCSDS_EPOCH; BoundedPropagator p1 = createPropagator(date, date.shiftedBy(10), 0); @@ -315,9 +321,87 @@ private BoundedPropagator createPropagator(AbsoluteDate start, double gm = Constants.EGM96_EARTH_MU; KeplerianPropagator propagator = new KeplerianPropagator(new KeplerianOrbit( 6778137, 0, 0, 0, 0, v, PositionAngleType.TRUE, frame, start, gm)); + propagator.setAttitudeProvider(new LofOffset(frame, LOFType.LVLH_CCSDS)); final EphemerisGenerator generator = propagator.getEphemerisGenerator(); propagator.propagate(start, end); return generator.getGeneratedEphemeris(); } + @Test + void testAttitude() { + AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + AbsoluteDate end = date.shiftedBy(20); + BoundedPropagator p1 = createPropagator(date, end, 0); + BoundedPropagator p2 = createPropagator(date.shiftedBy(10), end, 0); + final BoundedPropagator actual = new AggregateBoundedPropagator(Arrays.asList(p1, p2)); + + // using the attitude providers from the underlying propagator + final SpacecraftState s0 = actual.propagate(date.shiftedBy(15)); + Assertions.assertEquals(0, + Vector3D.angle(s0.getPosition(), + s0.getAttitude().getRotation().applyInverseTo(Vector3D.MINUS_K)), + 3.0e-16); + + // overriding explicitly the global attitude provider + actual.setAttitudeProvider(new FrameAlignedProvider(p1.getInitialState().getFrame())); + final SpacecraftState s1 = actual.propagate(date.shiftedBy(15)); + Assertions.assertEquals(0, + Vector3D.angle(Vector3D.MINUS_K, + s1.getAttitude().getRotation().applyInverseTo(Vector3D.MINUS_K)), + 3.0e-16); + Assertions.assertEquals(1.570796, + Vector3D.angle(s1.getPosition(), + s1.getAttitude().getRotation().applyInverseTo(Vector3D.MINUS_K)), + 1.0e-6); + + } + + @Test + void testPropagateOrbit() { + // GIVEN + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final SpacecraftState mockedState = Mockito.mock(SpacecraftState.class); + final Orbit expectedOrbit = Mockito.mock(Orbit.class); + Mockito.when(mockedState.getOrbit()).thenReturn(expectedOrbit); + final BoundedPropagator mockedBoundedPropagator = mockBoundedPropagator(date, mockedState); + final List boundedPropagatorList = new ArrayList<>(); + boundedPropagatorList.add(mockedBoundedPropagator); + final AggregateBoundedPropagator propagator = new AggregateBoundedPropagator(boundedPropagatorList); + + // WHEN + final Orbit actualOrbit = propagator.propagateOrbit(date); + + // THEN + Assertions.assertEquals(expectedOrbit, actualOrbit); + } + + @Test + void testGetPosition() { + // GIVEN + final AbsoluteDate date = AbsoluteDate.ARBITRARY_EPOCH; + final Frame mockedFrame = Mockito.mock(Frame.class); + final SpacecraftState mockedState = Mockito.mock(SpacecraftState.class); + final Vector3D expectedPosition = new Vector3D(1, 2, 3); + Mockito.when(mockedState.getPosition(mockedFrame)).thenReturn(expectedPosition); + final BoundedPropagator mockedBoundedPropagator = mockBoundedPropagator(date, mockedState); + final List boundedPropagatorList = new ArrayList<>(); + boundedPropagatorList.add(mockedBoundedPropagator); + final AggregateBoundedPropagator propagator = new AggregateBoundedPropagator(boundedPropagatorList); + + // WHEN + final Vector3D actualPosition = propagator.getPosition(date, mockedFrame); + + // THEN + Assertions.assertEquals(expectedPosition, actualPosition); + } + + private BoundedPropagator mockBoundedPropagator(final AbsoluteDate date, final SpacecraftState state) { + final BoundedPropagator mockedBoundedPropagator = Mockito.mock(BoundedPropagator.class); + Mockito.when(mockedBoundedPropagator.getMinDate()).thenReturn(AbsoluteDate.PAST_INFINITY); + Mockito.when(mockedBoundedPropagator.getMinDate()).thenReturn(AbsoluteDate.FUTURE_INFINITY); + Mockito.when(mockedBoundedPropagator.propagate(date)).thenReturn(state); + Mockito.when(mockedBoundedPropagator.getInitialState()).thenReturn(state); + return mockedBoundedPropagator; + } + } diff --git a/src/test/java/org/orekit/propagation/analytical/BrouwerLyddaneParametersDerivativesTest.java b/src/test/java/org/orekit/propagation/analytical/BrouwerLyddaneParametersDerivativesTest.java index 910ae19126..182ca56b7e 100644 --- a/src/test/java/org/orekit/propagation/analytical/BrouwerLyddaneParametersDerivativesTest.java +++ b/src/test/java/org/orekit/propagation/analytical/BrouwerLyddaneParametersDerivativesTest.java @@ -1,3 +1,20 @@ +/* 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.analytical; import org.hipparchus.geometry.euclidean.threed.Vector3D; diff --git a/src/test/java/org/orekit/propagation/analytical/BrouwerLyddanePropagatorTest.java b/src/test/java/org/orekit/propagation/analytical/BrouwerLyddanePropagatorTest.java index 91674c8d47..8055be71c7 100644 --- a/src/test/java/org/orekit/propagation/analytical/BrouwerLyddanePropagatorTest.java +++ b/src/test/java/org/orekit/propagation/analytical/BrouwerLyddanePropagatorTest.java @@ -1,3 +1,20 @@ +/* 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.analytical; import org.hipparchus.geometry.euclidean.threed.Vector3D; @@ -691,6 +708,7 @@ public void testMeanOrbit() { num.addForceModel(new HolmesFeatherstoneAttractionModel(itrf, provider)); num.setInitialState(new SpacecraftState(initialOsculating)); num.setOrbitType(OrbitType.KEPLERIAN); + num.setPositionAngleType(initialOsculating.getCachedPositionAngleType()); final StorelessUnivariateStatistic oscMin = new Min(); final StorelessUnivariateStatistic oscMax = new Max(); final StorelessUnivariateStatistic meanMin = new Min(); diff --git a/src/test/java/org/orekit/propagation/analytical/BrouwerLyddaneStateTransitionMatrixTest.java b/src/test/java/org/orekit/propagation/analytical/BrouwerLyddaneStateTransitionMatrixTest.java index 3a71b73633..3a518c8fc7 100644 --- a/src/test/java/org/orekit/propagation/analytical/BrouwerLyddaneStateTransitionMatrixTest.java +++ b/src/test/java/org/orekit/propagation/analytical/BrouwerLyddaneStateTransitionMatrixTest.java @@ -1,3 +1,20 @@ +/* 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.analytical; import org.hipparchus.geometry.euclidean.threed.Vector3D; diff --git a/src/test/java/org/orekit/propagation/analytical/EcksteinHechlerPropagatorTest.java b/src/test/java/org/orekit/propagation/analytical/EcksteinHechlerPropagatorTest.java index 0f638613c7..e18f465cbd 100644 --- a/src/test/java/org/orekit/propagation/analytical/EcksteinHechlerPropagatorTest.java +++ b/src/test/java/org/orekit/propagation/analytical/EcksteinHechlerPropagatorTest.java @@ -881,6 +881,7 @@ public void testMeanOrbit() { num.addForceModel(new HolmesFeatherstoneAttractionModel(itrf, GravityFieldFactory.getNormalizedProvider(provider))); num.setInitialState(new SpacecraftState(initialOsculating)); num.setOrbitType(OrbitType.CIRCULAR); + num.setPositionAngleType(initialOsculating.getCachedPositionAngleType()); final StorelessUnivariateStatistic oscMin = new Min(); final StorelessUnivariateStatistic oscMax = new Max(); final StorelessUnivariateStatistic meanMin = new Min(); diff --git a/src/test/java/org/orekit/propagation/analytical/EcksteinHechlerStateTransitionMatrixTest.java b/src/test/java/org/orekit/propagation/analytical/EcksteinHechlerStateTransitionMatrixTest.java index adfe989db3..6bff1026b1 100644 --- a/src/test/java/org/orekit/propagation/analytical/EcksteinHechlerStateTransitionMatrixTest.java +++ b/src/test/java/org/orekit/propagation/analytical/EcksteinHechlerStateTransitionMatrixTest.java @@ -1,3 +1,20 @@ +/* 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.analytical; import org.hipparchus.geometry.euclidean.threed.Vector3D; diff --git a/src/test/java/org/orekit/propagation/analytical/FieldBrouwerLyddanePropagatorTest.java b/src/test/java/org/orekit/propagation/analytical/FieldBrouwerLyddanePropagatorTest.java index 6bc4a7251c..68bc4e870a 100644 --- a/src/test/java/org/orekit/propagation/analytical/FieldBrouwerLyddanePropagatorTest.java +++ b/src/test/java/org/orekit/propagation/analytical/FieldBrouwerLyddanePropagatorTest.java @@ -1,3 +1,20 @@ +/* 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.analytical; import java.io.IOException; @@ -880,6 +897,7 @@ private > void doTestMeanOrbit(Field field) num.addForceModel(new HolmesFeatherstoneAttractionModel(itrf, provider)); num.setInitialState(new FieldSpacecraftState<>(initialOsculating)); num.setOrbitType(OrbitType.KEPLERIAN); + num.setPositionAngleType(initialOsculating.getCachedPositionAngleType()); final StorelessUnivariateStatistic oscMin = new Min(); final StorelessUnivariateStatistic oscMax = new Max(); final StorelessUnivariateStatistic meanMin = new Min(); diff --git a/src/test/java/org/orekit/propagation/analytical/FieldEcksteinHechlerPropagatorTest.java b/src/test/java/org/orekit/propagation/analytical/FieldEcksteinHechlerPropagatorTest.java index ef1fb11ef3..3565c035fd 100644 --- a/src/test/java/org/orekit/propagation/analytical/FieldEcksteinHechlerPropagatorTest.java +++ b/src/test/java/org/orekit/propagation/analytical/FieldEcksteinHechlerPropagatorTest.java @@ -817,7 +817,6 @@ private > void doDate(Field field) { FieldEcksteinHechlerPropagator propagator = new FieldEcksteinHechlerPropagator<>(orbit, provider); final FieldAbsoluteDate stopDate = date.shiftedBy(500.0); - @SuppressWarnings("unchecked") FieldDateDetector detector = new FieldDateDetector<>(field, stopDate); propagator.addEventDetector(detector); FieldAbsoluteDate farTarget = date.shiftedBy(10000.0); @@ -1002,6 +1001,7 @@ private > void doTestMeanOrbit(Field field) num.addForceModel(new HolmesFeatherstoneAttractionModel(itrf, GravityFieldFactory.getNormalizedProvider(provider))); num.setInitialState(new FieldSpacecraftState<>(initialOsculating)); num.setOrbitType(OrbitType.CIRCULAR); + num.setPositionAngleType(initialOsculating.getCachedPositionAngleType()); final StorelessUnivariateStatistic oscMin = new Min(); final StorelessUnivariateStatistic oscMax = new Max(); final StorelessUnivariateStatistic meanMin = new Min(); diff --git a/src/test/java/org/orekit/propagation/analytical/FieldKeplerianPropagatorTest.java b/src/test/java/org/orekit/propagation/analytical/FieldKeplerianPropagatorTest.java index 5b92378bf3..23a2321177 100644 --- a/src/test/java/org/orekit/propagation/analytical/FieldKeplerianPropagatorTest.java +++ b/src/test/java/org/orekit/propagation/analytical/FieldKeplerianPropagatorTest.java @@ -674,7 +674,6 @@ private > void doTestDate(Field field) { FramesFactory.getEME2000(), new FieldAbsoluteDate<>(field), zero.add(3.986004415e14)); FieldKeplerianPropagator propagator = new FieldKeplerianPropagator<>(orbit); final FieldAbsoluteDate stopDate = new FieldAbsoluteDate<>(field).shiftedBy(500.0); - @SuppressWarnings("unchecked") FieldDateDetector detector = new FieldDateDetector<>(field, stopDate); propagator.addEventDetector(detector); FieldAbsoluteDate farTarget = new FieldAbsoluteDate<>(field).shiftedBy(10000.0); diff --git a/src/test/java/org/orekit/propagation/analytical/KeplerianStateTransitionMatrixTest.java b/src/test/java/org/orekit/propagation/analytical/KeplerianStateTransitionMatrixTest.java index 81769b2324..eb5886a27f 100644 --- a/src/test/java/org/orekit/propagation/analytical/KeplerianStateTransitionMatrixTest.java +++ b/src/test/java/org/orekit/propagation/analytical/KeplerianStateTransitionMatrixTest.java @@ -1,3 +1,20 @@ +/* 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.analytical; import org.hipparchus.geometry.euclidean.threed.Vector3D; diff --git a/src/test/java/org/orekit/propagation/analytical/intelsat/FieldIntelsatElevenElementsPropagatorTest.java b/src/test/java/org/orekit/propagation/analytical/intelsat/FieldIntelsatElevenElementsPropagatorTest.java new file mode 100644 index 0000000000..72319ca497 --- /dev/null +++ b/src/test/java/org/orekit/propagation/analytical/intelsat/FieldIntelsatElevenElementsPropagatorTest.java @@ -0,0 +1,104 @@ +/* Copyright 2002-2024 Airbus Defence and Space + * 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. + * Airbus Defence and Space 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.intelsat; + +import org.hipparchus.util.Binary64; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.MathUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.FieldKeplerianOrbit; +import org.orekit.orbits.KeplerianOrbit; +import org.orekit.orbits.OrbitType; +import org.orekit.orbits.PositionAngleType; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.TimeScalesFactory; +import org.orekit.utils.IERSConventions; + +public class FieldIntelsatElevenElementsPropagatorTest { + + private static FieldIntelsatElevenElements ELEMENTS; + + @Test + public void testCannotResetIntermediateState() { + FieldIntelsatElevenElementsPropagator propagator = new FieldIntelsatElevenElementsPropagator<>(ELEMENTS); + try { + propagator.resetIntermediateState(null, false); + } + catch (OrekitException oe) { + Assertions.assertEquals(oe.getSpecifier(), OrekitMessages.NON_RESETABLE_STATE); + } + } + + @Test + public void testCannotResetInitialState() { + FieldIntelsatElevenElementsPropagator propagator = new FieldIntelsatElevenElementsPropagator<>(ELEMENTS); + try { + propagator.resetInitialState(null); + } + catch (OrekitException oe) { + Assertions.assertEquals(oe.getSpecifier(), OrekitMessages.NON_RESETABLE_STATE); + } + } + + @Test + public void testPropagation() { + // Reference: Intelsat calculator for spacecraft 4521 (used 2023/12/07) + // https://www.intelsat.com/resources/tools/ + FieldIntelsatElevenElementsPropagator propagator = new FieldIntelsatElevenElementsPropagator<>(ELEMENTS); + double referenceLongitude170Hours = 301.9191; + double referenceLatitude170Hours = 0.0257; + double tolerance = 0.0001; + propagator.propagateInEcef(ELEMENTS.getEpoch().shiftedBy(170 * 3600.0)); + Assertions.assertEquals(referenceLongitude170Hours, propagator.getEastLongitudeDegrees().getValue().getReal(), tolerance); + Assertions.assertEquals(referenceLatitude170Hours, propagator.getGeocentricLatitudeDegrees().getValue().getReal(), tolerance); + } + + @Test + public void testOrbitElementsAtT0() { + // Reference use of the Intelsat's 11 elements propagator developed in STK + FieldIntelsatElevenElementsPropagator propagator = new FieldIntelsatElevenElementsPropagator<>(ELEMENTS, FramesFactory.getTOD(IERSConventions.IERS_2010, false), + FramesFactory.getITRF(IERSConventions.IERS_2010, false)); + KeplerianOrbit orbit = ((FieldKeplerianOrbit) OrbitType.KEPLERIAN.convertType( + propagator.propagateOrbit(ELEMENTS.getEpoch(), propagator.getParameters(ELEMENTS.getEpoch().getField())))).toOrbit(); + Assertions.assertEquals(302.0355, propagator.getEastLongitudeDegrees().getValue().getReal(), 0.0001); + Assertions.assertEquals(0.0378, propagator.getGeocentricLatitudeDegrees().getValue().getReal(), 0.0001); + Assertions.assertEquals(-1.529465e-6, propagator.getEastLongitudeDegrees().getFirstDerivative().getReal(), 1.0e-12); + Assertions.assertEquals(-1.01044e-7, propagator.getGeocentricLatitudeDegrees().getFirstDerivative().getReal(), 1.0e-12); + Assertions.assertEquals(42172456.005, propagator.getOrbitRadius().getValue().getReal(), 1.0e-3); + Assertions.assertEquals(0.797, propagator.getOrbitRadius().getFirstDerivative().getReal(), 1.0e-3); + Assertions.assertEquals(42166413.453, orbit.getA(), 4.0e-2); + Assertions.assertEquals(0.000296, orbit.getE(), 1.0e-6); + Assertions.assertEquals(0.037825, FastMath.toDegrees(orbit.getI()), 1.0e-6); + Assertions.assertEquals(282.488, FastMath.toDegrees(MathUtils.normalizeAngle(orbit.getRightAscensionOfAscendingNode(), FastMath.PI)), 4.0e-3); + Assertions.assertEquals(333.151, FastMath.toDegrees(MathUtils.normalizeAngle(orbit.getPerigeeArgument(), FastMath.PI)), 4.0e-3); + Assertions.assertEquals(118.919, FastMath.toDegrees(MathUtils.normalizeAngle(orbit.getAnomaly(PositionAngleType.MEAN), FastMath.PI)), 1.0e-3); + } + + @BeforeAll + public static void initialize() { + Binary64 zero = new Binary64(0.0); + // Reference elements from Intelsat website (spacecraft 4521) + ELEMENTS = new FieldIntelsatElevenElements<>(new FieldAbsoluteDate<>(zero.getField(), "2023-12-04T00:00:00.000", TimeScalesFactory.getUTC()), zero.add(302.0058), + zero.add(-0.0096), zero.add(-0.000629), zero.add(0.0297), zero.add(-0.0004), zero.add(-0.0194), zero.add(0.0007), + zero.add(0.0378), zero.add(-0.0018), zero.add(-0.0011), zero.add(0.0015)); + } +} \ No newline at end of file diff --git a/src/test/java/org/orekit/propagation/analytical/intelsat/IntelsatElevenElementsPropagatorTest.java b/src/test/java/org/orekit/propagation/analytical/intelsat/IntelsatElevenElementsPropagatorTest.java new file mode 100644 index 0000000000..a4eb7f7539 --- /dev/null +++ b/src/test/java/org/orekit/propagation/analytical/intelsat/IntelsatElevenElementsPropagatorTest.java @@ -0,0 +1,101 @@ +/* Copyright 2002-2024 Airbus Defence and Space + * 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. + * Airbus Defence and Space 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.intelsat; + +import org.hipparchus.util.FastMath; +import org.hipparchus.util.MathUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.orekit.Utils; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.KeplerianOrbit; +import org.orekit.orbits.OrbitType; +import org.orekit.orbits.PositionAngleType; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.TimeScalesFactory; +import org.orekit.utils.IERSConventions; + +class IntelsatElevenElementsPropagatorTest { + + private static IntelsatElevenElements ELEMENTS; + + @Test + public void testCannotResetIntermediateState() { + IntelsatElevenElementsPropagator propagator = new IntelsatElevenElementsPropagator(ELEMENTS); + try { + propagator.resetIntermediateState(null, false); + } + catch (OrekitException oe) { + Assertions.assertEquals(oe.getSpecifier(), OrekitMessages.NON_RESETABLE_STATE); + } + } + + @Test + public void testCannotResetInitialState() { + IntelsatElevenElementsPropagator propagator = new IntelsatElevenElementsPropagator(ELEMENTS); + try { + propagator.resetInitialState(null); + } + catch (OrekitException oe) { + Assertions.assertEquals(oe.getSpecifier(), OrekitMessages.NON_RESETABLE_STATE); + } + } + + @Test + public void testPropagation() { + // Reference: Intelsat calculator for spacecraft 4521 (used 2023/12/04) + // https://www.intelsat.com/resources/tools/ + IntelsatElevenElementsPropagator propagator = new IntelsatElevenElementsPropagator(ELEMENTS); + double referenceLongitude170Hours = 301.9191; + double referenceLatitude170Hours = 0.0257; + double tolerance = 0.0001; + propagator.propagateInEcef(ELEMENTS.getEpoch().shiftedBy(170 * 3600.0)); + Assertions.assertEquals(referenceLongitude170Hours, propagator.getEastLongitudeDegrees().getValue(), tolerance); + Assertions.assertEquals(referenceLatitude170Hours, propagator.getGeocentricLatitudeDegrees().getValue(), tolerance); + } + + @Test + public void testOrbitElementsAtT0() { + // Reference use of the Intelsat's 11 elements propagator developed in STK + IntelsatElevenElementsPropagator propagator = new IntelsatElevenElementsPropagator(ELEMENTS, FramesFactory.getTOD(IERSConventions.IERS_2010, false), + FramesFactory.getITRF(IERSConventions.IERS_2010, false)); + KeplerianOrbit orbit = (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(propagator.propagateOrbit(ELEMENTS.getEpoch())); + Assertions.assertEquals(302.0355, propagator.getEastLongitudeDegrees().getValue(), 0.0001); + Assertions.assertEquals(0.0378, propagator.getGeocentricLatitudeDegrees().getValue(), 0.0001); + Assertions.assertEquals(-1.529465e-6, propagator.getEastLongitudeDegrees().getFirstDerivative(), 1.0e-12); + Assertions.assertEquals(-1.01044e-7, propagator.getGeocentricLatitudeDegrees().getFirstDerivative(), 1.0e-12); + Assertions.assertEquals(42172456.005, propagator.getOrbitRadius().getValue(), 1.0e-3); + Assertions.assertEquals(0.797, propagator.getOrbitRadius().getFirstDerivative(), 1.0e-3); + Assertions.assertEquals(42166413.453, orbit.getA(), 4.0e-2); + Assertions.assertEquals(0.000296, orbit.getE(), 1.0e-6); + Assertions.assertEquals(0.037825, FastMath.toDegrees(orbit.getI()), 1.0e-6); + Assertions.assertEquals(282.488, FastMath.toDegrees(MathUtils.normalizeAngle(orbit.getRightAscensionOfAscendingNode(), FastMath.PI)), 4.0e-3); + Assertions.assertEquals(333.151, FastMath.toDegrees(MathUtils.normalizeAngle(orbit.getPerigeeArgument(), FastMath.PI)), 4.0e-3); + Assertions.assertEquals(118.919, FastMath.toDegrees(MathUtils.normalizeAngle(orbit.getAnomaly(PositionAngleType.MEAN), FastMath.PI)), 1.0e-3); + } + + @BeforeAll + public static void initialize() { + Utils.setDataRoot("regular-data"); + // Reference elements from Intelsat website (spacecraft 4521) + ELEMENTS = new IntelsatElevenElements(new AbsoluteDate("2023-12-04T00:00:00.000", TimeScalesFactory.getUTC()), 302.0058, -0.0096, -0.000629, 0.0297, -0.0004, -0.0194, + 0.0007, 0.0378, -0.0018, -0.0011, 0.0015); + } +} diff --git a/src/test/java/org/orekit/propagation/analytical/tle/FieldTLEPropagatorTest.java b/src/test/java/org/orekit/propagation/analytical/tle/FieldTLEPropagatorTest.java index 3d76426cf2..2cea0637ce 100644 --- a/src/test/java/org/orekit/propagation/analytical/tle/FieldTLEPropagatorTest.java +++ b/src/test/java/org/orekit/propagation/analytical/tle/FieldTLEPropagatorTest.java @@ -19,6 +19,8 @@ import org.hamcrest.MatcherAssert; import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; +import org.hipparchus.analysis.differentiation.Gradient; +import org.hipparchus.analysis.differentiation.GradientField; import org.hipparchus.geometry.euclidean.threed.FieldLine; import org.hipparchus.geometry.euclidean.threed.FieldRotation; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; @@ -288,11 +290,71 @@ public > void doTestComparisonWithNonField(Fie } + @Test + void testResetInitialState() { + // GIVEN + final FieldTLE tle = getGradientTLE(); + final Field field = tle.getDate().getField(); + final Gradient[] parameters = tle.getParameters(field); + final FieldTLEPropagator tlePropagator = FieldTLEPropagator.selectExtrapolator(tle, parameters); + final FieldSpacecraftState initialState = tlePropagator.getInitialState(); + final Gradient unexpectedMass = initialState.getMass(); + final Gradient expectedMass = unexpectedMass.multiply(2); + final FieldSpacecraftState newState = new FieldSpacecraftState<>(initialState.getOrbit(), + initialState.getAttitude(), expectedMass); + + // WHEN + tlePropagator.resetInitialState(newState); + + // THEN + final FieldSpacecraftState actualState = tlePropagator.getInitialState(); + Assertions.assertEquals(expectedMass.getReal(), tlePropagator.getMass(actualState.getDate()).getReal()); + Assertions.assertEquals(expectedMass.getReal(), actualState.getMass().getReal()); + Assertions.assertNotEquals(unexpectedMass.getReal(), actualState.getMass().getReal()); + } + + private FieldTLE getGradientTLE() { + final String line1 = "1 37753U 11036A 12090.13205652 -.00000006 00000-0 00000+0 0 2272"; + final String line2 = "2 37753 55.0032 176.5796 0004733 13.2285 346.8266 2.00565440 5153"; + final GradientField field = GradientField.getField(1); + return new FieldTLE<>(field, line1, line2); + } + + @Test + void testResetIntermediateStateForward() { + testResetIntermediateStateTemplate(true); + } + + @Test + void testResetIntermediateStateBackward() { + testResetIntermediateStateTemplate(false); + } + + void testResetIntermediateStateTemplate(final boolean isForward) { + // GIVEN + final FieldTLE tle = getGradientTLE(); + final Field field = tle.getDate().getField(); + final Gradient[] parameters = tle.getParameters(field); + final FieldTLEPropagator tlePropagator = FieldTLEPropagator.selectExtrapolator(tle, parameters); + final double expectedMass = 2000.; + final FieldSpacecraftState propagatedState = tlePropagator.propagate(tle.getDate().shiftedBy(1)); + final FieldSpacecraftState modifiedState = new FieldSpacecraftState<>(propagatedState.getOrbit(), + field.getZero().newInstance(expectedMass)); + + // WHEN + tlePropagator.resetIntermediateState(modifiedState, isForward); + + // THEN + final double tinyTimeShift = (isForward) ? 1e-3 : -1e-3; + final double actualMass = tlePropagator.getMass(modifiedState.getDate().shiftedBy(tinyTimeShift)).getReal(); + Assertions.assertEquals(expectedMass, actualMass); + } + @BeforeEach public void setUp() { Utils.setDataRoot("regular-data"); - // the period of the GPS satellite + // the period of the GPS satellite period = 717.97 * 60.0; } diff --git a/src/test/java/org/orekit/propagation/analytical/tle/TLEParametersDerivativesTest.java b/src/test/java/org/orekit/propagation/analytical/tle/TLEParametersDerivativesTest.java index 67c0b2747e..8affcce50f 100644 --- a/src/test/java/org/orekit/propagation/analytical/tle/TLEParametersDerivativesTest.java +++ b/src/test/java/org/orekit/propagation/analytical/tle/TLEParametersDerivativesTest.java @@ -1,3 +1,20 @@ +/* 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.analytical.tle; import org.hipparchus.linear.RealMatrix; diff --git a/src/test/java/org/orekit/propagation/analytical/tle/TLEPropagatorTest.java b/src/test/java/org/orekit/propagation/analytical/tle/TLEPropagatorTest.java index 268713f223..d1031f03d1 100644 --- a/src/test/java/org/orekit/propagation/analytical/tle/TLEPropagatorTest.java +++ b/src/test/java/org/orekit/propagation/analytical/tle/TLEPropagatorTest.java @@ -20,6 +20,7 @@ import org.hipparchus.geometry.euclidean.threed.Line; import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.ode.events.Action; import org.hipparchus.util.FastMath; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -35,6 +36,8 @@ import org.orekit.propagation.EphemerisGenerator; import org.orekit.propagation.Propagator; import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.events.DateDetector; +import org.orekit.propagation.events.handlers.EventHandler; import org.orekit.propagation.sampling.OrekitFixedStepHandler; import org.orekit.time.AbsoluteDate; import org.orekit.utils.Constants; @@ -188,6 +191,87 @@ public void handleStep(SpacecraftState currentState) { } + @Test + void testResetInitialState() { + // GIVEN + final TLEPropagator tlePropagator = TLEPropagator.selectExtrapolator(tle); + final SpacecraftState initialState = tlePropagator.getInitialState(); + final double unexpectedMass = initialState.getMass(); + final double expectedMass = 2. * unexpectedMass; + final SpacecraftState newState = new SpacecraftState(initialState.getOrbit(), initialState.getAttitude(), + expectedMass); + + // WHEN + tlePropagator.resetInitialState(newState); + + // THEN + final SpacecraftState actualState = tlePropagator.getInitialState(); + Assertions.assertEquals(expectedMass, tlePropagator.getMass(actualState.getDate())); + Assertions.assertEquals(expectedMass, actualState.getMass()); + Assertions.assertNotEquals(unexpectedMass, actualState.getMass()); + } + + @Test + void testResetIntermediateStateForward() { + testResetIntermediateStateTemplate(true); + } + + @Test + void testResetIntermediateStateBackward() { + testResetIntermediateStateTemplate(false); + } + + void testResetIntermediateStateTemplate(final boolean isForward) { + // GIVEN + final TLEPropagator tlePropagator = TLEPropagator.selectExtrapolator(tle); + final double expectedMass = 2000.; + final SpacecraftState propagatedState = tlePropagator.propagate(tle.getDate().shiftedBy(1)); + final SpacecraftState modifiedState = new SpacecraftState(propagatedState.getOrbit(), expectedMass); + + // WHEN + tlePropagator.resetIntermediateState(modifiedState, isForward); + + // THEN + final double tinyTimeShift = (isForward) ? 1e-3 : -1e-3; + final double actualMass = tlePropagator.getMass(modifiedState.getDate().shiftedBy(tinyTimeShift)); + Assertions.assertEquals(expectedMass, actualMass); + } + + @Test + void testResetIntermediateStateHighLevelForward() { + testResetIntermediateStateHighLevelTemplate(true); + } + + @Test + void testResetIntermediateStateHighLevelBackward() { + testResetIntermediateStateHighLevelTemplate(false); + } + + void testResetIntermediateStateHighLevelTemplate(final boolean isForward) { + // GIVEN + final TLEPropagator tlePropagator = TLEPropagator.selectExtrapolator(tle); + final AbsoluteDate epoch = tlePropagator.getInitialState().getDate(); + final double totalShift = 1e4; + final double shiftSign = isForward ? 1 : -1; + final AbsoluteDate targetDate = epoch.shiftedBy(totalShift * shiftSign); + final EventHandler stateResetter = (s, detector, increasing) -> Action.RESET_STATE; + final DateDetector detector = new DateDetector(targetDate) + .withThreshold(1e-8).withHandler(stateResetter); + tlePropagator.addEventDetector(detector); + + // WHEN + final SpacecraftState actualState = tlePropagator.propagate(targetDate); + + // THEN + final SpacecraftState expectedState = TLEPropagator.selectExtrapolator(tle).propagate(targetDate); + final Vector3D expectedPosition = expectedState.getPosition(); + final Vector3D actualPosition = actualState.getPosition(); + final double tolerance = 1e-1; + Assertions.assertEquals(expectedPosition.getX(), actualPosition.getX(), tolerance); + Assertions.assertEquals(expectedPosition.getY(), actualPosition.getY(), tolerance); + Assertions.assertEquals(expectedPosition.getZ(), actualPosition.getZ(), tolerance); + } + @BeforeEach public void setUp() { Utils.setDataRoot("regular-data"); diff --git a/src/test/java/org/orekit/propagation/analytical/tle/TLEStateTransitionMatrixTest.java b/src/test/java/org/orekit/propagation/analytical/tle/TLEStateTransitionMatrixTest.java index 32415802ab..a52b260fe7 100644 --- a/src/test/java/org/orekit/propagation/analytical/tle/TLEStateTransitionMatrixTest.java +++ b/src/test/java/org/orekit/propagation/analytical/tle/TLEStateTransitionMatrixTest.java @@ -1,3 +1,20 @@ +/* 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.analytical.tle; import org.hipparchus.linear.RealMatrix; 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 d92089f933..eba949fd33 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,11 +20,13 @@ 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; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.orekit.TestUtils; import org.orekit.Utils; import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; @@ -37,8 +39,11 @@ 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; @@ -324,4 +329,31 @@ private > void checkConversion(final FieldTLE< Assertions.assertEquals(tle.getMeanAnomaly().getReal(), converted.getMeanAnomaly().getReal(), threshold * tle.getMeanAnomaly().getReal()); Assertions.assertEquals(tle.getBStar(), converted.getBStar(), threshold * tle.getBStar()); } + + @Test + public void testIssue1408() { + // The result of the TLE generation shall not be affected by the value of the gravitational + // parameter of the input orbit. + + final TleGenerationAlgorithm algorithm = new FixedPointTleGenerationAlgorithm(); + + // Initial TLE + final TLE initialTle = new TLE("1 31135U 07013A 11003.00000000 .00000816 00000-0 47577-4 0 12", + "2 31135 2.4656 183.9084 0021119 236.4164 60.4567 15.10546832 15"); + final SpacecraftState expectedState = TLEPropagator.selectExtrapolator(initialTle).getInitialState(); + final TimeStampedPVCoordinates expectedPV = expectedState.getPVCoordinates(); + + // Create a new orbit using the position and velocity taken from the TLE, but using + // a gravitational parameter mu different from the TLE mu. + final CartesianOrbit orbit = new CartesianOrbit(expectedState.getOrbit().getPVCoordinates(), expectedState.getFrame(), Constants.EGM96_EARTH_MU); + + // Generate a TLE based on the orbit and check that the generated TLE is the same as the + // original one. + final TLE generatedTle = TLE.stateToTLE(new SpacecraftState(orbit), initialTle, algorithm); + Assertions.assertEquals(initialTle.getLine1(), generatedTle.getLine1()); + Assertions.assertEquals(initialTle.getLine2(), generatedTle.getLine2()); + final TimeStampedPVCoordinates actualPvCoordinates = TLEPropagator.selectExtrapolator(generatedTle).getInitialState().getPVCoordinates(); + TestUtils.validateVector3D(expectedPV.getPosition(), actualPvCoordinates.getPosition(), 1.0e-4); + TestUtils.validateVector3D(expectedPV.getVelocity(), actualPvCoordinates.getVelocity(), 1.0e-4); + } } diff --git a/src/test/java/org/orekit/propagation/conversion/BrouwerLyddanePropagatorBuilderTest.java b/src/test/java/org/orekit/propagation/conversion/BrouwerLyddanePropagatorBuilderTest.java index 9fc429b287..ccb9056807 100644 --- a/src/test/java/org/orekit/propagation/conversion/BrouwerLyddanePropagatorBuilderTest.java +++ b/src/test/java/org/orekit/propagation/conversion/BrouwerLyddanePropagatorBuilderTest.java @@ -16,6 +16,8 @@ */ package org.orekit.propagation.conversion; +import static org.orekit.propagation.conversion.AbstractPropagatorBuilderTest.assertPropagatorBuilderIsACopy; + import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.util.FastMath; import org.junit.jupiter.api.Assertions; @@ -35,14 +37,13 @@ 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.time.AbsoluteDate; import org.orekit.utils.Constants; import org.orekit.utils.PVCoordinates; import org.orekit.utils.ParameterDriver; -import static org.orekit.propagation.conversion.AbstractPropagatorBuilderTest.assertPropagatorBuilderIsACopy; - public class BrouwerLyddanePropagatorBuilderTest { private Orbit orbit; @@ -77,7 +78,7 @@ public void doTestBuildPropagator() { 1.0, BrouwerLyddanePropagator.M2); - final BrouwerLyddanePropagator prop = builder.buildPropagator(builder.getSelectedNormalizedParameters()); + final Propagator prop = builder.buildPropagator(); final Orbit orbitWithBuilder = prop.propagate(initDate.shiftedBy(60000)).getOrbit(); // Verify @@ -121,7 +122,7 @@ public void doTestBuildPropagatorWithDrag() { } // Build the propagator - final BrouwerLyddanePropagator prop = builder.buildPropagator(builder.getSelectedNormalizedParameters()); + final BrouwerLyddanePropagator prop = (BrouwerLyddanePropagator) builder.buildPropagator(); // Verify Assertions.assertEquals(M2, prop.getM2(), Double.MIN_VALUE); diff --git a/src/test/java/org/orekit/propagation/conversion/DSSTPropagatorBuilderTest.java b/src/test/java/org/orekit/propagation/conversion/DSSTPropagatorBuilderTest.java index 167bf6b79f..c06ce5893e 100644 --- a/src/test/java/org/orekit/propagation/conversion/DSSTPropagatorBuilderTest.java +++ b/src/test/java/org/orekit/propagation/conversion/DSSTPropagatorBuilderTest.java @@ -16,6 +16,8 @@ */ package org.orekit.propagation.conversion; +import static org.orekit.propagation.conversion.AbstractPropagatorBuilderTest.assertPropagatorBuilderIsACopy; + import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.ode.nonstiff.DormandPrince853Integrator; import org.junit.jupiter.api.Assertions; @@ -53,8 +55,6 @@ import java.text.ParseException; import java.util.List; -import static org.orekit.propagation.conversion.AbstractPropagatorBuilderTest.assertPropagatorBuilderIsACopy; - public class DSSTPropagatorBuilderTest { private static final double eps = 2.0e-10; @@ -214,7 +214,7 @@ private void doTestBuildPropagator(final ODEIntegratorBuilder foiBuilder) { builder.addForceModel(moon); builder.setMass(1000.); - final DSSTPropagator prop = builder.buildPropagator(builder.getSelectedNormalizedParameters()); + final DSSTPropagator prop = (DSSTPropagator) builder.buildPropagator(); final Orbit orbitWithBuilder = prop.propagate(initDate.shiftedBy(600)).getOrbit(); @@ -242,7 +242,7 @@ public void testIssue598() { // Verify that there is no Newtonian attraction force model Assertions.assertFalse(hasNewtonianAttraction(builder.getAllForceModels())); // Build the DSST propagator (not used here) - builder.buildPropagator(builder.getSelectedNormalizedParameters()); + builder.buildPropagator(); // Verify the addition of the Newtonian attraction force model Assertions.assertTrue(hasNewtonianAttraction(builder.getAllForceModels())); // Add a new force model to ensure the Newtonian attraction stay at the last position @@ -298,7 +298,7 @@ public CombinedDerivatives combinedDerivatives(SpacecraftState s) { try { // Build the propagator - builder.buildPropagator(builder.getSelectedNormalizedParameters()); + builder.buildPropagator(); Assertions.fail("an exception should have been thrown"); } catch (OrekitException oe) { Assertions.assertEquals(oe.getSpecifier(), OrekitMessages.ADDITIONAL_STATE_NAME_ALREADY_IN_USE); diff --git a/src/test/java/org/orekit/propagation/conversion/EphemerisPropagatorBuilderTest.java b/src/test/java/org/orekit/propagation/conversion/EphemerisPropagatorBuilderTest.java index 880791b0cc..135680cc59 100644 --- a/src/test/java/org/orekit/propagation/conversion/EphemerisPropagatorBuilderTest.java +++ b/src/test/java/org/orekit/propagation/conversion/EphemerisPropagatorBuilderTest.java @@ -17,11 +17,13 @@ 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.mockito.Mockito; +import org.orekit.TestUtils; import org.orekit.attitudes.AttitudeProvider; import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; @@ -36,9 +38,12 @@ import org.orekit.utils.PVCoordinates; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; -import static org.orekit.propagation.conversion.AbstractPropagatorBuilderTest.assertPropagatorBuilderIsACopy; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; /** * Unit tests for {@link EphemerisPropagatorBuilder}. @@ -46,6 +51,46 @@ * @author Vincent Cucchietti */ public class EphemerisPropagatorBuilderTest { + + @Test + @DisplayName("Test issue 1316 : Regression in EphemerisPropagatorBuilder API") + void testIssue1316() { + // GIVEN + final int interpolationPoints = 3; + final double extrapolationThresholds = 0.007; + + // Create mock attitude provider + final AttitudeProvider mockAttitudeProvider = mock(AttitudeProvider.class); + + // Get default orbit + final Orbit defaultOrbit = TestUtils.getDefaultOrbit(new AbsoluteDate()); + + // Create fake list of states + final SpacecraftState mockState1 = mock(SpacecraftState.class); + when(mockState1.getFrame()).thenReturn(defaultOrbit.getFrame()); + when(mockState1.getOrbit()).thenReturn(defaultOrbit); + final SpacecraftState mockState2 = mock(SpacecraftState.class); + final SpacecraftState mockState3 = mock(SpacecraftState.class); + + final List fakeStates = Arrays.asList(mockState1, mockState2, mockState3); + + // WHEN + final EphemerisPropagatorBuilder builder = new EphemerisPropagatorBuilder(fakeStates, + interpolationPoints, + extrapolationThresholds, + mockAttitudeProvider); + + // THEN + // Assert initial orbit + assertEquals(defaultOrbit.getFrame(), builder.getFrame()); + assertEquals(defaultOrbit.getType(), builder.getOrbitType()); + assertEquals(defaultOrbit.getDate(), builder.getInitialOrbitDate()); + assertEquals(defaultOrbit.getMu(), builder.getMu()); + + // Assert attitude provider + assertEquals(mockAttitudeProvider, builder.getAttitudeProvider()); + } + @Test @DisplayName("Test buildPropagator method") void should_create_expected_propagator() { @@ -66,14 +111,14 @@ void should_create_expected_propagator() { new EphemerisPropagatorBuilder(states, stateInterpolator); // When - final Ephemeris builtPropagator = (Ephemeris) builder.buildPropagator(builder.getSelectedNormalizedParameters()); + final Ephemeris builtPropagator = (Ephemeris) builder.buildPropagator(); // Then final Ephemeris expectedPropagator = new Ephemeris(states, stateInterpolator); - Assertions.assertEquals(expectedPropagator.getFrame(), builtPropagator.getFrame()); - Assertions.assertEquals(expectedPropagator.getMinDate(), builtPropagator.getMinDate()); - Assertions.assertEquals(expectedPropagator.getMaxDate(), builtPropagator.getMaxDate()); + assertEquals(expectedPropagator.getFrame(), builtPropagator.getFrame()); + assertEquals(expectedPropagator.getMinDate(), builtPropagator.getMinDate()); + assertEquals(expectedPropagator.getMaxDate(), builtPropagator.getMaxDate()); Assertions.assertArrayEquals(expectedPropagator.getManagedAdditionalStates(), builtPropagator.getManagedAdditionalStates()); @@ -96,7 +141,7 @@ void testCopyMethod() { final Frame frame = FramesFactory.getGCRF(); final TimeInterpolator stateInterpolator = new SpacecraftStateInterpolator(frame); - final AttitudeProvider attitudeProvider = Mockito.mock(AttitudeProvider.class); + final AttitudeProvider attitudeProvider = mock(AttitudeProvider.class); final EphemerisPropagatorBuilder builder = new EphemerisPropagatorBuilder(states, stateInterpolator, attitudeProvider); diff --git a/src/test/java/org/orekit/propagation/conversion/KeplerianConverterTest.java b/src/test/java/org/orekit/propagation/conversion/KeplerianConverterTest.java index 79d621ff69..4c0fca10a2 100644 --- a/src/test/java/org/orekit/propagation/conversion/KeplerianConverterTest.java +++ b/src/test/java/org/orekit/propagation/conversion/KeplerianConverterTest.java @@ -36,7 +36,7 @@ import java.util.ArrayList; import java.util.List; -public class KeplerianConverterTest { +class KeplerianConverterTest { private Orbit orbit; @@ -45,17 +45,17 @@ public class KeplerianConverterTest { private final static double mu = 3.9860047e14; @Test - public void testConversionPositionVelocity() { - checkFit(orbit, 86400, 300, 1.0e-3, false, 1.901e-8); + void testConversionPositionVelocity() { + checkFit(orbit, 86400, 300, 1.0e-3, false, 7.812e-9); } @Test - public void testConversionPositionOnly() { - checkFit(orbit, 86400, 300, 1.0e-3, true, 2.691e-8); + void testConversionPositionOnly() { + checkFit(orbit, 86400, 300, 1.0e-3, true, 2.337e-8); } @Test - public void testConversionWithFreeParameter() { + void testConversionWithFreeParameter() { Assertions.assertThrows(OrekitException.class, () -> { checkFit(orbit, 86400, 300, 1.0e-3, true, 2.65e-8, "toto"); }); diff --git a/src/test/java/org/orekit/propagation/conversion/NumericalConverterTest.java b/src/test/java/org/orekit/propagation/conversion/NumericalConverterTest.java index ab020c995e..fc9e81905b 100644 --- a/src/test/java/org/orekit/propagation/conversion/NumericalConverterTest.java +++ b/src/test/java/org/orekit/propagation/conversion/NumericalConverterTest.java @@ -82,7 +82,7 @@ public void testIssue598() { // Verify that there is no Newtonian attraction force model Assertions.assertFalse(hasNewtonianAttraction(builder.getAllForceModels())); // Build the Numerical propagator (not used here) - builder.buildPropagator(builder.getSelectedNormalizedParameters()); + builder.buildPropagator(); // Verify the addition of the Newtonian attraction force model Assertions.assertTrue(hasNewtonianAttraction(builder.getAllForceModels())); // Add a new force model to ensure the Newtonian attraction stay at the last position @@ -255,7 +255,7 @@ public CombinedDerivatives combinedDerivatives(SpacecraftState s) { try { // Build the numerical propagator - builder.buildPropagator(builder.getSelectedNormalizedParameters()); + builder.buildPropagator(); Assertions.fail("an exception should have been thrown"); } catch (OrekitException oe) { Assertions.assertEquals(oe.getSpecifier(), OrekitMessages.ADDITIONAL_STATE_NAME_ALREADY_IN_USE); diff --git a/src/test/java/org/orekit/propagation/conversion/TLEConverterTest.java b/src/test/java/org/orekit/propagation/conversion/TLEConverterTest.java index b79e9c9c55..55cc35f510 100644 --- a/src/test/java/org/orekit/propagation/conversion/TLEConverterTest.java +++ b/src/test/java/org/orekit/propagation/conversion/TLEConverterTest.java @@ -60,7 +60,7 @@ public void testIssue859() { final TLEPropagatorBuilder propagatorBuilderError = new TLEPropagatorBuilder(tle, PositionAngleType.MEAN, 1., new FixedPointTleGenerationAlgorithm()); try { - propagatorBuilderError.buildPropagator(propagatorBuilderError.getSelectedNormalizedParameters()); + propagatorBuilderError.buildPropagator(); } catch (OrekitException oe) { Assertions.assertEquals(OrekitMessages.UNABLE_TO_COMPUTE_TLE, oe.getSpecifier()); } diff --git a/src/test/java/org/orekit/propagation/conversion/averaging/AbstractHarmonicsBasedOrbitalStateTest.java b/src/test/java/org/orekit/propagation/conversion/averaging/AbstractHarmonicsBasedOrbitalStateTest.java new file mode 100644 index 0000000000..0b1dfbbb1d --- /dev/null +++ b/src/test/java/org/orekit/propagation/conversion/averaging/AbstractHarmonicsBasedOrbitalStateTest.java @@ -0,0 +1,58 @@ +package org.orekit.propagation.conversion.averaging; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.OrbitType; +import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.conversion.averaging.elements.AveragedOrbitalElements; +import org.orekit.time.AbsoluteDate; + +class AbstractHarmonicsBasedOrbitalStateTest { + + @Test + void testGetMu() { + // GIVEN + final double expectedMu = 1.; + final UnnormalizedSphericalHarmonicsProvider mockedProvider = Mockito + .mock(UnnormalizedSphericalHarmonicsProvider.class); + Mockito.when(mockedProvider.getMu()).thenReturn(expectedMu); + final TestHarmonicsBasedOrbitalSTate elements = new TestHarmonicsBasedOrbitalSTate(mockedProvider); + // WHEN + final double actualMu = elements.getMu(); + // THEN + Assertions.assertEquals(expectedMu, actualMu); + } + + private static class TestHarmonicsBasedOrbitalSTate + extends AbstractHarmonicsBasedOrbitalState { + + protected TestHarmonicsBasedOrbitalSTate(UnnormalizedSphericalHarmonicsProvider harmonicsProvider) { + super(AbsoluteDate.ARBITRARY_EPOCH, FramesFactory.getGCRF(), harmonicsProvider); + } + + @Override + public AveragedOrbitalElements getAveragedElements() { + return null; + } + + @Override + public OrbitType getOrbitType() { + return null; + } + + @Override + public PositionAngleType getPositionAngleType() { + return null; + } + + @Override + public Orbit toOsculatingOrbit() { + return null; + } + } + +} diff --git a/src/test/java/org/orekit/propagation/conversion/averaging/BrouwerLyddaneOrbitalStateTest.java b/src/test/java/org/orekit/propagation/conversion/averaging/BrouwerLyddaneOrbitalStateTest.java new file mode 100644 index 0000000000..a47bcf56e9 --- /dev/null +++ b/src/test/java/org/orekit/propagation/conversion/averaging/BrouwerLyddaneOrbitalStateTest.java @@ -0,0 +1,58 @@ +package org.orekit.propagation.conversion.averaging; + +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.data.DataContext; +import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.KeplerianOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.conversion.averaging.elements.AveragedKeplerianWithMeanAngle; +import org.orekit.time.AbsoluteDate; + +class BrouwerLyddaneOrbitalStateTest { + + @BeforeEach + public void setUp() { + Utils.setDataRoot("regular-data:potential"); + } + + @Test + void testToOsculating() { + // GIVEN + final UnnormalizedSphericalHarmonicsProvider provider = getProvider(5); + final BrouwerLyddaneOrbitalState averagedState = new BrouwerLyddaneOrbitalState(AbsoluteDate.ARBITRARY_EPOCH, + new AveragedKeplerianWithMeanAngle(1e7, 0.1, 1., 2., 3., + -1.), FramesFactory.getGCRF(), provider); + // WHEN + final Orbit orbit = averagedState.toOsculatingOrbit(); + final KeplerianOrbit keplerianOrbit = (KeplerianOrbit) averagedState.getOrbitType() + .convertType(orbit); + // THEN + Assertions.assertEquals(averagedState.getDate(), keplerianOrbit.getDate()); + compareOrbitalElements(averagedState.getAveragedElements(), keplerianOrbit, + averagedState.getPositionAngleType()); + } + + private void compareOrbitalElements(final AveragedKeplerianWithMeanAngle elements, + final KeplerianOrbit keplerianOrbit, + final PositionAngleType positionAngleType) { + Assertions.assertEquals(elements.getAveragedSemiMajorAxis(), keplerianOrbit.getA(), 1e4); + Assertions.assertEquals(elements.getAveragedEccentricity(), keplerianOrbit.getE(), 1e-3); + Assertions.assertEquals(elements.getAveragedInclination(), keplerianOrbit.getI(), 1e-3); + Assertions.assertEquals(elements.getAveragedPerigeeArgument(), + keplerianOrbit.getPerigeeArgument(), 1e-2); + Assertions.assertEquals(elements.getAveragedRightAscensionOfTheAscendingNode(), + keplerianOrbit.getRightAscensionOfAscendingNode(), 1e-3); + Assertions.assertEquals(elements.getAveragedMeanAnomaly(), + keplerianOrbit.getAnomaly(positionAngleType), 1e-2); + } + + private UnnormalizedSphericalHarmonicsProvider getProvider(final int maxDegree) { + return DataContext.getDefault().getGravityFields().getUnnormalizedProvider(maxDegree, 0); + } + +} diff --git a/src/test/java/org/orekit/propagation/conversion/averaging/DSST6X0OrbitalStateTest.java b/src/test/java/org/orekit/propagation/conversion/averaging/DSST6X0OrbitalStateTest.java new file mode 100644 index 0000000000..8d4aa7dbbb --- /dev/null +++ b/src/test/java/org/orekit/propagation/conversion/averaging/DSST6X0OrbitalStateTest.java @@ -0,0 +1,59 @@ +package org.orekit.propagation.conversion.averaging; + +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.data.DataContext; +import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.EquinoctialOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.conversion.averaging.elements.AveragedEquinoctialWithMeanAngle; +import org.orekit.time.AbsoluteDate; + +class DSST6X0OrbitalStateTest { + + @BeforeEach + public void setUp() { + Utils.setDataRoot("regular-data:potential"); + } + + @Test + void testToOsculating() { + // GIVEN + final UnnormalizedSphericalHarmonicsProvider provider = getProvider(); + final DSST6X0OrbitalState averagedState = new DSST6X0OrbitalState(AbsoluteDate.ARBITRARY_EPOCH, + new AveragedEquinoctialWithMeanAngle(1e7, 0.1, 0., 0.2, -0.3, + -1.), FramesFactory.getGCRF(), provider); + // WHEN + final Orbit orbit = averagedState.toOsculatingOrbit(); + final EquinoctialOrbit equinoctialOrbit = (EquinoctialOrbit) averagedState.getOrbitType().convertType(orbit); + // THEN + Assertions.assertEquals(averagedState.getDate(), equinoctialOrbit.getDate()); + compareOrbitalElements(averagedState.getAveragedElements(), equinoctialOrbit, + averagedState.getPositionAngleType()); + } + + private void compareOrbitalElements(final AveragedEquinoctialWithMeanAngle elements, + final EquinoctialOrbit equinoctialOrbit, + final PositionAngleType positionAngleType) { + Assertions.assertEquals(elements.getAveragedSemiMajorAxis(), equinoctialOrbit.getA(), 1.6e4); + Assertions.assertEquals(elements.getAveragedEquinoctialEx(), + equinoctialOrbit.getEquinoctialEx(), 1.e-3); + Assertions.assertEquals(elements.getAveragedEquinoctialEy(), + equinoctialOrbit.getEquinoctialEy(), 2.5e-3); + Assertions.assertEquals(elements.getAveragedHx(), + equinoctialOrbit.getHx(), 4.e-3); + Assertions.assertEquals(elements.getAveragedHy(), + equinoctialOrbit.getHy(), 1.e-3); + Assertions.assertEquals(elements.getAveragedMeanLongitudeArgument(), + equinoctialOrbit.getL(positionAngleType), 1.e-3); + } + + private UnnormalizedSphericalHarmonicsProvider getProvider() { + return DataContext.getDefault().getGravityFields().getUnnormalizedProvider(6, 0); + } + +} diff --git a/src/test/java/org/orekit/propagation/conversion/averaging/EcksteinHechlerOrbitalStateTest.java b/src/test/java/org/orekit/propagation/conversion/averaging/EcksteinHechlerOrbitalStateTest.java new file mode 100644 index 0000000000..a059506cfc --- /dev/null +++ b/src/test/java/org/orekit/propagation/conversion/averaging/EcksteinHechlerOrbitalStateTest.java @@ -0,0 +1,56 @@ +package org.orekit.propagation.conversion.averaging; + +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.data.DataContext; +import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.CircularOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.conversion.averaging.elements.AveragedCircularWithMeanAngle; +import org.orekit.time.AbsoluteDate; + +class EcksteinHechlerOrbitalStateTest { + + @BeforeEach + public void setUp() { + Utils.setDataRoot("regular-data:potential"); + } + + @Test + void testToOsculating() { + // GIVEN + final UnnormalizedSphericalHarmonicsProvider provider = getProvider(); + final EcksteinHechlerOrbitalState averagedState = new EcksteinHechlerOrbitalState(AbsoluteDate.ARBITRARY_EPOCH, + new AveragedCircularWithMeanAngle(1e7, -0.01, 0., 0.01, 0., + 0.), FramesFactory.getGCRF(), provider); + // WHEN + final Orbit orbit = averagedState.toOsculatingOrbit(); + final CircularOrbit circularOrbit = (CircularOrbit) averagedState.getOrbitType().convertType(orbit); + // THEN + Assertions.assertEquals(averagedState.getDate(), circularOrbit.getDate()); + compareOrbitalElements(averagedState.getAveragedElements(), circularOrbit, + averagedState.getPositionAngleType()); + } + + private void compareOrbitalElements(final AveragedCircularWithMeanAngle elements, + final CircularOrbit circularOrbit, + final PositionAngleType positionAngleType) { + Assertions.assertEquals(elements.getAveragedSemiMajorAxis(), circularOrbit.getA(), 1e4); + Assertions.assertEquals(elements.getAveragedCircularEx(), circularOrbit.getCircularEx(), 1e-3); + Assertions.assertEquals(elements.getAveragedCircularEy(), circularOrbit.getCircularEy(), 1e-3); + Assertions.assertEquals(elements.getAveragedInclination(), circularOrbit.getI(), 1e-3); + Assertions.assertEquals(elements.getAveragedRightAscensionOfTheAscendingNode(), + circularOrbit.getRightAscensionOfAscendingNode(), 1e-3); + Assertions.assertEquals(elements.getAveragedMeanLatitudeArgument(), + circularOrbit.getAlpha(positionAngleType), 1.1e-3); + } + + private UnnormalizedSphericalHarmonicsProvider getProvider() { + return DataContext.getDefault().getGravityFields().getUnnormalizedProvider(6, 0); + } + +} diff --git a/src/test/java/org/orekit/propagation/conversion/averaging/SGP4OrbitalStateTest.java b/src/test/java/org/orekit/propagation/conversion/averaging/SGP4OrbitalStateTest.java new file mode 100644 index 0000000000..1f72d0416e --- /dev/null +++ b/src/test/java/org/orekit/propagation/conversion/averaging/SGP4OrbitalStateTest.java @@ -0,0 +1,69 @@ +package org.orekit.propagation.conversion.averaging; + +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.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.analytical.tle.TLE; +import org.orekit.propagation.conversion.averaging.elements.AveragedKeplerianWithMeanAngle; +import org.orekit.time.AbsoluteDate; + +class SGP4OrbitalStateTest { + + @BeforeEach + public void setUp() { + Utils.setDataRoot("regular-data"); + } + + @Test + void testToOsculating() { + final SGP4OrbitalState averagedState = new SGP4OrbitalState(AbsoluteDate.ARBITRARY_EPOCH, + new AveragedKeplerianWithMeanAngle(1e7, 0.1, 1., 2., 3., -1.)); + // WHEN + final Orbit orbit = averagedState.toOsculatingOrbit(); + final KeplerianOrbit keplerianOrbit = (KeplerianOrbit) averagedState.getOrbitType().convertType(orbit); + // THEN + Assertions.assertEquals(averagedState.getDate(), keplerianOrbit.getDate()); + Assertions.assertEquals(averagedState.getFrame(), keplerianOrbit.getFrame()); + compareOrbitalElements(averagedState.getAveragedElements(), keplerianOrbit, + averagedState.getPositionAngleType()); + } + + private void compareOrbitalElements(final AveragedKeplerianWithMeanAngle elements, + final KeplerianOrbit keplerianOrbit, + final PositionAngleType positionAngleType) { + Assertions.assertEquals(elements.getAveragedSemiMajorAxis(), keplerianOrbit.getA(), 1e3); + Assertions.assertEquals(elements.getAveragedEccentricity(), keplerianOrbit.getE(), 1e-3); + Assertions.assertEquals(elements.getAveragedInclination(), keplerianOrbit.getI(), 1e-3); + Assertions.assertEquals(elements.getAveragedPerigeeArgument(), + keplerianOrbit.getPerigeeArgument(), 1e-2); + Assertions.assertEquals(elements.getAveragedRightAscensionOfTheAscendingNode(), + keplerianOrbit.getRightAscensionOfAscendingNode(), 1e-3); + Assertions.assertEquals(elements.getAveragedMeanAnomaly(), + keplerianOrbit.getAnomaly(positionAngleType), 1e-2); + } + + @Test + void testOf() { + // GIVEN + final String line1SPOT = "1 22823U 93061A 03339.49496229 .00000173 00000-0 10336-3 0 133"; + final String line2SPOT = "2 22823 98.4132 359.2998 0017888 100.4310 259.8872 14.18403464527664"; + final TLE tle = new TLE(line1SPOT, line2SPOT); + final Frame teme = FramesFactory.getTEME(); + // WHEN + final SGP4OrbitalState orbitalState = SGP4OrbitalState.of(tle, teme); + // THEN + final AveragedKeplerianWithMeanAngle elements = orbitalState.getAveragedElements(); + Assertions.assertEquals(tle.getE(), elements.getAveragedEccentricity()); + Assertions.assertEquals(tle.getI(), elements.getAveragedInclination()); + Assertions.assertEquals(tle.getRaan(), elements.getAveragedRightAscensionOfTheAscendingNode()); + Assertions.assertEquals(tle.getPerigeeArgument(), elements.getAveragedPerigeeArgument()); + Assertions.assertEquals(tle.getMeanAnomaly(), elements.getAveragedMeanAnomaly()); + } + +} diff --git a/src/test/java/org/orekit/propagation/conversion/averaging/converters/OsculatingToBrouwerLyddaneElementsConverterTest.java b/src/test/java/org/orekit/propagation/conversion/averaging/converters/OsculatingToBrouwerLyddaneElementsConverterTest.java new file mode 100644 index 0000000000..5138170498 --- /dev/null +++ b/src/test/java/org/orekit/propagation/conversion/averaging/converters/OsculatingToBrouwerLyddaneElementsConverterTest.java @@ -0,0 +1,50 @@ +package org.orekit.propagation.conversion.averaging.converters; + +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.data.DataContext; +import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.KeplerianOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.conversion.averaging.BrouwerLyddaneOrbitalState; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.Constants; +import org.orekit.utils.PVCoordinates; + +class OsculatingToBrouwerLyddaneElementsConverterTest { + + @BeforeEach + public void setUp() { + Utils.setDataRoot("regular-data:potential"); + } + + @Test + void testConvertToAveragedElements() { + // GIVEN + final KeplerianOrbit osculatingOrbit = new KeplerianOrbit(1e7, 0.1, 1., 1., 2., -3., + PositionAngleType.MEAN, FramesFactory.getGCRF(), AbsoluteDate.ARBITRARY_EPOCH, + Constants.EGM96_EARTH_MU); + final OsculatingToBrouwerLyddaneConverter converter = new OsculatingToBrouwerLyddaneConverter(getProvider()); + converter.setEpsilon(1e-12); + converter.setMaxIterations(100); + // WHEN + final BrouwerLyddaneOrbitalState averagedElements = converter + .convertToAveraged(osculatingOrbit); + // THEN + final Orbit recomputedOsculatingOrbit = averagedElements.toOsculatingOrbit(); + final PVCoordinates relativePV = new PVCoordinates(osculatingOrbit.getPVCoordinates(), + recomputedOsculatingOrbit.getPVCoordinates(osculatingOrbit.getFrame())); + final double expectedDifference = 0.; + Assertions.assertEquals(expectedDifference, relativePV.getPosition().getNorm(), 1e-7); + Assertions.assertEquals(expectedDifference, relativePV.getVelocity().getNorm(), 1e-10); + } + + private UnnormalizedSphericalHarmonicsProvider getProvider() { + return DataContext.getDefault().getGravityFields().getUnnormalizedProvider(5, 0); + } + +} diff --git a/src/test/java/org/orekit/propagation/conversion/averaging/converters/OsculatingToDSST6X0ElementsConverterTest.java b/src/test/java/org/orekit/propagation/conversion/averaging/converters/OsculatingToDSST6X0ElementsConverterTest.java new file mode 100644 index 0000000000..f49be3e5cb --- /dev/null +++ b/src/test/java/org/orekit/propagation/conversion/averaging/converters/OsculatingToDSST6X0ElementsConverterTest.java @@ -0,0 +1,50 @@ +package org.orekit.propagation.conversion.averaging.converters; + +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.data.DataContext; +import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.EquinoctialOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.conversion.averaging.DSST6X0OrbitalState; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.Constants; +import org.orekit.utils.PVCoordinates; + +class OsculatingToDSST6X0ElementsConverterTest { + + @BeforeEach + public void setUp() { + Utils.setDataRoot("regular-data:potential"); + } + + @Test + void testConvertToAveragedElements() { + // GIVEN + final EquinoctialOrbit osculatingOrbit = new EquinoctialOrbit(1e7, 0.1, -0.2, 0.2, -0.1, -3., + PositionAngleType.MEAN, FramesFactory.getGCRF(), AbsoluteDate.ARBITRARY_EPOCH, + Constants.EGM96_EARTH_MU); + final OsculatingToDSST6X0Converter converter = new OsculatingToDSST6X0Converter(getProvider()); + converter.setEpsilon(1e-12); + converter.setMaxIterations(100); + // WHEN + final DSST6X0OrbitalState averagedElements = converter + .convertToAveraged(osculatingOrbit); + // THEN + final Orbit recomputedOsculatingOrbit = averagedElements.toOsculatingOrbit(); + final PVCoordinates relativePV = new PVCoordinates(osculatingOrbit.getPVCoordinates(), + recomputedOsculatingOrbit.getPVCoordinates(osculatingOrbit.getFrame())); + final double expectedDifference = 0.; + Assertions.assertEquals(expectedDifference, relativePV.getPosition().getNorm(), 2e-5); + Assertions.assertEquals(expectedDifference, relativePV.getVelocity().getNorm(), 1e-8); + } + + private UnnormalizedSphericalHarmonicsProvider getProvider() { + return DataContext.getDefault().getGravityFields().getUnnormalizedProvider(6, 0); + } + +} diff --git a/src/test/java/org/orekit/propagation/conversion/averaging/converters/OsculatingToEcksteinHechlerElementsConverterTest.java b/src/test/java/org/orekit/propagation/conversion/averaging/converters/OsculatingToEcksteinHechlerElementsConverterTest.java new file mode 100644 index 0000000000..4950648b33 --- /dev/null +++ b/src/test/java/org/orekit/propagation/conversion/averaging/converters/OsculatingToEcksteinHechlerElementsConverterTest.java @@ -0,0 +1,50 @@ +package org.orekit.propagation.conversion.averaging.converters; + +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.data.DataContext; +import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.CircularOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.conversion.averaging.EcksteinHechlerOrbitalState; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.Constants; +import org.orekit.utils.PVCoordinates; + +class OsculatingToEcksteinHechlerElementsConverterTest { + + @BeforeEach + public void setUp() { + Utils.setDataRoot("regular-data:potential"); + } + + @Test + void testConvertToAveragedElements() { + // GIVEN + final CircularOrbit osculatingOrbit = new CircularOrbit(1e7, 2e-4, -1e-4, 1.e-3, 2., -3., + PositionAngleType.MEAN, FramesFactory.getGCRF(), AbsoluteDate.ARBITRARY_EPOCH, + Constants.EGM96_EARTH_MU); + final OsculatingToEcksteinHechlerConverter converter = new OsculatingToEcksteinHechlerConverter(getProvider()); + converter.setEpsilon(1e-12); + converter.setMaxIterations(100); + // WHEN + final EcksteinHechlerOrbitalState averagedElements = converter + .convertToAveraged(osculatingOrbit); + // THEN + final Orbit recomputedOsculatingOrbit = averagedElements.toOsculatingOrbit(); + final PVCoordinates relativePV = new PVCoordinates(osculatingOrbit.getPVCoordinates(), + recomputedOsculatingOrbit.getPVCoordinates(osculatingOrbit.getFrame())); + final double expectedDifference = 0.; + Assertions.assertEquals(expectedDifference, relativePV.getPosition().getNorm(), 1e-7); + Assertions.assertEquals(expectedDifference, relativePV.getVelocity().getNorm(), 3e-2); + } + + private UnnormalizedSphericalHarmonicsProvider getProvider() { + return DataContext.getDefault().getGravityFields().getUnnormalizedProvider(6, 0); + } + +} diff --git a/src/test/java/org/orekit/propagation/conversion/averaging/converters/OsculatingToSGP4ElementsConverterTest.java b/src/test/java/org/orekit/propagation/conversion/averaging/converters/OsculatingToSGP4ElementsConverterTest.java new file mode 100644 index 0000000000..e54ce3cf08 --- /dev/null +++ b/src/test/java/org/orekit/propagation/conversion/averaging/converters/OsculatingToSGP4ElementsConverterTest.java @@ -0,0 +1,44 @@ +package org.orekit.propagation.conversion.averaging.converters; + +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.frames.FramesFactory; +import org.orekit.orbits.KeplerianOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.conversion.averaging.SGP4OrbitalState; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.Constants; +import org.orekit.utils.PVCoordinates; + +class OsculatingToSGP4ElementsConverterTest { + + @BeforeEach + public void setUp() { + Utils.setDataRoot("regular-data:potential"); + } + + @Test + void testConvertToAveragedElements() { + // GIVEN + final KeplerianOrbit osculatingOrbit = new KeplerianOrbit(1e7, 0.1, 1., 1., 2., -3., + PositionAngleType.MEAN, FramesFactory.getGCRF(), AbsoluteDate.ARBITRARY_EPOCH, + Constants.EGM96_EARTH_MU); + final OsculatingToSGP4Converter converter = new OsculatingToSGP4Converter(); + converter.setEpsilon(1e-12); + converter.setMaxIterations(100); + // WHEN + final SGP4OrbitalState averagedElements = converter + .convertToAveraged(osculatingOrbit); + // THEN + final Orbit recomputedOsculatingOrbit = averagedElements.toOsculatingOrbit(); + final PVCoordinates relativePV = new PVCoordinates(osculatingOrbit.getPVCoordinates(), + recomputedOsculatingOrbit.getPVCoordinates(osculatingOrbit.getFrame())); + final double expectedDifference = 0.; + Assertions.assertEquals(expectedDifference, relativePV.getPosition().getNorm(), 1e-5); + Assertions.assertEquals(expectedDifference, relativePV.getVelocity().getNorm(), 3e-3); + } + +} diff --git a/src/test/java/org/orekit/propagation/conversion/averaging/elements/AveragedCircularWithMeanAngleTest.java b/src/test/java/org/orekit/propagation/conversion/averaging/elements/AveragedCircularWithMeanAngleTest.java new file mode 100644 index 0000000000..3aad9ea2cb --- /dev/null +++ b/src/test/java/org/orekit/propagation/conversion/averaging/elements/AveragedCircularWithMeanAngleTest.java @@ -0,0 +1,23 @@ +package org.orekit.propagation.conversion.averaging.elements; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class AveragedCircularWithMeanAngleTest { + + @Test + void toArrayTest() { + // GIVEN + final AveragedCircularWithMeanAngle elements = new AveragedCircularWithMeanAngle(1., 2., 3., 4., 5., 6.); + // WHEN + final double[] elementsAsArray = elements.toArray(); + // THEN + Assertions.assertEquals(elements.getAveragedSemiMajorAxis(), elementsAsArray[0]); + Assertions.assertEquals(elements.getAveragedCircularEx(), elementsAsArray[1]); + Assertions.assertEquals(elements.getAveragedCircularEy(), elementsAsArray[2]); + Assertions.assertEquals(elements.getAveragedInclination(), elementsAsArray[3]); + Assertions.assertEquals(elements.getAveragedRightAscensionOfTheAscendingNode(), elementsAsArray[4]); + Assertions.assertEquals(elements.getAveragedMeanLatitudeArgument(), elementsAsArray[5]); + } + +} diff --git a/src/test/java/org/orekit/propagation/conversion/averaging/elements/AveragedEquinoctialWithMeanAngleTest.java b/src/test/java/org/orekit/propagation/conversion/averaging/elements/AveragedEquinoctialWithMeanAngleTest.java new file mode 100644 index 0000000000..3a460c2126 --- /dev/null +++ b/src/test/java/org/orekit/propagation/conversion/averaging/elements/AveragedEquinoctialWithMeanAngleTest.java @@ -0,0 +1,23 @@ +package org.orekit.propagation.conversion.averaging.elements; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class AveragedEquinoctialWithMeanAngleTest { + + @Test + void toArrayTest() { + // GIVEN + final AveragedEquinoctialWithMeanAngle elements = new AveragedEquinoctialWithMeanAngle(1., 2., 3., 4., 5., 6.); + // WHEN + final double[] elementsAsArray = elements.toArray(); + // THEN + Assertions.assertEquals(elements.getAveragedSemiMajorAxis(), elementsAsArray[0]); + Assertions.assertEquals(elements.getAveragedEquinoctialEx(), elementsAsArray[1]); + Assertions.assertEquals(elements.getAveragedEquinoctialEy(), elementsAsArray[2]); + Assertions.assertEquals(elements.getAveragedHx(), elementsAsArray[3]); + Assertions.assertEquals(elements.getAveragedHy(), elementsAsArray[4]); + Assertions.assertEquals(elements.getAveragedMeanLongitudeArgument(), elementsAsArray[5]); + } + +} diff --git a/src/test/java/org/orekit/propagation/conversion/averaging/elements/AveragedKeplerianWithMeanAngleTest.java b/src/test/java/org/orekit/propagation/conversion/averaging/elements/AveragedKeplerianWithMeanAngleTest.java new file mode 100644 index 0000000000..4bf744590d --- /dev/null +++ b/src/test/java/org/orekit/propagation/conversion/averaging/elements/AveragedKeplerianWithMeanAngleTest.java @@ -0,0 +1,22 @@ +package org.orekit.propagation.conversion.averaging.elements; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class AveragedKeplerianWithMeanAngleTest { + + @Test + void toArrayTest() { + // GIVEN + final AveragedKeplerianWithMeanAngle elements = new AveragedKeplerianWithMeanAngle(1., 2., 3., 4., 5., 6.); + // WHEN + final double[] elementsAsArray = elements.toArray(); + // THEN + Assertions.assertEquals(elements.getAveragedSemiMajorAxis(), elementsAsArray[0]); + Assertions.assertEquals(elements.getAveragedEccentricity(), elementsAsArray[1]); + Assertions.assertEquals(elements.getAveragedInclination(), elementsAsArray[2]); + Assertions.assertEquals(elements.getAveragedPerigeeArgument(), elementsAsArray[3]); + Assertions.assertEquals(elements.getAveragedRightAscensionOfTheAscendingNode(), elementsAsArray[4]); + Assertions.assertEquals(elements.getAveragedMeanAnomaly(), elementsAsArray[5]); + } +} diff --git a/src/test/java/org/orekit/propagation/events/AdaptableIntervalTest.java b/src/test/java/org/orekit/propagation/events/AdaptableIntervalTest.java new file mode 100644 index 0000000000..56da7986da --- /dev/null +++ b/src/test/java/org/orekit/propagation/events/AdaptableIntervalTest.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; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.orekit.propagation.SpacecraftState; + +class AdaptableIntervalTest { + + @Test + void testOf() { + // GIVEN + final double expectedValue = 1.; + // WHEN + final AdaptableInterval adaptableInterval = AdaptableInterval.of(expectedValue); + // THEN + final double actualValue = adaptableInterval.currentInterval(Mockito.mock(SpacecraftState.class)); + Assertions.assertEquals(expectedValue, actualValue); + } + +} diff --git a/src/test/java/org/orekit/propagation/events/AndDetectorTest.java b/src/test/java/org/orekit/propagation/events/AndDetectorTest.java index 74983d2327..ce26c67894 100644 --- a/src/test/java/org/orekit/propagation/events/AndDetectorTest.java +++ b/src/test/java/org/orekit/propagation/events/AndDetectorTest.java @@ -119,10 +119,10 @@ public void testCancellation() { public void testInit() { // setup EventDetector a = Mockito.mock(EventDetector.class); - Mockito.when(a.getMaxCheckInterval()).thenReturn(s-> AbstractDetector.DEFAULT_MAXCHECK); + Mockito.when(a.getMaxCheckInterval()).thenReturn(AdaptableInterval.of(AbstractDetector.DEFAULT_MAXCHECK)); Mockito.when(a.getThreshold()).thenReturn(AbstractDetector.DEFAULT_THRESHOLD); EventDetector b = Mockito.mock(EventDetector.class); - Mockito.when(b.getMaxCheckInterval()).thenReturn(s-> AbstractDetector.DEFAULT_MAXCHECK); + Mockito.when(b.getMaxCheckInterval()).thenReturn(AdaptableInterval.of(AbstractDetector.DEFAULT_MAXCHECK)); Mockito.when(b.getThreshold()).thenReturn(AbstractDetector.DEFAULT_THRESHOLD); EventHandler c = Mockito.mock(EventHandler.class); BooleanDetector and = BooleanDetector.andCombine(a, b).withHandler(c); @@ -175,7 +175,7 @@ public double getThreshold() { @Override public AdaptableInterval getMaxCheckInterval() { - return s -> AbstractDetector.DEFAULT_MAXCHECK; + return AdaptableInterval.of(AbstractDetector.DEFAULT_MAXCHECK); } @Override diff --git a/src/test/java/org/orekit/propagation/events/AngularSeparationFromSatelliteDetectorTest.java b/src/test/java/org/orekit/propagation/events/AngularSeparationFromSatelliteDetectorTest.java index 528f4c6d00..bb04e03d1d 100644 --- a/src/test/java/org/orekit/propagation/events/AngularSeparationFromSatelliteDetectorTest.java +++ b/src/test/java/org/orekit/propagation/events/AngularSeparationFromSatelliteDetectorTest.java @@ -1,3 +1,20 @@ +/* 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.events; import org.hipparchus.geometry.euclidean.threed.Vector3D; diff --git a/src/test/java/org/orekit/propagation/events/ApsideDetectorTest.java b/src/test/java/org/orekit/propagation/events/ApsideDetectorTest.java index e6c5e96619..689be0e585 100644 --- a/src/test/java/org/orekit/propagation/events/ApsideDetectorTest.java +++ b/src/test/java/org/orekit/propagation/events/ApsideDetectorTest.java @@ -33,18 +33,19 @@ import org.orekit.propagation.analytical.EcksteinHechlerPropagator; import org.orekit.propagation.events.EventsLogger.LoggedEvent; import org.orekit.propagation.events.handlers.ContinueOnEvent; +import org.orekit.propagation.events.intervals.ApsideDetectionAdaptableIntervalFactory; import org.orekit.time.AbsoluteDate; import org.orekit.time.TimeScale; import org.orekit.time.TimeScalesFactory; import org.orekit.utils.Constants; import org.orekit.utils.PVCoordinates; -public class ApsideDetectorTest { +class ApsideDetectorTest { private Propagator propagator; @Test - public void testSimple() { + void testSimple() { EventDetector detector = new ApsideDetector(propagator.getInitialState().getOrbit()). withMaxCheck(600.0). withThreshold(1.0e-12). @@ -70,24 +71,13 @@ public void testSimple() { } @Test - public void testFixedMaxCheck() { - doTestMaxcheck(s -> 20.0, 4738); + void testFixedMaxCheck() { + doTestMaxcheck(AdaptableInterval.of(20.0), 4738); } @Test - public void testAnomalyAwareMaxCheck() { - doTestMaxcheck(s -> { - final double baseMaxCheck = 20.0; - final KeplerianOrbit orbit = (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(s.getOrbit()); - final double period = orbit.getKeplerianPeriod(); - final double timeSincePreviousPerigee = MathUtils.normalizeAngle(orbit.getMeanAnomaly(), FastMath.PI) / - orbit.getKeplerianMeanMotion(); - final double timeToNextPerigee = period - timeSincePreviousPerigee; - final double timeToApogee = FastMath.abs(0.5 * period - timeSincePreviousPerigee); - final double timeToClosestApside = FastMath.min(timeSincePreviousPerigee, - FastMath.min(timeToApogee, timeToNextPerigee)); - return (timeToClosestApside < 2 * baseMaxCheck) ? baseMaxCheck : timeToClosestApside - 0.5 * baseMaxCheck; - }, 730); + void testAnomalyAwareMaxCheck() { + doTestMaxcheck(ApsideDetectionAdaptableIntervalFactory.getForwardApsideDetectionAdaptableInterval(), 726); } private void doTestMaxcheck(final AdaptableInterval maxCheck, int expectedCalls) { diff --git a/src/test/java/org/orekit/propagation/events/BetaAngleDetectorTest.java b/src/test/java/org/orekit/propagation/events/BetaAngleDetectorTest.java new file mode 100644 index 0000000000..0b22af6fb3 --- /dev/null +++ b/src/test/java/org/orekit/propagation/events/BetaAngleDetectorTest.java @@ -0,0 +1,120 @@ +/* Copyright 2002-2024 Joseph Reed + * 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. + * Joseph Reed 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 static org.junit.jupiter.api.Assertions.assertEquals; + +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.ode.nonstiff.AdaptiveStepsizeIntegrator; +import org.hipparchus.ode.nonstiff.DormandPrince853Integrator; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.MathUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.orekit.Utils; +import org.orekit.bodies.CelestialBodyFactory; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.EquinoctialOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.propagation.Propagator; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.events.handlers.RecordAndContinue; +import org.orekit.propagation.numerical.NumericalPropagator; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.TimeScalesFactory; +import org.orekit.utils.PVCoordinates; + +public class BetaAngleDetectorTest { + private Propagator propagator; + private AbsoluteDate date; + + @BeforeEach + void setup() { + Utils.setDataRoot("regular-data"); + final Vector3D position = new Vector3D(-6142438.668, 3492467.560, -25767.25680); + final Vector3D velocity = new Vector3D(505.8479685, 942.7809215, 7435.922231); + final AbsoluteDate iniDate = new AbsoluteDate(1969, 7, 28, 4, 0, 0.0, TimeScalesFactory.getTT()); + final Orbit orbit = new EquinoctialOrbit(new PVCoordinates(position, velocity), + FramesFactory.getGCRF(), iniDate, 3.9860047e14); + final SpacecraftState initialState = new SpacecraftState(orbit); + double[] absTolerance = { + 0.001, 1.0e-9, 1.0e-9, 1.0e-6, 1.0e-6, 1.0e-6, 0.001 + }; + double[] relTolerance = { + 1.0e-7, 1.0e-4, 1.0e-4, 1.0e-7, 1.0e-7, 1.0e-7, 1.0e-7 + }; + AdaptiveStepsizeIntegrator integrator = + new DormandPrince853Integrator(0.001, 1000, absTolerance, relTolerance); + integrator.setInitialStepSize(60); + propagator = new NumericalPropagator(integrator); + ((NumericalPropagator) propagator).setInitialState(initialState); + date = iniDate; + } + + @Test + void evaluate() { + final BetaAngleDetector detector = new BetaAngleDetector(0); + + AbsoluteDate d = date; + for (int i = 0; i < 50; i++) { + final SpacecraftState state = propagator.propagate(d); + final double g = detector.g(state); + final double beta = BetaAngleDetector.calculateBetaAngle( + state, CelestialBodyFactory.getSun(), FramesFactory.getGCRF()); + + final double expectedBeta = MathUtils.SEMI_PI - Vector3D.angle( + state.getPVCoordinates(FramesFactory.getGCRF()).getMomentum().normalize(), + CelestialBodyFactory.getSun().getPosition(state.getDate(), FramesFactory.getGCRF()).normalize()); + assertEquals(-beta, g, 1e-9); + assertEquals(expectedBeta, beta, 1e-9); + + d = d.shiftedBy(86400); + } + } + + @Test + void simpleStop() { + final BetaAngleDetector detector = new BetaAngleDetector(0, + CelestialBodyFactory.getSun(), + FramesFactory.getGCRF()); + + propagator.addEventDetector(detector); + + final SpacecraftState state = propagator.propagate(date, date.shiftedBy(30 * 86400)); + assertEquals(1883928.588393031, state.getDate().durationFrom(date), 1e-9); + + assertEquals(0, BetaAngleDetector.calculateBetaAngle(state, propagator), 1e-9); + } + + @Test + void record() { + final RecordAndContinue handler = new RecordAndContinue(); + final BetaAngleDetector detector = new BetaAngleDetector(0, + CelestialBodyFactory.getMoon(), + FramesFactory.getGCRF()) + .withBetaThreshold(FastMath.toRadians(1)) + .withCelestialProvider(CelestialBodyFactory.getSun()) + .withInertialFrame(FramesFactory.getEME2000()) + .withHandler(handler); + + propagator.addEventDetector(detector); + + final SpacecraftState state = propagator.propagate(date, date.shiftedBy(30 * 86400)); + assertEquals(30 * 86400, state.getDate().durationFrom(date), 1e-9); + assertEquals(1, handler.getEvents().size()); + } +} diff --git a/src/test/java/org/orekit/propagation/events/CloseEventsAbstractTest.java b/src/test/java/org/orekit/propagation/events/CloseEventsAbstractTest.java index 6cc0b815c3..610d3bc547 100644 --- a/src/test/java/org/orekit/propagation/events/CloseEventsAbstractTest.java +++ b/src/test/java/org/orekit/propagation/events/CloseEventsAbstractTest.java @@ -2169,7 +2169,7 @@ protected static class TimeDetector extends AbstractDetector { * @param eventTs event times past epoch. */ public TimeDetector(double... eventTs) { - this(s -> DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, + this(AdaptableInterval.of(DEFAULT_MAXCHECK), DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, new StopOnEvent(), toDates(eventTs)); } @@ -2179,7 +2179,7 @@ public TimeDetector(double... eventTs) { * @param eventTs event times past epoch. */ public TimeDetector(AbsoluteDate... eventTs) { - this(s -> DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, + this(AdaptableInterval.of(DEFAULT_MAXCHECK), DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, new StopOnEvent(), Arrays.asList(eventTs)); } @@ -2228,12 +2228,12 @@ private static class FlatDetector extends AbstractDetector { private final EventDetector g; public FlatDetector(double... eventTs) { - this(s -> DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, + this(AdaptableInterval.of(DEFAULT_MAXCHECK), DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, new StopOnEvent(), new TimeDetector(eventTs)); } public FlatDetector(AbsoluteDate... eventTs) { - this(s -> DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, + this(AdaptableInterval.of(DEFAULT_MAXCHECK), DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, new StopOnEvent(), new TimeDetector(eventTs)); } @@ -2268,7 +2268,7 @@ private static class ContinuousDetector extends AbstractDetector eventTs; public ContinuousDetector(double... eventTs) { - this(s -> DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, + this(AdaptableInterval.of(DEFAULT_MAXCHECK), DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, new StopOnEvent(), toDates(eventTs)); } @@ -2400,7 +2400,7 @@ private class ResetChangesSignGenerator extends AbstractDetector DEFAULT_MAXCHECK, DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, + this(AdaptableInterval.of(DEFAULT_MAXCHECK), DEFAULT_THRESHOLD, DEFAULT_MAX_ITER, new ContinueOnEvent(), t0, y1, y2, change); } diff --git a/src/test/java/org/orekit/propagation/events/CylindricalShadowEclipseDetectorTest.java b/src/test/java/org/orekit/propagation/events/CylindricalShadowEclipseDetectorTest.java new file mode 100644 index 0000000000..0c35c08207 --- /dev/null +++ b/src/test/java/org/orekit/propagation/events/CylindricalShadowEclipseDetectorTest.java @@ -0,0 +1,111 @@ +package org.orekit.propagation.events; + +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.mockito.Mockito; +import org.orekit.Utils; +import org.orekit.bodies.CelestialBodyFactory; +import org.orekit.frames.Frame; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.events.handlers.ContinueOnEvent; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.*; + +class CylindricalShadowEclipseDetectorTest { + + @BeforeEach + public void setUp() { + Utils.setDataRoot("regular-data"); + } + + @Test + void testCreate() { + // GIVEN + final ExtendedPVCoordinatesProvider 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()); + // THEN + Assertions.assertEquals(expectedMaxIter, detector.getMaxIterationCount()); + Assertions.assertEquals(expectedThreshold, detector.getThreshold()); + Assertions.assertEquals(adaptableInterval, detector.getMaxCheckInterval()); + } + + @Test + void testG0Eclipse() { + // GIVEN + final PVCoordinatesProvider sun = new TestDirectionProvider(); + final CylindricalShadowEclipseDetector eclipseDetector = new CylindricalShadowEclipseDetector(sun, + Constants.EGM96_EARTH_EQUATORIAL_RADIUS, new ContinueOnEvent()); + final Vector3D position = new Vector3D(1., eclipseDetector.getOccultingBodyRadius(), 0.); + final SpacecraftState mockedState = mockState(position); + // WHEN + final double g = eclipseDetector.g(mockedState); + // THEN + Assertions.assertEquals(0., g); + } + + @Test + void testGEclipse() { + // GIVEN + final PVCoordinatesProvider sun = new TestDirectionProvider(); + final CylindricalShadowEclipseDetector eclipseDetector = new CylindricalShadowEclipseDetector(sun, + Constants.EGM96_EARTH_EQUATORIAL_RADIUS, new ContinueOnEvent()); + final Vector3D position = new Vector3D(1e7, 0, -1e2); + final SpacecraftState mockedState = mockState(position); + // WHEN + final double g = eclipseDetector.g(mockedState); + // THEN + Assertions.assertTrue(g < 0.); + } + + @Test + void testGNoEclipse() { + // GIVEN + final PVCoordinatesProvider sun = new TestDirectionProvider(); + final CylindricalShadowEclipseDetector eclipseDetector = new CylindricalShadowEclipseDetector(sun, + Constants.EGM96_EARTH_EQUATORIAL_RADIUS, new ContinueOnEvent()); + final Vector3D position = new Vector3D(0., 1e4, 0.); + final SpacecraftState mockedState = mockState(position); + // WHEN + final double g = eclipseDetector.g(mockedState); + // THEN + Assertions.assertTrue(g > 0.); + } + + @Test + void testGNoEclipse2() { + // GIVEN + final PVCoordinatesProvider sun = new TestDirectionProvider(); + final CylindricalShadowEclipseDetector eclipseDetector = new CylindricalShadowEclipseDetector(sun, + Constants.EGM96_EARTH_EQUATORIAL_RADIUS, new ContinueOnEvent()); + final Vector3D position = new Vector3D(-1e6, 1e3, 0.); + final SpacecraftState mockedState = mockState(position); + // WHEN + final double g = eclipseDetector.g(mockedState); + // THEN + Assertions.assertTrue(g > 0.); + } + + private SpacecraftState mockState(final Vector3D position) { + final SpacecraftState mockedState = Mockito.mock(SpacecraftState.class); + Mockito.when(mockedState.getPosition()).thenReturn(position); + return mockedState; + } + + private static class TestDirectionProvider implements PVCoordinatesProvider { + + @Override + public TimeStampedPVCoordinates getPVCoordinates(AbsoluteDate date, Frame frame) { + return new TimeStampedPVCoordinates(date, new PVCoordinates(Vector3D.MINUS_I)); + } + } + +} diff --git a/src/test/java/org/orekit/propagation/events/EclipseDetectorTest.java b/src/test/java/org/orekit/propagation/events/EclipseDetectorTest.java index 4b408cd1b4..abc8c24027 100644 --- a/src/test/java/org/orekit/propagation/events/EclipseDetectorTest.java +++ b/src/test/java/org/orekit/propagation/events/EclipseDetectorTest.java @@ -53,7 +53,7 @@ import java.util.List; -public class EclipseDetectorTest { +class EclipseDetectorTest { private double mu; private AbsoluteDate iniDate; @@ -65,7 +65,7 @@ public class EclipseDetectorTest { private double sunRadius; @Test - public void testPolar() { + void testPolar() { final KeplerianOrbit original = (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(initialState.getOrbit()); final KeplerianOrbit polar = new KeplerianOrbit(original.getA(), original.getE(), 0.5 * FastMath.PI, original.getPerigeeArgument(), @@ -108,7 +108,7 @@ public void testPolar() { Assertions.assertEquals( 2280.427, events.get(1).getState().getDate().durationFrom(iniDate), 1.0e-3); Assertions.assertTrue(events.get(2).getEventDetector() == withFlattening); Assertions.assertTrue(events.get(2).isIncreasing()); - Assertions.assertEquals( 4310.742, events.get(2).getState().getDate().durationFrom(iniDate), 1.0e-3); + Assertions.assertEquals( 4310.741, events.get(2).getState().getDate().durationFrom(iniDate), 1.0e-3); Assertions.assertTrue(events.get(3).getEventDetector() == withoutFlattening); Assertions.assertTrue(events.get(3).isIncreasing()); Assertions.assertEquals( 4317.155, events.get(3).getState().getDate().durationFrom(iniDate), 1.6e-3); @@ -134,7 +134,7 @@ public void testPolar() { } @Test - public void testEclipse() { + void testEclipse() { EclipseDetector e = new EclipseDetector(sun, sunRadius, earth). withMaxCheck(60.0). withThreshold(1.0e-3). @@ -151,7 +151,7 @@ public void testEclipse() { } @Test - public void testPenumbra() { + void testPenumbra() { EclipseDetector e = new EclipseDetector(sun, sunRadius, earth). withMaxCheck(60.0). withThreshold(1.0e-3). @@ -163,7 +163,7 @@ public void testPenumbra() { } @Test - public void testWithMethods() { + void testWithMethods() { EclipseDetector e = new EclipseDetector(sun, sunRadius, earth). withHandler(new StopOnDecreasing()). withMaxCheck(120.0). @@ -180,7 +180,7 @@ public void testWithMethods() { } @Test - public void testInsideOcculting() { + void testInsideOcculting() { EclipseDetector e = new EclipseDetector(sun, sunRadius, earth); SpacecraftState s = new SpacecraftState(new CartesianOrbit(new TimeStampedPVCoordinates(AbsoluteDate.J2000_EPOCH, new Vector3D(1e6, 2e6, 3e6), @@ -196,7 +196,7 @@ public void testInsideOcculting() { } @Test - public void testInsideOcculted() { + void testInsideOcculted() { EclipseDetector e = new EclipseDetector(sun, sunRadius, earth); Vector3D p = sun.getPosition(AbsoluteDate.J2000_EPOCH, FramesFactory.getGCRF()); SpacecraftState s = new SpacecraftState(new CartesianOrbit(new TimeStampedPVCoordinates(AbsoluteDate.J2000_EPOCH, @@ -208,7 +208,7 @@ public void testInsideOcculted() { } @Test - public void testTooSmallMaxIterationCount() { + void testTooSmallMaxIterationCount() { int n = 5; EclipseDetector e = new EclipseDetector(sun, sunRadius, earth). withHandler(new StopOnDecreasing()). diff --git a/src/test/java/org/orekit/propagation/events/ElevationDetectorTest.java b/src/test/java/org/orekit/propagation/events/ElevationDetectorTest.java index c5842b2cff..87b3476fe3 100644 --- a/src/test/java/org/orekit/propagation/events/ElevationDetectorTest.java +++ b/src/test/java/org/orekit/propagation/events/ElevationDetectorTest.java @@ -41,10 +41,13 @@ import org.orekit.propagation.SpacecraftState; import org.orekit.propagation.analytical.EcksteinHechlerPropagator; import org.orekit.propagation.analytical.KeplerianPropagator; +import org.orekit.propagation.analytical.tle.TLE; +import org.orekit.propagation.analytical.tle.TLEPropagator; import org.orekit.propagation.events.EventsLogger.LoggedEvent; import org.orekit.propagation.events.handlers.ContinueOnEvent; import org.orekit.propagation.events.handlers.EventHandler; import org.orekit.propagation.events.handlers.StopOnIncreasing; +import org.orekit.propagation.events.intervals.ElevationDetectionAdaptableIntervalFactory; import org.orekit.propagation.sampling.OrekitFixedStepHandler; import org.orekit.time.AbsoluteDate; import org.orekit.time.TimeScale; @@ -108,7 +111,7 @@ public void testAgata() { private static class Checking implements EventHandler, OrekitFixedStepHandler { - private TopocentricFrame topo; + private final TopocentricFrame topo; private boolean visible; public Checking(final TopocentricFrame topo) { @@ -137,10 +140,6 @@ public void handleStep(SpacecraftState currentState) } } - @Override - public void init(SpacecraftState initialState, AbsoluteDate target, double step) { - } - } @Test @@ -235,7 +234,7 @@ public void testHorizon() { public void testIssue136() { // Initial state definition : date, orbit - AbsoluteDate initialDate = new AbsoluteDate(2004, 01, 01, 23, 30, 00.000, TimeScalesFactory.getUTC()); + AbsoluteDate initialDate = new AbsoluteDate(2004, 1, 1, 23, 30, 00.000, TimeScalesFactory.getUTC()); Frame inertialFrame = FramesFactory.getEME2000(); // inertial frame for orbit definition Orbit initialOrbit = new KeplerianOrbit(6828137.005, 7.322641382145889e-10, 1.6967079057368113, 0.0, 1.658054062748353, @@ -524,6 +523,34 @@ public Action eventOccurred(SpacecraftState s, EventDetector detector, boolean i } + @Test + public void testIssue1407() { + final TLE tle = new TLE("1 25544U 98067A 03042.38687590 .00035128 00000-0 41387-3 0 9990", + "2 25544 051.6337 292.0267 0005644 135.6869 224.5096 15.60691825241426"); + final OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, + Constants.WGS84_EARTH_FLATTENING, + FramesFactory.getITRF(IERSConventions.IERS_2010, true)); + final TopocentricFrame topo = new TopocentricFrame(earth, + new GeodeticPoint(FastMath.toRadians(38.832772), + FastMath.toRadians(-104.74755), + 1839.0), + "Colorado Springs"); + final AdaptableInterval maxCheck = ElevationDetectionAdaptableIntervalFactory. + getAdaptableInterval(topo, + ElevationDetectionAdaptableIntervalFactory.DEFAULT_ELEVATION_SWITCH, + 60.0); + final ElevationDetector detector = new ElevationDetector(maxCheck, 1.0e-3, topo). + withConstantElevation(FastMath.toRadians(10.0)). + withHandler(new ContinueOnEvent()); + + final EventsLogger logger = new EventsLogger(); + final Propagator propagator = TLEPropagator.selectExtrapolator(tle); + propagator.addEventDetector(logger.monitorDetector(detector)); + propagator.propagate(tle.getDate().shiftedBy(Constants.JULIAN_DAY)); + Assertions.assertEquals(8, logger.getLoggedEvents().size()); + + } + @BeforeEach public void setUp() { Utils.setDataRoot("regular-data"); diff --git a/src/test/java/org/orekit/propagation/events/ElevationExtremumDetectorTest.java b/src/test/java/org/orekit/propagation/events/ElevationExtremumDetectorTest.java index a0656b8b9b..ee86e3ba52 100644 --- a/src/test/java/org/orekit/propagation/events/ElevationExtremumDetectorTest.java +++ b/src/test/java/org/orekit/propagation/events/ElevationExtremumDetectorTest.java @@ -55,7 +55,7 @@ public void testLEO() { withThreshold(1.e-6). withHandler(new ContinueOnEvent()); final EventSlopeFilter maxElevationDetector = - new EventSlopeFilter(raw, FilterType.TRIGGER_ONLY_DECREASING_EVENTS); + new EventSlopeFilter<>(raw, FilterType.TRIGGER_ONLY_DECREASING_EVENTS); Assertions.assertEquals(60.0, raw.getMaxCheckInterval().currentInterval(null), 1.0e-15); Assertions.assertEquals(1.0e-6, raw.getThreshold(), 1.0e-15); @@ -106,4 +106,3 @@ public void setUp() { } } - diff --git a/src/test/java/org/orekit/propagation/events/EventDetectorTest.java b/src/test/java/org/orekit/propagation/events/EventDetectorTest.java index fd28ff54c9..643ac60b93 100644 --- a/src/test/java/org/orekit/propagation/events/EventDetectorTest.java +++ b/src/test/java/org/orekit/propagation/events/EventDetectorTest.java @@ -171,7 +171,7 @@ public void testIssue108Numerical() { final int n = 100; NumericalPropagator propagator = new NumericalPropagator(new ClassicalRungeKuttaIntegrator(step)); propagator.resetInitialState(new SpacecraftState(orbit)); - GCallsCounter counter = new GCallsCounter(s -> 100000.0, 1.0e-6, 20, new StopOnEvent()); + GCallsCounter counter = new GCallsCounter(AdaptableInterval.of(100000.0), 1.0e-6, 20, new StopOnEvent()); propagator.addEventDetector(counter); propagator.propagate(date.shiftedBy(n * step)); Assertions.assertEquals(n + 1, counter.getCount()); @@ -188,7 +188,7 @@ public void testIssue108Analytical() { final double step = 60.0; final int n = 100; KeplerianPropagator propagator = new KeplerianPropagator(orbit); - GCallsCounter counter = new GCallsCounter(s -> 100000.0, 1.0e-6, 20, new StopOnEvent()); + GCallsCounter counter = new GCallsCounter(AdaptableInterval.of(100000.0), 1.0e-6, 20, new StopOnEvent()); propagator.addEventDetector(counter); propagator.setStepHandler(step, currentState -> {}); propagator.propagate(date.shiftedBy(n * step)); @@ -331,7 +331,7 @@ public int getMaxIterationCount() { @Override public AdaptableInterval getMaxCheckInterval() { - return s -> 60; + return AdaptableInterval.of(60.); } @Override diff --git a/src/test/java/org/orekit/propagation/events/FieldAdaptableIntervalTest.java b/src/test/java/org/orekit/propagation/events/FieldAdaptableIntervalTest.java new file mode 100644 index 0000000000..b813b2f9fa --- /dev/null +++ b/src/test/java/org/orekit/propagation/events/FieldAdaptableIntervalTest.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.FieldSpacecraftState; + +class FieldAdaptableIntervalTest { + + @SuppressWarnings("unchecked") + @Test + void testOf() { + // GIVEN + final double expectedValue = 1.; + // WHEN + final FieldAdaptableInterval adaptableInterval = FieldAdaptableInterval.of(expectedValue); + // THEN + final double actualValue = adaptableInterval.currentInterval(Mockito.mock(FieldSpacecraftState.class)); + Assertions.assertEquals(expectedValue, actualValue); + } + +} diff --git a/src/test/java/org/orekit/propagation/events/FieldAndDetectorTest.java b/src/test/java/org/orekit/propagation/events/FieldAndDetectorTest.java index 5e5254e58c..b19283a4c2 100644 --- a/src/test/java/org/orekit/propagation/events/FieldAndDetectorTest.java +++ b/src/test/java/org/orekit/propagation/events/FieldAndDetectorTest.java @@ -125,10 +125,10 @@ public void testCancellation() { public void testInit() { // setup FieldEventDetector a = Mockito.mock(FieldEventDetector.class); - Mockito.when(a.getMaxCheckInterval()).thenReturn(s -> AbstractDetector.DEFAULT_MAXCHECK); + Mockito.when(a.getMaxCheckInterval()).thenReturn(FieldAdaptableInterval.of(AbstractDetector.DEFAULT_MAXCHECK)); Mockito.when(a.getThreshold()).thenReturn(new Binary64(AbstractDetector.DEFAULT_THRESHOLD)); FieldEventDetector b = Mockito.mock(FieldEventDetector.class); - Mockito.when(b.getMaxCheckInterval()).thenReturn(s -> AbstractDetector.DEFAULT_MAXCHECK); + Mockito.when(b.getMaxCheckInterval()).thenReturn(FieldAdaptableInterval.of(AbstractDetector.DEFAULT_MAXCHECK)); Mockito.when(b.getThreshold()).thenReturn(new Binary64(AbstractDetector.DEFAULT_THRESHOLD)); FieldEventHandler c = Mockito.mock(FieldEventHandler.class); FieldBooleanDetector and = FieldBooleanDetector.andCombine(a, b).withHandler(c); @@ -181,7 +181,7 @@ public Binary64 getThreshold() { @Override public FieldAdaptableInterval getMaxCheckInterval() { - return s -> AbstractDetector.DEFAULT_MAXCHECK; + return FieldAdaptableInterval.of(AbstractDetector.DEFAULT_MAXCHECK); } @Override diff --git a/src/test/java/org/orekit/propagation/events/FieldApsideDetectorTest.java b/src/test/java/org/orekit/propagation/events/FieldApsideDetectorTest.java index 3b5c66e859..2ec7ed4b00 100644 --- a/src/test/java/org/orekit/propagation/events/FieldApsideDetectorTest.java +++ b/src/test/java/org/orekit/propagation/events/FieldApsideDetectorTest.java @@ -18,36 +18,34 @@ import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; +import org.hipparchus.complex.Complex; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; -import org.hipparchus.util.Binary64; import org.hipparchus.util.Binary64Field; import org.hipparchus.util.FastMath; import org.hipparchus.util.MathUtils; 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.FramesFactory; -import org.orekit.orbits.FieldCartesianOrbit; -import org.orekit.orbits.FieldKeplerianOrbit; -import org.orekit.orbits.FieldOrbit; -import org.orekit.orbits.KeplerianOrbit; -import org.orekit.orbits.OrbitType; +import org.orekit.orbits.*; import org.orekit.propagation.FieldPropagator; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.analytical.FieldEcksteinHechlerPropagator; import org.orekit.propagation.events.FieldEventsLogger.FieldLoggedEvent; import org.orekit.propagation.events.handlers.FieldContinueOnEvent; +import org.orekit.propagation.events.intervals.ApsideDetectionAdaptableIntervalFactory; import org.orekit.time.FieldAbsoluteDate; import org.orekit.time.TimeScale; import org.orekit.time.TimeScalesFactory; import org.orekit.utils.Constants; import org.orekit.utils.FieldPVCoordinates; -public class FieldApsideDetectorTest { +class FieldApsideDetectorTest { @Test - public void testSimple() { + void testSimple() { doTestSimple(Binary64Field.getInstance()); } @@ -78,25 +76,32 @@ private > void doTestSimple(Field field) { } @Test - public void testFixedMaxCheck() { - doTestMaxcheck(Binary64Field.getInstance(), s -> 20.0, 4682); + void testFixedMaxCheck() { + doTestMaxcheck(Binary64Field.getInstance(), FieldAdaptableInterval.of(20.), 4687); } @Test - public void testAnomalyAwareMaxCheck() { - doTestMaxcheck(Binary64Field.getInstance(), - s -> { - final double baseMaxCheck = 20.0; - final KeplerianOrbit orbit = ((FieldKeplerianOrbit) OrbitType.KEPLERIAN.convertType(s.getOrbit())).toOrbit(); - final double period = orbit.getKeplerianPeriod(); - final double timeSincePreviousPerigee = MathUtils.normalizeAngle(orbit.getMeanAnomaly(), FastMath.PI) / - orbit.getKeplerianMeanMotion(); - final double timeToNextPerigee = period - timeSincePreviousPerigee; - final double timeToApogee = FastMath.abs(0.5 * period - timeSincePreviousPerigee); - final double timeToClosestApside = FastMath.min(timeSincePreviousPerigee, - FastMath.min(timeToApogee, timeToNextPerigee)); - return (timeToClosestApside < 2 * baseMaxCheck) ? baseMaxCheck : timeToClosestApside - 0.5 * baseMaxCheck; - }, 671); + void testConstructor() { + // GIVEN + final double period = 10.; + final Complex threshold = Complex.ONE; + final FieldOrbit mockedFieldOrbit = Mockito.mock(FieldOrbit.class); + Mockito.when(mockedFieldOrbit.getKeplerianPeriod()).thenReturn(new Complex(period)); + // WHEN + final FieldApsideDetector fieldApsideDetector = new FieldApsideDetector<>(threshold, mockedFieldOrbit); + // THEN + final Orbit mockedOrbit = Mockito.mock(Orbit.class); + Mockito.when(mockedOrbit.getKeplerianPeriod()).thenReturn(period); + final ApsideDetector apsideDetector = new ApsideDetector(threshold.getReal(), mockedOrbit); + Assertions.assertEquals(apsideDetector.getThreshold(), fieldApsideDetector.getThreshold().getReal()); + } + + @Test + void testAnomalyAwareMaxCheck() { + final AdaptableInterval adaptableInterval = ApsideDetectionAdaptableIntervalFactory + .getForwardApsideDetectionAdaptableInterval(); + doTestMaxcheck(Binary64Field.getInstance(), state -> adaptableInterval.currentInterval(state.toSpacecraftState()), + 643); } private > void doTestMaxcheck(final Field field, diff --git a/src/test/java/org/orekit/propagation/events/FieldBetaAngleDetectorTest.java b/src/test/java/org/orekit/propagation/events/FieldBetaAngleDetectorTest.java new file mode 100644 index 0000000000..6044fb43c6 --- /dev/null +++ b/src/test/java/org/orekit/propagation/events/FieldBetaAngleDetectorTest.java @@ -0,0 +1,132 @@ +/* Copyright 2002-2024 Joseph Reed + * 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. + * Joseph Reed 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 static org.junit.jupiter.api.Assertions.assertEquals; + +import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.ode.nonstiff.AdaptiveStepsizeFieldIntegrator; +import org.hipparchus.ode.nonstiff.DormandPrince853FieldIntegrator; +import org.hipparchus.util.Binary64; +import org.hipparchus.util.Binary64Field; +import org.hipparchus.util.FastMath; +import org.hipparchus.util.MathUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.orekit.Utils; +import org.orekit.bodies.CelestialBodyFactory; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.FieldEquinoctialOrbit; +import org.orekit.orbits.FieldOrbit; +import org.orekit.propagation.FieldPropagator; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.events.handlers.FieldRecordAndContinue; +import org.orekit.propagation.numerical.FieldNumericalPropagator; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.TimeScalesFactory; +import org.orekit.utils.FieldPVCoordinates; +import org.orekit.utils.FieldPVCoordinatesProvider; + +public class FieldBetaAngleDetectorTest { + private FieldPropagator propagator; + private FieldAbsoluteDate date; + + @BeforeEach + void setup() { + Utils.setDataRoot("regular-data"); + final FieldVector3D position = new FieldVector3D<>( + new Binary64(-6142438.668), new Binary64(3492467.560), new Binary64(-25767.25680)); + final FieldVector3D velocity = new FieldVector3D( + new Binary64(505.8479685), new Binary64(942.7809215), new Binary64(7435.922231)); + final FieldAbsoluteDate iniDate = new FieldAbsoluteDate<>(Binary64Field.getInstance(), 1969, 7, 28, 4, 0, 0.0, TimeScalesFactory.getTT()); + final FieldOrbit orbit = + new FieldEquinoctialOrbit<>(new FieldPVCoordinates<>(position, velocity), + FramesFactory.getGCRF(), iniDate, new Binary64(3.9860047e14)); + final FieldSpacecraftState initialState = new FieldSpacecraftState<>(orbit); + double[] absTolerance = { + 0.001, 1.0e-9, 1.0e-9, 1.0e-6, 1.0e-6, 1.0e-6, 0.001 + }; + double[] relTolerance = { + 1.0e-7, 1.0e-4, 1.0e-4, 1.0e-7, 1.0e-7, 1.0e-7, 1.0e-7 + }; + AdaptiveStepsizeFieldIntegrator integrator = + new DormandPrince853FieldIntegrator<>(Binary64Field.getInstance(), 0.001, 1000, absTolerance, relTolerance); + integrator.setInitialStepSize(60); + propagator = new FieldNumericalPropagator<>(Binary64Field.getInstance(), integrator); + ((FieldNumericalPropagator) propagator).setInitialState(initialState); + date = iniDate; + } + + @Test + void evaluate() { + final FieldPVCoordinatesProvider sun = CelestialBodyFactory.getSun().toFieldPVCoordinatesProvider(Binary64Field.getInstance()); + final FieldBetaAngleDetector detector = new FieldBetaAngleDetector<>(Binary64.ZERO); + + FieldAbsoluteDate d = date; + for (int i = 0; i < 50; i++) { + final FieldSpacecraftState state = propagator.propagate(d); + final Binary64 g = detector.g(state); + final Binary64 beta = FieldBetaAngleDetector.calculateBetaAngle( + state, sun, FramesFactory.getGCRF()); + + final Binary64 expectedBeta = FieldVector3D.angle( + state.getPVCoordinates(FramesFactory.getGCRF()).getMomentum().normalize(), + sun.getPosition(state.getDate(), FramesFactory.getGCRF()).normalize()).negate().add(MathUtils.SEMI_PI); + assertEquals(beta.negate(), g); + assertEquals(expectedBeta.getReal(), beta.getReal(), 1e-9); + d = d.shiftedBy(86400); + } + } + + @Test + void simpleStop() { + final FieldPVCoordinatesProvider sun = CelestialBodyFactory.getSun().toFieldPVCoordinatesProvider(Binary64Field.getInstance()); + final FieldBetaAngleDetector detector = new FieldBetaAngleDetector<>( + Binary64Field.getInstance(), + Binary64.ZERO, + sun, + FramesFactory.getGCRF()); + + propagator.addEventDetector(detector); + + final FieldSpacecraftState state = propagator.propagate(date, date.shiftedBy(30 * 86400)); + assertEquals(1883928.588393031, state.getDate().durationFrom(date).getReal(), 1e-9); + + assertEquals(0, FieldBetaAngleDetector.calculateBetaAngle(state, sun).getReal(), 1e-9); + } + + @Test + void record() { + final FieldPVCoordinatesProvider sun = CelestialBodyFactory.getSun().toFieldPVCoordinatesProvider(Binary64Field.getInstance()); + final FieldRecordAndContinue handler = new FieldRecordAndContinue<>(); + final FieldBetaAngleDetector detector = new FieldBetaAngleDetector<>( + Binary64Field.getInstance(), + Binary64.ZERO, + CelestialBodyFactory.getMoon().toFieldPVCoordinatesProvider(Binary64Field.getInstance()), + FramesFactory.getGCRF()) + .withBetaThreshold(FastMath.toRadians(Binary64.ONE)) + .withCelestialProvider(sun) + .withInertialFrame(FramesFactory.getEME2000()) + .withHandler(handler); + + propagator.addEventDetector(detector); + + final FieldSpacecraftState state = propagator.propagate(date, date.shiftedBy(30 * 86400)); + assertEquals(30 * 86400, state.getDate().durationFrom(date).getReal(), 1e-9); + assertEquals(1, handler.getEvents().size()); + } +} diff --git a/src/test/java/org/orekit/propagation/events/FieldCloseEventsAbstractTest.java b/src/test/java/org/orekit/propagation/events/FieldCloseEventsAbstractTest.java index 89060b5bd7..e3a427e542 100644 --- a/src/test/java/org/orekit/propagation/events/FieldCloseEventsAbstractTest.java +++ b/src/test/java/org/orekit/propagation/events/FieldCloseEventsAbstractTest.java @@ -1959,7 +1959,7 @@ public Action eventOccurred(FieldSpacecraftState state, private abstract class AbstractTestDetector> extends FieldAbstractDetector { AbstractTestDetector(final double maxCheck, final double tolerance, final List> events) { - super(s -> maxCheck, v(tolerance), 100, new FieldRecordAndContinue<>(events)); + super(FieldAdaptableInterval.of(maxCheck), v(tolerance), 100, new FieldRecordAndContinue<>(events)); } @Override @@ -2390,7 +2390,7 @@ protected class TimeDetector extends FieldAbstractDetector { * @param eventTs event times past epoch. */ public TimeDetector(double... eventTs) { - this(s -> DEFAULT_MAXCHECK, v(DEFAULT_THRESHOLD), DEFAULT_MAX_ITER, + this(FieldAdaptableInterval.of(DEFAULT_MAXCHECK), v(DEFAULT_THRESHOLD), DEFAULT_MAX_ITER, new FieldStopOnEvent<>(), toDates(eventTs)); } @@ -2401,7 +2401,7 @@ public TimeDetector(double... eventTs) { */ @SafeVarargs public TimeDetector(FieldAbsoluteDate... eventTs) { - this(s -> DEFAULT_MAXCHECK, v(DEFAULT_THRESHOLD), DEFAULT_MAX_ITER, + this(FieldAdaptableInterval.of(DEFAULT_MAXCHECK), v(DEFAULT_THRESHOLD), DEFAULT_MAX_ITER, new FieldStopOnEvent<>(), Arrays.asList(eventTs)); } @@ -2449,13 +2449,13 @@ private class FlatDetector extends FieldAbstractDetector { private final FieldEventDetector g; public FlatDetector(double... eventTs) { - this(s -> DEFAULT_MAXCHECK, v(DEFAULT_THRESHOLD), DEFAULT_MAX_ITER, + this(FieldAdaptableInterval.of(DEFAULT_MAXCHECK), v(DEFAULT_THRESHOLD), DEFAULT_MAX_ITER, new FieldStopOnEvent<>(), new TimeDetector(eventTs)); } @SafeVarargs public FlatDetector(FieldAbsoluteDate... eventTs) { - this(s -> DEFAULT_MAXCHECK, v(DEFAULT_THRESHOLD), DEFAULT_MAX_ITER, + this(FieldAdaptableInterval.of(DEFAULT_MAXCHECK), v(DEFAULT_THRESHOLD), DEFAULT_MAX_ITER, new FieldStopOnEvent<>(), new TimeDetector(eventTs)); } @@ -2490,7 +2490,7 @@ private class ContinuousDetector extends FieldAbstractDetector> eventTs; public ContinuousDetector(double... eventTs) { - this(s -> DEFAULT_MAXCHECK, v(DEFAULT_THRESHOLD), DEFAULT_MAX_ITER, + this(FieldAdaptableInterval.of(DEFAULT_MAXCHECK), v(DEFAULT_THRESHOLD), DEFAULT_MAX_ITER, new FieldStopOnEvent<>(), toDates(eventTs)); } @@ -2624,7 +2624,7 @@ private class ResetChangesSignGenerator extends FieldAbstractDetector t0, final double y1, final double y2, final double change) { - this(s -> DEFAULT_MAXCHECK, + this(FieldAdaptableInterval.of(DEFAULT_MAXCHECK), t0.getField().getZero().newInstance(DEFAULT_THRESHOLD), DEFAULT_MAX_ITER, new FieldContinueOnEvent<>(), t0, y1, y2, change); diff --git a/src/test/java/org/orekit/propagation/events/FieldCylindricalShadowEclipseDetectorTest.java b/src/test/java/org/orekit/propagation/events/FieldCylindricalShadowEclipseDetectorTest.java new file mode 100644 index 0000000000..dacafcddaf --- /dev/null +++ b/src/test/java/org/orekit/propagation/events/FieldCylindricalShadowEclipseDetectorTest.java @@ -0,0 +1,129 @@ +package org.orekit.propagation.events; + +import org.hipparchus.CalculusFieldElement; +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.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.orekit.Utils; +import org.orekit.bodies.CelestialBodyFactory; +import org.orekit.frames.Frame; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.events.handlers.FieldContinueOnEvent; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.*; + +class FieldCylindricalShadowEclipseDetectorTest { + + @BeforeEach + public void setUp() { + Utils.setDataRoot("regular-data"); + } + + @Test + void testCreate() { + // GIVEN + final ExtendedPVCoordinatesProvider sun = CelestialBodyFactory.getSun(); + final FieldCylindricalShadowEclipseDetector eclipseDetector = new FieldCylindricalShadowEclipseDetector<>(sun, + getComplexEarthRadius(), new FieldContinueOnEvent<>()); + final FieldAdaptableInterval adaptableInterval = FieldAdaptableInterval.of(1.); + final Complex expectedThreshold = new Complex(0.1); + final int expectedMaxIter = 10; + // WHEN + final FieldCylindricalShadowEclipseDetector detector = eclipseDetector.create(adaptableInterval, expectedThreshold, + expectedMaxIter, eclipseDetector.getHandler()); + // THEN + Assertions.assertEquals(expectedMaxIter, detector.getMaxIterationCount()); + Assertions.assertEquals(expectedThreshold, detector.getThreshold()); + Assertions.assertEquals(adaptableInterval, detector.getMaxCheckInterval()); + } + + @Test + void testG0Eclipse() { + // GIVEN + final ExtendedPVCoordinatesProvider sun = new TestDirectionProvider(); + final FieldCylindricalShadowEclipseDetector eclipseDetector = new FieldCylindricalShadowEclipseDetector<>(sun, + getComplexEarthRadius(), new FieldContinueOnEvent<>()); + final FieldVector3D position = new FieldVector3D<>(ComplexField.getInstance(), + new Vector3D(1., eclipseDetector.getOccultingBodyRadius().getReal(), 0.)); + final FieldSpacecraftState mockedState = mockState(position); + // WHEN + final double g = eclipseDetector.g(mockedState).getReal(); + // THEN + Assertions.assertEquals(0., g); + } + + @Test + void testGEclipse() { + // GIVEN + final ExtendedPVCoordinatesProvider sun = new TestDirectionProvider(); + final FieldCylindricalShadowEclipseDetector eclipseDetector = new FieldCylindricalShadowEclipseDetector<>(sun, + getComplexEarthRadius(), new FieldContinueOnEvent<>()); + final FieldVector3D position = new FieldVector3D<>(ComplexField.getInstance(), new Vector3D(1e7, 0, -1e2)); + final FieldSpacecraftState mockedState = mockState(position); + // WHEN + final double g = eclipseDetector.g(mockedState).getReal(); + // THEN + Assertions.assertTrue(g < 0.); + } + + @Test + void testGNoEclipse() { + // GIVEN + final ExtendedPVCoordinatesProvider sun = new TestDirectionProvider(); + final FieldCylindricalShadowEclipseDetector eclipseDetector = new FieldCylindricalShadowEclipseDetector<>(sun, + getComplexEarthRadius(), new FieldContinueOnEvent<>()); + final FieldVector3D position = new FieldVector3D<>(ComplexField.getInstance(), new Vector3D(0., 1e4, 0.)); + final FieldSpacecraftState mockedState = mockState(position); + // WHEN + final double g = eclipseDetector.g(mockedState).getReal(); + // THEN + Assertions.assertTrue(g > 0.); + } + + @Test + void testGNoEclipse2() { + // GIVEN + final ExtendedPVCoordinatesProvider sun = new TestDirectionProvider(); + final FieldCylindricalShadowEclipseDetector eclipseDetector = new FieldCylindricalShadowEclipseDetector<>(sun, + getComplexEarthRadius(), new FieldContinueOnEvent<>()); + final FieldVector3D position = new FieldVector3D<>(ComplexField.getInstance(), + new Vector3D(-1e6, 1e3, 0.)); + final FieldSpacecraftState mockedState = mockState(position); + // WHEN + final double g = eclipseDetector.g(mockedState).getReal(); + // THEN + Assertions.assertTrue(g > 0.); + } + + @SuppressWarnings("unchecked") + private FieldSpacecraftState mockState(final FieldVector3D position) { + final FieldSpacecraftState mockedState = Mockito.mock(FieldSpacecraftState.class); + Mockito.when(mockedState.getDate()).thenReturn(FieldAbsoluteDate.getArbitraryEpoch(position.getX().getField())); + Mockito.when(mockedState.getPosition()).thenReturn(position); + return mockedState; + } + + private static Complex getComplexEarthRadius() { + return new Complex(Constants.EGM96_EARTH_EQUATORIAL_RADIUS); + } + + private static class TestDirectionProvider implements ExtendedPVCoordinatesProvider { + + @Override + public > TimeStampedFieldPVCoordinates getPVCoordinates(FieldAbsoluteDate date, Frame frame) { + return new TimeStampedFieldPVCoordinates<>(date.getField(), getPVCoordinates(date.toAbsoluteDate(), frame)); + } + + @Override + public TimeStampedPVCoordinates getPVCoordinates(AbsoluteDate date, Frame frame) { + return new TimeStampedPVCoordinates(date, new PVCoordinates(Vector3D.MINUS_I)); + } + } + +} diff --git a/src/test/java/org/orekit/propagation/events/FieldDateDetectorTest.java b/src/test/java/org/orekit/propagation/events/FieldDateDetectorTest.java index 26e5dc55f0..90a143b053 100644 --- a/src/test/java/org/orekit/propagation/events/FieldDateDetectorTest.java +++ b/src/test/java/org/orekit/propagation/events/FieldDateDetectorTest.java @@ -334,11 +334,10 @@ private > void doTestIssue935(Field field) // Min gap to seconds int maxCheck = (int) ((end - start) / 2000); - @SuppressWarnings("unchecked") FieldDateDetector dateDetector = new FieldDateDetector<>(field, getAbsoluteDateFromTimestamp(field, start)). - withMinGap(maxCheck). - withThreshold(field.getZero().newInstance(1.0e-6)). - withHandler(new FieldStopOnEvent<>()); + withMinGap(maxCheck). + withThreshold(field.getZero().newInstance(1.0e-6)). + withHandler(new FieldStopOnEvent<>()); dateDetector.addEventDate(getAbsoluteDateFromTimestamp(field, end)); // Add event detectors to orbit diff --git a/src/test/java/org/orekit/propagation/events/FieldEventDetectorTest.java b/src/test/java/org/orekit/propagation/events/FieldEventDetectorTest.java index 60c7583283..9f102b02d6 100644 --- a/src/test/java/org/orekit/propagation/events/FieldEventDetectorTest.java +++ b/src/test/java/org/orekit/propagation/events/FieldEventDetectorTest.java @@ -108,7 +108,6 @@ public void init(final FieldSpacecraftState initialState, FieldPropagator propagator = new FieldKeplerianPropagator<>(orbit); T stepSize = zero.add(60.0); - @SuppressWarnings("unchecked") final FieldDateDetector detector = new FieldDateDetector<>(field, date.shiftedBy(stepSize.multiply(5.25))).withHandler(handler); propagator.addEventDetector(detector); propagator.propagate(date.shiftedBy(stepSize.multiply(10))); @@ -138,7 +137,6 @@ private > void doTestBasicScheduling(Field FieldPropagator propagator = new FieldKeplerianPropagator<>(orbit); T stepSize = zero.add(60.0); OutOfOrderChecker checker = new OutOfOrderChecker<>(stepSize); - @SuppressWarnings("unchecked") FieldDateDetector detector = new FieldDateDetector<>(field, date.shiftedBy(stepSize.multiply(5.25))).withHandler(checker); propagator.addEventDetector(detector); propagator.setStepHandler(stepSize, checker); @@ -206,7 +204,7 @@ private > void doTestIssue108Numerical(Field propagator = new FieldNumericalPropagator<>(field, new ClassicalRungeKuttaFieldIntegrator<>(field, step)); propagator.setOrbitType(OrbitType.EQUINOCTIAL); propagator.resetInitialState(new FieldSpacecraftState<>(orbit)); - GCallsCounter counter = new GCallsCounter<>(s -> 100000.0, zero.add(1.0e-6), 20, + GCallsCounter counter = new GCallsCounter<>(FieldAdaptableInterval.of(100000.0), zero.add(1.0e-6), 20, new FieldStopOnEvent()); propagator.addEventDetector(counter); propagator.propagate(date.shiftedBy(step.multiply(n))); @@ -233,7 +231,7 @@ private > void doTestIssue108Analytical(Field< final T step = zero.add(60.0); final int n = 100; FieldKeplerianPropagator propagator = new FieldKeplerianPropagator<>(orbit); - GCallsCounter counter = new GCallsCounter<>(s -> 100000.0, zero.add(1.0e-6), 20, + GCallsCounter counter = new GCallsCounter<>(FieldAdaptableInterval.of(100000.0), zero.add(1.0e-6), 20, new FieldStopOnEvent()); propagator.addEventDetector(counter); propagator.setStepHandler(step, currentState -> {}); @@ -304,11 +302,11 @@ private > void doTestNoisyGFunction(Field f zero.add(1920.6332221785074), zero.add(-5172.2177085540500))), eme2000, initialDate, zero.add(Constants.WGS84_EARTH_MU))); - k2.addEventDetector(new FieldCloseApproachDetector<>(s -> 2015.243454166727, zero.add(0.0001), 100, + k2.addEventDetector(new FieldCloseApproachDetector<>(FieldAdaptableInterval.of(2015.243454166727), zero.add(0.0001), 100, new FieldContinueOnEvent(), k1)); k2.addEventDetector(new FieldDateDetector<>(field, interruptDates). - withMaxCheck(s -> Constants.JULIAN_DAY). + withMaxCheck(FieldAdaptableInterval.of(Constants.JULIAN_DAY)). withThreshold(field.getZero().newInstance(1.0e-6))); FieldSpacecraftState s = k2.propagate(startDate, targetDate); Assertions.assertEquals(0.0, interruptDates[0].durationFrom(s.getDate()).getReal(), 1.1e-6); @@ -349,7 +347,6 @@ public void testWrappedException() { doTestWrappedException(Binary64Field.getInstance()); } - @SuppressWarnings("unchecked") private > void doTestWrappedException(Field field) { final T zero = field.getZero(); final Throwable dummyCause = new RuntimeException(); @@ -410,7 +407,7 @@ public int getMaxIterationCount() { @Override public FieldAdaptableInterval getMaxCheckInterval() { - return s -> 60; + return FieldAdaptableInterval.of(60.); } @Override @@ -503,7 +500,6 @@ private > void doTestScheduling(final Field }); for (int i = 0; i < 10; ++i) { - @SuppressWarnings("unchecked") FieldDateDetector detector = new FieldDateDetector<>(field, initialDate.shiftedBy(0.0625 * (i + 1))). withHandler((state, d, increasing) -> { checker.callDate(state.getDate()); diff --git a/src/test/java/org/orekit/propagation/events/FieldEventEnablingPredicateFilterTest.java b/src/test/java/org/orekit/propagation/events/FieldEventEnablingPredicateFilterTest.java index b00f42091f..9cd54f07e7 100644 --- a/src/test/java/org/orekit/propagation/events/FieldEventEnablingPredicateFilterTest.java +++ b/src/test/java/org/orekit/propagation/events/FieldEventEnablingPredicateFilterTest.java @@ -173,7 +173,6 @@ public boolean eventIsEnabled(final FieldSpacecraftState state, @Test public void testResetState() { final List> reset = new ArrayList<>(); - @SuppressWarnings("unchecked") FieldDateDetector raw = new FieldDateDetector<>(Binary64Field.getInstance(), orbit.getDate().shiftedBy(3600.0)). withMaxCheck(1000.0). withHandler(new FieldEventHandler() { @@ -222,7 +221,6 @@ public void testExceedHistoryForward() throws IOException { final double period = 900.0; // the raw detector should trigger one event at each 900s period - @SuppressWarnings("unchecked") final FieldDateDetector raw = new FieldDateDetector<>(Binary64Field.getInstance(), orbit.getDate().shiftedBy(-0.5 * period)). withMaxCheck(period / 3). @@ -277,7 +275,6 @@ public void testExceedHistoryBackward() throws IOException { final double period = 900.0; // the raw detector should trigger one event at each 900s period - @SuppressWarnings("unchecked") final FieldDateDetector raw = new FieldDateDetector<>(Binary64Field.getInstance(), orbit.getDate().shiftedBy(+0.5 * period)). withMaxCheck(period / 3). @@ -330,7 +327,6 @@ public boolean eventIsEnabled(FieldSpacecraftState state, @Test public void testGenerics() { // setup - @SuppressWarnings("unchecked") FieldDateDetector detector = new FieldDateDetector<>(orbit.getDate().getField(), orbit.getDate()); FieldEnablingPredicate predicate = (state, eventDetector, g) -> true; diff --git a/src/test/java/org/orekit/propagation/events/FieldExtremumApproachDetectorTest.java b/src/test/java/org/orekit/propagation/events/FieldExtremumApproachDetectorTest.java new file mode 100644 index 0000000000..6457d1f5b9 --- /dev/null +++ b/src/test/java/org/orekit/propagation/events/FieldExtremumApproachDetectorTest.java @@ -0,0 +1,141 @@ +package org.orekit.propagation.events; + +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.Utils; +import org.orekit.bodies.CelestialBodyFactory; +import org.orekit.frames.Frame; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.FieldKeplerianOrbit; +import org.orekit.orbits.FieldOrbit; +import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.FieldPropagator; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.analytical.FieldKeplerianPropagator; +import org.orekit.propagation.events.handlers.FieldStopOnEvent; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.utils.FieldPVCoordinatesProvider; +import org.orekit.utils.PVCoordinatesProvider; + +import static org.mockito.Mockito.mock; + +class FieldExtremumApproachDetectorTest { + /** + * Test the detector on a keplerian orbit and detect extremum approach with Earth. + */ + @Test + public void testStopPropagationClosestApproachByDefault() { + // Given + // Loading Orekit data + Utils.setDataRoot("regular-data"); + + // Generating orbit + final Field field = Binary64Field.getInstance(); + final FieldAbsoluteDate initialDate = new FieldAbsoluteDate<>(field, new AbsoluteDate()); + final Frame frame = FramesFactory.getEME2000(); + final Binary64 mu = new Binary64(398600e9); //m**3/s**2 + + final Binary64 rp = new Binary64((6378 + 400) * 1000); //m + final Binary64 ra = new Binary64((6378 + 800) * 1000); //m + + final Binary64 a = ra.add(rp).divide(2); //m + final Binary64 e = ra.subtract(rp).divide(ra.add(rp)); //m + final Binary64 i = new Binary64(0); //rad + final Binary64 pa = new Binary64(0); //rad + final Binary64 raan = new Binary64(0); //rad + final Binary64 anomaly = new Binary64(0); //rad + final FieldOrbit orbit = + new FieldKeplerianOrbit<>(a, e, i, pa, raan, anomaly, PositionAngleType.TRUE, frame, initialDate, mu); + + // Will detect extremum approaches with Earth + final PVCoordinatesProvider earthPVProvider = CelestialBodyFactory.getEarth(); + + // Initializing detector + final FieldExtremumApproachDetector detector = new FieldExtremumApproachDetector<>(field, earthPVProvider); + + // Initializing propagator + final FieldPropagator propagator = new FieldKeplerianPropagator<>(orbit); + propagator.addEventDetector(detector); + + // When + final FieldSpacecraftState stateAtEvent = + propagator.propagate(initialDate.shiftedBy(orbit.getKeplerianPeriod().multiply(2.))); + + // Then + Assertions.assertEquals(stateAtEvent.getDate().durationFrom(initialDate).getReal(), + orbit.getKeplerianPeriod().getReal(), 1e-9); + + } + + /** + * Test the detector on a keplerian orbit and detect extremum approach with Earth. + */ + @Test + public void testStopPropagationFarthestApproachWithHandler() { + + // Given + // Loading Orekit data + Utils.setDataRoot("regular-data"); + + // Generating orbit + final Field field = Binary64Field.getInstance(); + final FieldAbsoluteDate initialDate = new FieldAbsoluteDate<>(field, new AbsoluteDate()); + final Frame frame = FramesFactory.getEME2000(); + final Binary64 mu = new Binary64(398600e9); //m**3/s**2 + + final Binary64 rp = new Binary64(6378 + 400 * 1000); //m + final Binary64 ra = new Binary64((6378 + 800) * 1000); //m + + final Binary64 a = ra.add(rp).divide(2); //m + final Binary64 e = ra.subtract(rp).divide(ra.add(rp)); //m + final Binary64 i = new Binary64(0.); //rad + final Binary64 pa = new Binary64(0); //rad + final Binary64 raan = new Binary64(0); //rad + final Binary64 anomaly = new Binary64(0); //rad + final FieldOrbit orbit = + new FieldKeplerianOrbit<>(a, e, i, pa, raan, anomaly, PositionAngleType.TRUE, frame, initialDate, mu); + + // Will detect extremum approaches with Earth + final PVCoordinatesProvider earthPVProvider = CelestialBodyFactory.getEarth(); + + // Initializing detector with custom handler + final FieldExtremumApproachDetector detector = + new FieldExtremumApproachDetector<>(field, earthPVProvider).withHandler(new FieldStopOnEvent<>()); + + // Initializing propagator + final FieldPropagator propagator = new FieldKeplerianPropagator<>(orbit); + propagator.addEventDetector(detector); + + // When + final FieldSpacecraftState stateAtEvent = + propagator.propagate(initialDate.shiftedBy(orbit.getKeplerianPeriod().multiply(2))); + + // Then + Assertions.assertEquals(stateAtEvent.getDate().durationFrom(initialDate).getReal(), + orbit.getKeplerianPeriod().divide(2).getReal(), 1e-7); + + } + + @Test + @SuppressWarnings("unchecked") + void testSecondaryPVCoordinatesProviderGetter() { + // Given + final Field field = Binary64Field.getInstance(); + final FieldPVCoordinatesProvider secondaryPVProvider = mock(FieldPVCoordinatesProvider.class); + + final FieldExtremumApproachDetector extremumApproachDetector = + new FieldExtremumApproachDetector<>(field, secondaryPVProvider); + + // When + final FieldPVCoordinatesProvider returnedSecondaryPVProvider = + extremumApproachDetector.getSecondaryPVProvider(); + + // Then + Assertions.assertEquals(secondaryPVProvider, returnedSecondaryPVProvider); + } + +} diff --git a/src/test/java/org/orekit/propagation/events/FieldLatitudeRangeCrossingDetectorTest.java b/src/test/java/org/orekit/propagation/events/FieldLatitudeRangeCrossingDetectorTest.java new file mode 100644 index 0000000000..f1b5a5f8a9 --- /dev/null +++ b/src/test/java/org/orekit/propagation/events/FieldLatitudeRangeCrossingDetectorTest.java @@ -0,0 +1,178 @@ +/* Copyright 2023-2024 Alberto Ferrero + * 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. + * Alberto Ferrero 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.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; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.orekit.Utils; +import org.orekit.bodies.OneAxisEllipsoid; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.FieldEquinoctialOrbit; +import org.orekit.orbits.FieldOrbit; +import org.orekit.propagation.FieldPropagator; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.analytical.FieldEcksteinHechlerPropagator; +import org.orekit.propagation.events.FieldEventsLogger.FieldLoggedEvent; +import org.orekit.propagation.events.handlers.FieldContinueOnEvent; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.TimeScale; +import org.orekit.time.TimeScalesFactory; +import org.orekit.utils.Constants; +import org.orekit.utils.FieldPVCoordinates; +import org.orekit.utils.IERSConventions; +import org.orekit.utils.PVCoordinates; + +/** Unit tests for {@link FieldLatitudeRangeCrossingDetector}. */ +public class FieldLatitudeRangeCrossingDetectorTest { + + /** Arbitrary Field. */ + private static final Binary64Field field = Binary64Field.getInstance(); + + @Test + public void testRegularCrossing() { + + final OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, + Constants.WGS84_EARTH_FLATTENING, + FramesFactory.getITRF(IERSConventions.IERS_2010, true)); + + FieldLatitudeRangeCrossingDetector d = + new FieldLatitudeRangeCrossingDetector<>(v(60.0), v(1.e-6), + earth, FastMath.toRadians(50.0), FastMath.toRadians(60.0)). + withHandler(new FieldContinueOnEvent<>()); + + Assertions.assertEquals(60.0, d.getMaxCheckInterval().currentInterval(null), 1.0e-15); + Assertions.assertEquals(1.0e-6, d.getThreshold().getReal(), 1.0e-15); + Assertions.assertEquals(50.0, FastMath.toDegrees(d.getFromLatitude()), 1.0e-14); + Assertions.assertEquals(60.0, FastMath.toDegrees(d.getToLatitude()), 1.0e-14); + Assertions.assertEquals(AbstractDetector.DEFAULT_MAX_ITER, d.getMaxIterationCount()); + + final TimeScale utc = TimeScalesFactory.getUTC(); + final Vector3D position = new Vector3D(-6142438.668, 3492467.56, -25767.257); + final Vector3D velocity = new Vector3D(505.848, 942.781, 7435.922); + final FieldAbsoluteDate date = new FieldAbsoluteDate<>(field, 2003, 9, 16, utc); + final FieldOrbit orbit = new FieldEquinoctialOrbit<>( + new FieldPVCoordinates<>(v(1), new PVCoordinates(position, velocity)), + FramesFactory.getEME2000(), date, + v(Constants.EIGEN5C_EARTH_MU)); + + FieldPropagator propagator = + new FieldEcksteinHechlerPropagator<>(orbit, + Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS, + v(Constants.EIGEN5C_EARTH_MU), + Constants.EIGEN5C_EARTH_C20, + Constants.EIGEN5C_EARTH_C30, + Constants.EIGEN5C_EARTH_C40, + Constants.EIGEN5C_EARTH_C50, + Constants.EIGEN5C_EARTH_C60); + + FieldEventsLogger logger = new FieldEventsLogger<>(); + propagator.addEventDetector(logger.monitorDetector(d)); + + propagator.propagate(date.shiftedBy(Constants.JULIAN_DAY)); + for (FieldLoggedEvent e : logger.getLoggedEvents()) { + FieldSpacecraftState state = e.getState(); + double latitude = earth.transform(state.getPosition(earth.getBodyFrame()), + earth.getBodyFrame(), date).getLatitude().getReal(); + if (e.isIncreasing()) { + if (state.getPVCoordinates().getVelocity().getZ().getReal() < 0) { + // entering northward + Assertions.assertEquals(60.0, FastMath.toDegrees(latitude), FastMath.toRadians(1e-4)); + } else { + // entering southward + Assertions.assertEquals(50.0, FastMath.toDegrees(latitude), FastMath.toRadians(1e-4)); + } + } else { + if (state.getPVCoordinates().getVelocity().getZ().getReal() < 0) { + // exiting southward + Assertions.assertEquals(50.0, FastMath.toDegrees(latitude), FastMath.toRadians(1e-4)); + } else { + // exiting northward + Assertions.assertEquals(60.0, FastMath.toDegrees(latitude), FastMath.toRadians(1e-4)); + } + } + } + Assertions.assertEquals(30 * 2, logger.getLoggedEvents().size()); + + } + + @Test + public void testNoCrossing() { + + final OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, + Constants.WGS84_EARTH_FLATTENING, + FramesFactory.getITRF(IERSConventions.IERS_2010, true)); + + FieldLatitudeRangeCrossingDetector d = + new FieldLatitudeRangeCrossingDetector<>(v(10.0), v(1.e-6), + earth, FastMath.toRadians(82.0), FastMath.toRadians(87.0)). + withHandler(new FieldContinueOnEvent<>()); + + Assertions.assertEquals(10.0, d.getMaxCheckInterval().currentInterval(null), 1.0e-15); + Assertions.assertEquals(1.0e-6, d.getThreshold().getReal(), 1.0e-15); + Assertions.assertEquals(82.0, FastMath.toDegrees(d.getFromLatitude()), 1.0e-14); + Assertions.assertEquals(87.0, FastMath.toDegrees(d.getToLatitude()), 1.0e-14); + Assertions.assertEquals(AbstractDetector.DEFAULT_MAX_ITER, d.getMaxIterationCount()); + + final TimeScale utc = TimeScalesFactory.getUTC(); + final Vector3D position = new Vector3D(-6142438.668, 3492467.56, -25767.257); + final Vector3D velocity = new Vector3D(505.848, 942.781, 7435.922); + final FieldAbsoluteDate date = new FieldAbsoluteDate<>(field, 2003, 9, 16, utc); + final FieldOrbit orbit = new FieldEquinoctialOrbit<>( + new FieldPVCoordinates<>(v(1), new PVCoordinates(position, velocity)), + FramesFactory.getEME2000(), date, + v(Constants.EIGEN5C_EARTH_MU)); + + FieldPropagator propagator = + new FieldEcksteinHechlerPropagator<>(orbit, + Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS, + v(Constants.EIGEN5C_EARTH_MU), + Constants.EIGEN5C_EARTH_C20, + Constants.EIGEN5C_EARTH_C30, + Constants.EIGEN5C_EARTH_C40, + Constants.EIGEN5C_EARTH_C50, + Constants.EIGEN5C_EARTH_C60); + + FieldEventsLogger logger = new FieldEventsLogger(); + propagator.addEventDetector(logger.monitorDetector(d)); + + propagator.propagate(date.shiftedBy(Constants.JULIAN_DAY)); + Assertions.assertEquals(0, logger.getLoggedEvents().size()); + + } + + /** + * Convert double to field value. + * + * @param value to box. + * @return boxed value. + */ + private static Binary64 v(double value) { + return new Binary64(value); + } + + @BeforeEach + public void setUp() { + Utils.setDataRoot("regular-data"); + } + +} + diff --git a/src/test/java/org/orekit/propagation/events/FieldLongitudeCrossingDetectorTest.java b/src/test/java/org/orekit/propagation/events/FieldLongitudeCrossingDetectorTest.java index dececccca1..c5a9e6c7b0 100644 --- a/src/test/java/org/orekit/propagation/events/FieldLongitudeCrossingDetectorTest.java +++ b/src/test/java/org/orekit/propagation/events/FieldLongitudeCrossingDetectorTest.java @@ -1,3 +1,20 @@ +/* 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.events; import static org.orekit.orbits.PositionAngleType.MEAN; diff --git a/src/test/java/org/orekit/propagation/events/FieldLongitudeRangeCrossingDetectorTest.java b/src/test/java/org/orekit/propagation/events/FieldLongitudeRangeCrossingDetectorTest.java new file mode 100644 index 0000000000..ee7fea5556 --- /dev/null +++ b/src/test/java/org/orekit/propagation/events/FieldLongitudeRangeCrossingDetectorTest.java @@ -0,0 +1,179 @@ +/* Copyright 2023-2024 Alberto Ferrero + * 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. + * Alberto Ferrero 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 static org.orekit.orbits.PositionAngleType.MEAN; + +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; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.orekit.Utils; +import org.orekit.bodies.FieldGeodeticPoint; +import org.orekit.bodies.OneAxisEllipsoid; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.FieldEquinoctialOrbit; +import org.orekit.orbits.FieldKeplerianOrbit; +import org.orekit.orbits.FieldOrbit; +import org.orekit.propagation.FieldPropagator; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.analytical.FieldEcksteinHechlerPropagator; +import org.orekit.propagation.analytical.FieldKeplerianPropagator; +import org.orekit.propagation.events.handlers.FieldContinueOnEvent; +import org.orekit.time.FieldAbsoluteDate; +import org.orekit.time.TimeScale; +import org.orekit.time.TimeScalesFactory; +import org.orekit.utils.Constants; +import org.orekit.utils.FieldPVCoordinates; +import org.orekit.utils.IERSConventions; +import org.orekit.utils.PVCoordinates; + +/** Unit tests for {@link FieldLongitudeRangeCrossingDetector}. */ +public class FieldLongitudeRangeCrossingDetectorTest { + + /** + * Arbitrary Field. + */ + private static final Binary64Field field = Binary64Field.getInstance(); + + + @Test + public void testRegularCrossing() { + + final OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, + Constants.WGS84_EARTH_FLATTENING, + FramesFactory.getITRF(IERSConventions.IERS_2010, true)); + + FieldLongitudeRangeCrossingDetector d = + new FieldLongitudeRangeCrossingDetector<>(v(60.0), v(1.e-6), earth, + FastMath.toRadians(10.0), + FastMath.toRadians(18.0)). + withHandler(new FieldContinueOnEvent<>()); + + Assertions.assertEquals(60.0, d.getMaxCheckInterval().currentInterval(null), 1.0e-15); + Assertions.assertEquals(1.0e-6, d.getThreshold().getReal(), 1.0e-15); + Assertions.assertEquals(10.0, FastMath.toDegrees(d.getFromLongitude()), 1.0e-14); + Assertions.assertEquals(18.0, FastMath.toDegrees(d.getToLongitude()), 1.0e-14); + Assertions.assertEquals(AbstractDetector.DEFAULT_MAX_ITER, d.getMaxIterationCount()); + Assertions.assertSame(earth, d.getBody()); + + final TimeScale utc = TimeScalesFactory.getUTC(); + final Vector3D position = new Vector3D(-6142438.668, 3492467.56, -25767.257); + final Vector3D velocity = new Vector3D(505.848, 942.781, 7435.922); + final FieldAbsoluteDate date = new FieldAbsoluteDate<>(field, 2003, 9, 16, utc); + final FieldOrbit orbit = new FieldEquinoctialOrbit<>( + new FieldPVCoordinates<>(v(1), new PVCoordinates(position, velocity)), + FramesFactory.getEME2000(), date, + v(Constants.EIGEN5C_EARTH_MU)); + + FieldPropagator propagator = + new FieldEcksteinHechlerPropagator<>(orbit, + Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS, + v(Constants.EIGEN5C_EARTH_MU), + Constants.EIGEN5C_EARTH_C20, + Constants.EIGEN5C_EARTH_C30, + Constants.EIGEN5C_EARTH_C40, + Constants.EIGEN5C_EARTH_C50, + Constants.EIGEN5C_EARTH_C60); + + FieldEventsLogger logger = new FieldEventsLogger<>(); + propagator.addEventDetector(logger.monitorDetector(d)); + + propagator.propagate(date.shiftedBy(Constants.JULIAN_DAY)); + for (FieldEventsLogger.FieldLoggedEvent e : logger.getLoggedEvents()) { + FieldSpacecraftState state = e.getState(); + double longitude = earth.transform(state.getPVCoordinates(earth.getBodyFrame()).getPosition(), + earth.getBodyFrame(), date).getLongitude().getReal(); + if (e.isIncreasing()) { + // retrograde orbit, enter + Assertions.assertEquals(18.0, FastMath.toDegrees(longitude), 3.5e-7); + } else { + // retrograde orbit, exit + Assertions.assertEquals(10.0, FastMath.toDegrees(longitude), 3.5e-7); + } + Assertions.assertEquals(28, logger.getLoggedEvents().size()); + } + } + + @Test + public void testZigZag() { + + final OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, + Constants.WGS84_EARTH_FLATTENING, + FramesFactory.getITRF(IERSConventions.IERS_2010, true)); + + FieldLongitudeRangeCrossingDetector d = + new FieldLongitudeRangeCrossingDetector<>(v(600.0), v(1.e-6), earth, + FastMath.toRadians(-120.0), + FastMath.toRadians(-100.0)). + withHandler(new FieldContinueOnEvent<>()); + + Assertions.assertEquals(600.0, d.getMaxCheckInterval().currentInterval(null), 1.0e-15); + Assertions.assertEquals(1.0e-6, d.getThreshold().getReal(), 1.0e-15); + Assertions.assertEquals(-120.0, FastMath.toDegrees(d.getFromLongitude()), 1.0e-8); + Assertions.assertEquals(-100.0, FastMath.toDegrees(d.getToLongitude()), 1.0e-8); + Assertions.assertEquals(AbstractDetector.DEFAULT_MAX_ITER, d.getMaxIterationCount()); + + FieldAbsoluteDate date = FieldAbsoluteDate.getJ2000Epoch(field); + final FieldOrbit orbit = + new FieldKeplerianOrbit<>(v(26464560.0), v(0.8311), v(0.122138), v(3.10686), v(1.00681), + v(0.048363), MEAN, + FramesFactory.getEME2000(), + date, + v(Constants.EIGEN5C_EARTH_MU)); + + + FieldPropagator propagator = new FieldKeplerianPropagator<>(orbit); + + FieldEventsLogger logger = new FieldEventsLogger<>(); + propagator.addEventDetector(logger.monitorDetector(d)); + + propagator.propagate(orbit.getDate().shiftedBy(1 * Constants.JULIAN_DAY)); + Assertions.assertEquals(4, logger.getLoggedEvents().size()); + + // eccentric orbit, at apogee Earth rotation makes as reversed effect + double[] expectedLongitude = new double[]{d.getFromLongitude(), d.getToLongitude(), + d.getToLongitude(), d.getFromLongitude()}; + for (int i = 0; i < 4; ++i) { + FieldSpacecraftState state = logger.getLoggedEvents().get(i).getState(); + FieldGeodeticPoint gp = earth.transform(state.getPVCoordinates(earth.getBodyFrame()).getPosition(), + earth.getBodyFrame(), date); + Assertions.assertEquals(expectedLongitude[i], gp.getLongitude().getReal(), 1.2e-9); + } + + } + + /** + * Convert double to field value. + * + * @param value to box. + * @return boxed value. + */ + private static Binary64 v(double value) { + return new Binary64(value); + } + + @BeforeEach + public void setUp() { + Utils.setDataRoot("regular-data"); + } + +} + diff --git a/src/test/java/org/orekit/propagation/events/FieldNegateDetectorTest.java b/src/test/java/org/orekit/propagation/events/FieldNegateDetectorTest.java index 9c28104536..9d8cea39bb 100644 --- a/src/test/java/org/orekit/propagation/events/FieldNegateDetectorTest.java +++ b/src/test/java/org/orekit/propagation/events/FieldNegateDetectorTest.java @@ -47,7 +47,7 @@ private > void doTestInit(final Field field //setup @SuppressWarnings("unchecked") FieldEventDetector a = Mockito.mock(FieldEventDetector.class); - Mockito.when(a.getMaxCheckInterval()).thenReturn(s -> AbstractDetector.DEFAULT_MAXCHECK); + Mockito.when(a.getMaxCheckInterval()).thenReturn(FieldAdaptableInterval.of(AbstractDetector.DEFAULT_MAXCHECK)); Mockito.when(a.getThreshold()).thenReturn(field.getZero().newInstance(AbstractDetector.DEFAULT_THRESHOLD)); @SuppressWarnings("unchecked") FieldEventHandler c = Mockito.mock(FieldEventHandler.class); @@ -77,7 +77,7 @@ private > void doTestG(final Field field) { //setup @SuppressWarnings("unchecked") FieldEventDetector a = Mockito.mock(FieldEventDetector.class); - Mockito.when(a.getMaxCheckInterval()).thenReturn(s -> AbstractDetector.DEFAULT_MAXCHECK); + Mockito.when(a.getMaxCheckInterval()).thenReturn(FieldAdaptableInterval.of(AbstractDetector.DEFAULT_MAXCHECK)); Mockito.when(a.getThreshold()).thenReturn(field.getZero().newInstance(AbstractDetector.DEFAULT_THRESHOLD)); FieldNegateDetector detector = new FieldNegateDetector<>(a); @SuppressWarnings("unchecked") @@ -101,7 +101,7 @@ private > void doTestCreate(final Field fie //setup @SuppressWarnings("unchecked") FieldEventDetector a = Mockito.mock(FieldEventDetector.class); - Mockito.when(a.getMaxCheckInterval()).thenReturn(s -> AbstractDetector.DEFAULT_MAXCHECK); + Mockito.when(a.getMaxCheckInterval()).thenReturn(FieldAdaptableInterval.of(AbstractDetector.DEFAULT_MAXCHECK)); Mockito.when(a.getThreshold()).thenReturn(field.getZero().newInstance(AbstractDetector.DEFAULT_THRESHOLD)); FieldNegateDetector detector = new FieldNegateDetector<>(a); diff --git a/src/test/java/org/orekit/propagation/events/FieldOrDetectorTest.java b/src/test/java/org/orekit/propagation/events/FieldOrDetectorTest.java index fa6cf11a0b..7d248734d3 100644 --- a/src/test/java/org/orekit/propagation/events/FieldOrDetectorTest.java +++ b/src/test/java/org/orekit/propagation/events/FieldOrDetectorTest.java @@ -129,10 +129,10 @@ public void testCancellation() { public void testInit() { // setup FieldEventDetector a = Mockito.mock(FieldEventDetector.class); - Mockito.when(a.getMaxCheckInterval()).thenReturn(s -> AbstractDetector.DEFAULT_MAXCHECK); + Mockito.when(a.getMaxCheckInterval()).thenReturn(FieldAdaptableInterval.of(AbstractDetector.DEFAULT_MAXCHECK)); Mockito.when(a.getThreshold()).thenReturn(new Binary64(AbstractDetector.DEFAULT_THRESHOLD)); FieldEventDetector b = Mockito.mock(FieldEventDetector.class); - Mockito.when(b.getMaxCheckInterval()).thenReturn(s -> AbstractDetector.DEFAULT_MAXCHECK); + Mockito.when(b.getMaxCheckInterval()).thenReturn(FieldAdaptableInterval.of(AbstractDetector.DEFAULT_MAXCHECK)); Mockito.when(b.getThreshold()).thenReturn(new Binary64(AbstractDetector.DEFAULT_THRESHOLD)); FieldEventHandler c = Mockito.mock(FieldEventHandler.class); FieldBooleanDetector or = FieldBooleanDetector.orCombine(a, b).withHandler(c); @@ -185,7 +185,7 @@ public Binary64 getThreshold() { @Override public FieldAdaptableInterval getMaxCheckInterval() { - return s -> AbstractDetector.DEFAULT_MAXCHECK; + return FieldAdaptableInterval.of(AbstractDetector.DEFAULT_MAXCHECK); } @Override diff --git a/src/test/java/org/orekit/propagation/events/FieldRelativeDistanceDetectorTest.java b/src/test/java/org/orekit/propagation/events/FieldRelativeDistanceDetectorTest.java new file mode 100644 index 0000000000..7db7d13ac6 --- /dev/null +++ b/src/test/java/org/orekit/propagation/events/FieldRelativeDistanceDetectorTest.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; + +import org.hipparchus.complex.Complex; +import org.hipparchus.geometry.euclidean.threed.Vector3D; +import org.hipparchus.util.Binary64; +import org.hipparchus.util.Binary64Field; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.CartesianOrbit; +import org.orekit.orbits.FieldCartesianOrbit; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.analytical.FieldKeplerianPropagator; +import org.orekit.propagation.analytical.KeplerianPropagator; +import org.orekit.propagation.events.handlers.FieldStopOnDecreasing; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.Constants; +import org.orekit.utils.FieldPVCoordinatesProvider; +import org.orekit.utils.TimeStampedPVCoordinates; + +class FieldRelativeDistanceDetectorTest { + + @Test + void testGetDistanceThreshold() { + // GIVEN + final Complex expectedDistanceThreshold = Complex.ONE; + final FieldRelativeDistanceDetector distanceDetector = new FieldRelativeDistanceDetector<>( + mockProvider(), expectedDistanceThreshold); + // WHEN + final Complex actualDistanceThreshold = distanceDetector.getDistanceThreshold(); + // THEN + Assertions.assertEquals(expectedDistanceThreshold, actualDistanceThreshold); + } + + @Test + void testCreate() { + // GIVEN + final Complex distanceThreshold = Complex.ONE; + final FieldRelativeDistanceDetector distanceDetector = new FieldRelativeDistanceDetector<>( + mockProvider(), distanceThreshold); + final FieldStopOnDecreasing expectedHandler = new FieldStopOnDecreasing<>(); + // WHEN + final FieldRelativeDistanceDetector detector = distanceDetector.create(distanceDetector.getMaxCheckInterval(), + distanceDetector.getThreshold(), distanceDetector.getMaxIterationCount(), expectedHandler); + // THEN + Assertions.assertEquals(expectedHandler, detector.getHandler()); + } + + @SuppressWarnings("unchecked") + private FieldPVCoordinatesProvider mockProvider() { + return Mockito.mock(FieldPVCoordinatesProvider.class); + } + + @Test + void testG() { + // GIVEN + final CartesianOrbit initialOrbit = createOrbit(new Vector3D(1e7, 0, 0)); + final double distanceThreshold = 0.; + final Binary64Field field = Binary64Field.getInstance(); + final FieldKeplerianPropagator fieldKeplerianPropagator = new FieldKeplerianPropagator<>( + new FieldCartesianOrbit<>(field, initialOrbit)); + final FieldRelativeDistanceDetector fieldRelativeDistanceDetector = new FieldRelativeDistanceDetector<>( + fieldKeplerianPropagator, field.getZero().newInstance(distanceThreshold)); + final CartesianOrbit orbit = createOrbit(new Vector3D(1e7, 1e3, 2e3)); + final FieldCartesianOrbit fieldOrbit = new FieldCartesianOrbit<>(field, orbit); + final FieldSpacecraftState fieldSpacecraftState = new FieldSpacecraftState<>(fieldOrbit); + // WHEN + final Binary64 g = fieldRelativeDistanceDetector.g(fieldSpacecraftState); + final double actualDistance = g.getReal(); + // THEN + final RelativeDistanceDetector relativeDistanceDetector = new RelativeDistanceDetector( + new KeplerianPropagator(initialOrbit), distanceThreshold); + final double expectedDistance = relativeDistanceDetector.g(fieldSpacecraftState.toSpacecraftState()); + Assertions.assertEquals(expectedDistance, actualDistance); + } + + private CartesianOrbit createOrbit(final Vector3D position) { + final Vector3D velocity = new Vector3D(0., 6e3, -1e2); + final TimeStampedPVCoordinates pvCoordinates = new TimeStampedPVCoordinates(AbsoluteDate.ARBITRARY_EPOCH, + position, velocity); + return new CartesianOrbit(pvCoordinates, FramesFactory.getGCRF(), Constants.EGM96_EARTH_MU); + } + +} diff --git a/src/test/java/org/orekit/propagation/events/FootprintOverlapDetectorTest.java b/src/test/java/org/orekit/propagation/events/FootprintOverlapDetectorTest.java index 079a071a32..5e1b37c036 100644 --- a/src/test/java/org/orekit/propagation/events/FootprintOverlapDetectorTest.java +++ b/src/test/java/org/orekit/propagation/events/FootprintOverlapDetectorTest.java @@ -54,7 +54,6 @@ import org.orekit.utils.IERSConventions; import org.orekit.utils.PVCoordinates; -import java.io.IOException; import java.lang.reflect.Field; import java.util.List; @@ -89,7 +88,7 @@ public void testSampleAroundPole() throws NoSuchFieldException, IllegalAccessExc } @Test - public void testRightForwardView() throws IOException { + public void testRightForwardView() { propagator.setAttitudeProvider(new LofOffset(initialOrbit.getFrame(), LOFType.LVLH_CCSDS, RotationOrder.XYZ, @@ -130,7 +129,7 @@ public void testRightForwardView() throws IOException { // above Saint-Chamond (Loire), pointing near Saint-Dié-des-Vosges (Vosges) towards North-East checkEventPair(events.get(2), events.get(3), - 639113.0751, 38.8681, 45.5212, 4.4866, 48.4066, 7.1546); + 639113.5532, 38.3899, 45.5356, 4.4813, 48.4211, 7.1499); // event is on a descending orbit, so the pointing direction, // taking roll and pitch offsets, is towards South-West with respect to spacecraft diff --git a/src/test/java/org/orekit/propagation/events/LatitudeRangeCrossingDetectorTest.java b/src/test/java/org/orekit/propagation/events/LatitudeRangeCrossingDetectorTest.java new file mode 100644 index 0000000000..e9e400b142 --- /dev/null +++ b/src/test/java/org/orekit/propagation/events/LatitudeRangeCrossingDetectorTest.java @@ -0,0 +1,159 @@ +/* Copyright 2023-2024 Alberto Ferrero + * 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. + * Alberto Ferrero 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.geometry.euclidean.threed.Vector3D; +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.OneAxisEllipsoid; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.EquinoctialOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.propagation.Propagator; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.analytical.EcksteinHechlerPropagator; +import org.orekit.propagation.events.EventsLogger.LoggedEvent; +import org.orekit.propagation.events.handlers.ContinueOnEvent; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.TimeScale; +import org.orekit.time.TimeScalesFactory; +import org.orekit.utils.Constants; +import org.orekit.utils.IERSConventions; +import org.orekit.utils.PVCoordinates; + +public class LatitudeRangeCrossingDetectorTest { + + @Test + public void testRegularCrossing() { + + final OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, + Constants.WGS84_EARTH_FLATTENING, + FramesFactory.getITRF(IERSConventions.IERS_2010, true)); + + LatitudeRangeCrossingDetector d = + new LatitudeRangeCrossingDetector(60.0, 1.e-6, earth, + FastMath.toRadians(50.0), FastMath.toRadians(60.0)). + withHandler(new ContinueOnEvent()); + + Assertions.assertEquals(60.0, d.getMaxCheckInterval().currentInterval(null), 1.0e-15); + Assertions.assertEquals(1.0e-6, d.getThreshold(), 1.0e-15); + Assertions.assertEquals(50.0, FastMath.toDegrees(d.getFromLatitude()), 1.0e-14); + Assertions.assertEquals(60.0, FastMath.toDegrees(d.getToLatitude()), 1.0e-14); + Assertions.assertEquals(AbstractDetector.DEFAULT_MAX_ITER, d.getMaxIterationCount()); + + final TimeScale utc = TimeScalesFactory.getUTC(); + final Vector3D position = new Vector3D(-6142438.668, 3492467.56, -25767.257); + final Vector3D velocity = new Vector3D(505.848, 942.781, 7435.922); + final AbsoluteDate date = new AbsoluteDate(2003, 9, 16, utc); + final Orbit orbit = new EquinoctialOrbit(new PVCoordinates(position, velocity), + FramesFactory.getEME2000(), date, + Constants.EIGEN5C_EARTH_MU); + + Propagator propagator = + new EcksteinHechlerPropagator(orbit, + Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS, + Constants.EIGEN5C_EARTH_MU, + Constants.EIGEN5C_EARTH_C20, + Constants.EIGEN5C_EARTH_C30, + Constants.EIGEN5C_EARTH_C40, + Constants.EIGEN5C_EARTH_C50, + Constants.EIGEN5C_EARTH_C60); + + EventsLogger logger = new EventsLogger(); + propagator.addEventDetector(logger.monitorDetector(d)); + + propagator.propagate(date.shiftedBy(Constants.JULIAN_DAY)); + for (LoggedEvent e : logger.getLoggedEvents()) { + SpacecraftState state = e.getState(); + double latitude = earth.transform(state.getPVCoordinates(earth.getBodyFrame()).getPosition(), + earth.getBodyFrame(), null).getLatitude(); + if (e.isIncreasing()) { + if (state.getPVCoordinates().getVelocity().getZ() < 0) { + // entering northward + Assertions.assertEquals(60.0, FastMath.toDegrees(latitude), FastMath.toRadians(1e-4)); + } else { + // entering southward + Assertions.assertEquals(50.0, FastMath.toDegrees(latitude), FastMath.toRadians(1e-4)); + } + } else { + if (state.getPVCoordinates().getVelocity().getZ() < 0) { + // exiting southward + Assertions.assertEquals(50.0, FastMath.toDegrees(latitude), FastMath.toRadians(1e-4)); + } else { + // exiting northward + Assertions.assertEquals(60.0, FastMath.toDegrees(latitude), FastMath.toRadians(1e-4)); + } + } + } + Assertions.assertEquals(30 * 2, logger.getLoggedEvents().size()); + + } + + @Test + public void testNoCrossing() { + + final OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, + Constants.WGS84_EARTH_FLATTENING, + FramesFactory.getITRF(IERSConventions.IERS_2010, true)); + + LatitudeRangeCrossingDetector d = + new LatitudeRangeCrossingDetector(10.0, 1.e-6, earth, + FastMath.toRadians(82.0), FastMath.toRadians(87.0)). + withHandler(new ContinueOnEvent()); + + Assertions.assertEquals(10.0, d.getMaxCheckInterval().currentInterval(null), 1.0e-15); + Assertions.assertEquals(1.0e-6, d.getThreshold(), 1.0e-15); + Assertions.assertEquals(82.0, FastMath.toDegrees(d.getFromLatitude()), 1.0e-14); + Assertions.assertEquals(87.0, FastMath.toDegrees(d.getToLatitude()), 1.0e-14); + Assertions.assertEquals(AbstractDetector.DEFAULT_MAX_ITER, d.getMaxIterationCount()); + + final TimeScale utc = TimeScalesFactory.getUTC(); + final Vector3D position = new Vector3D(-6142438.668, 3492467.56, -25767.257); + final Vector3D velocity = new Vector3D(505.848, 942.781, 7435.922); + final AbsoluteDate date = new AbsoluteDate(2003, 9, 16, utc); + final Orbit orbit = new EquinoctialOrbit(new PVCoordinates(position, velocity), + FramesFactory.getEME2000(), date, + Constants.EIGEN5C_EARTH_MU); + + Propagator propagator = + new EcksteinHechlerPropagator(orbit, + Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS, + Constants.EIGEN5C_EARTH_MU, + Constants.EIGEN5C_EARTH_C20, + Constants.EIGEN5C_EARTH_C30, + Constants.EIGEN5C_EARTH_C40, + Constants.EIGEN5C_EARTH_C50, + Constants.EIGEN5C_EARTH_C60); + + EventsLogger logger = new EventsLogger(); + propagator.addEventDetector(logger.monitorDetector(d)); + + propagator.propagate(date.shiftedBy(Constants.JULIAN_DAY)); + Assertions.assertEquals(0, logger.getLoggedEvents().size()); + + } + + @BeforeEach + public void setUp() { + Utils.setDataRoot("regular-data"); + } + +} + diff --git a/src/test/java/org/orekit/propagation/events/LongitudeRangeCrossingDetectorTest.java b/src/test/java/org/orekit/propagation/events/LongitudeRangeCrossingDetectorTest.java new file mode 100644 index 0000000000..3120685b76 --- /dev/null +++ b/src/test/java/org/orekit/propagation/events/LongitudeRangeCrossingDetectorTest.java @@ -0,0 +1,161 @@ +/* Copyright 2023-2024 Alberto Ferrero + * 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. + * Alberto Ferrero 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 static org.orekit.orbits.PositionAngleType.MEAN; + +import org.hipparchus.geometry.euclidean.threed.Vector3D; +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.GeodeticPoint; +import org.orekit.bodies.OneAxisEllipsoid; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.EquinoctialOrbit; +import org.orekit.orbits.KeplerianOrbit; +import org.orekit.orbits.Orbit; +import org.orekit.propagation.Propagator; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.analytical.EcksteinHechlerPropagator; +import org.orekit.propagation.analytical.KeplerianPropagator; +import org.orekit.propagation.events.EventsLogger.LoggedEvent; +import org.orekit.propagation.events.handlers.ContinueOnEvent; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.TimeScale; +import org.orekit.time.TimeScalesFactory; +import org.orekit.utils.Constants; +import org.orekit.utils.IERSConventions; +import org.orekit.utils.PVCoordinates; + +public class LongitudeRangeCrossingDetectorTest { + + @Test + public void testRegularCrossing() { + + final OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, + Constants.WGS84_EARTH_FLATTENING, + FramesFactory.getITRF(IERSConventions.IERS_2010, true)); + + LongitudeRangeCrossingDetector d = + new LongitudeRangeCrossingDetector(earth, + FastMath.toRadians(10.0), + FastMath.toRadians(18.0)). + withMaxCheck(60). + withThreshold(1.e-6). + withHandler(new ContinueOnEvent()); + + Assertions.assertEquals(60.0, d.getMaxCheckInterval().currentInterval(null), 1.0e-15); + Assertions.assertEquals(1.0e-6, d.getThreshold(), 1.0e-15); + Assertions.assertEquals(10.0, FastMath.toDegrees(d.getFromLongitude()), 1.0e-14); + Assertions.assertEquals(18.0, FastMath.toDegrees(d.getToLongitude()), 1.0e-14); + Assertions.assertEquals(AbstractDetector.DEFAULT_MAX_ITER, d.getMaxIterationCount()); + Assertions.assertSame(earth, d.getBody()); + + final TimeScale utc = TimeScalesFactory.getUTC(); + final Vector3D position = new Vector3D(-6142438.668, 3492467.56, -25767.257); + final Vector3D velocity = new Vector3D(505.848, 942.781, 7435.922); + final AbsoluteDate date = new AbsoluteDate(2003, 9, 16, utc); + final Orbit orbit = new EquinoctialOrbit(new PVCoordinates(position, velocity), + FramesFactory.getEME2000(), date, + Constants.EIGEN5C_EARTH_MU); + + Propagator propagator = + new EcksteinHechlerPropagator(orbit, + Constants.EIGEN5C_EARTH_EQUATORIAL_RADIUS, + Constants.EIGEN5C_EARTH_MU, + Constants.EIGEN5C_EARTH_C20, + Constants.EIGEN5C_EARTH_C30, + Constants.EIGEN5C_EARTH_C40, + Constants.EIGEN5C_EARTH_C50, + Constants.EIGEN5C_EARTH_C60); + + EventsLogger logger = new EventsLogger(); + propagator.addEventDetector(logger.monitorDetector(d)); + + propagator.propagate(date.shiftedBy(Constants.JULIAN_DAY)); + for (LoggedEvent e : logger.getLoggedEvents()) { + SpacecraftState state = e.getState(); + double longitude = earth.transform(state.getPVCoordinates(earth.getBodyFrame()).getPosition(), + earth.getBodyFrame(), date).getLongitude(); + if (e.isIncreasing()) { + // retrograde orbit, enter + Assertions.assertEquals(18.0, FastMath.toDegrees(longitude), 3.5e-7); + } else { + // retrograde orbit, exit + Assertions.assertEquals(10.0, FastMath.toDegrees(longitude), 3.5e-7); + } + Assertions.assertEquals(28, logger.getLoggedEvents().size()); + } + } + + @Test + public void testZigZag() { + + final OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, + Constants.WGS84_EARTH_FLATTENING, + FramesFactory.getITRF(IERSConventions.IERS_2010, true)); + + LongitudeRangeCrossingDetector d = + new LongitudeRangeCrossingDetector(600.0, 1.e-6, earth, + FastMath.toRadians(-120.0), + FastMath.toRadians(-100.0)). + withHandler(new ContinueOnEvent()); + + Assertions.assertEquals(600.0, d.getMaxCheckInterval().currentInterval(null), 1.0e-15); + Assertions.assertEquals(1.0e-6, d.getThreshold(), 1.0e-15); + Assertions.assertEquals(-120.0, FastMath.toDegrees(d.getFromLongitude()), 1.0e-8); + Assertions.assertEquals(-100.0, FastMath.toDegrees(d.getToLongitude()), 1.0e-8); + Assertions.assertEquals(AbstractDetector.DEFAULT_MAX_ITER, d.getMaxIterationCount()); + + AbsoluteDate date = AbsoluteDate.J2000_EPOCH; + KeplerianOrbit orbit = + new KeplerianOrbit(26464560.0, 0.8311, 0.122138, 3.10686, 1.00681, + 0.048363, MEAN, + FramesFactory.getEME2000(), + date, + Constants.EIGEN5C_EARTH_MU); + + + Propagator propagator = new KeplerianPropagator(orbit); + + EventsLogger logger = new EventsLogger(); + propagator.addEventDetector(logger.monitorDetector(d)); + + propagator.propagate(orbit.getDate().shiftedBy(1 * Constants.JULIAN_DAY)); + Assertions.assertEquals(4, logger.getLoggedEvents().size()); + + // eccentric orbit, at apogee Earth rotation makes as reversed effect + double[] expectedLongitude = new double[]{d.getFromLongitude(), d.getToLongitude(), + d.getToLongitude(), d.getFromLongitude()}; + for (int i = 0; i < 4; ++i) { + SpacecraftState state = logger.getLoggedEvents().get(i).getState(); + GeodeticPoint gp = earth.transform(state.getPVCoordinates(earth.getBodyFrame()).getPosition(), + earth.getBodyFrame(), date); + Assertions.assertEquals(expectedLongitude[i], gp.getLongitude(), 1.2e-9); + } + + } + + @BeforeEach + public void setUp() { + Utils.setDataRoot("regular-data"); + } + +} + diff --git a/src/test/java/org/orekit/propagation/events/NegateDetectorTest.java b/src/test/java/org/orekit/propagation/events/NegateDetectorTest.java index 5dfe1166b0..65669d6ab7 100644 --- a/src/test/java/org/orekit/propagation/events/NegateDetectorTest.java +++ b/src/test/java/org/orekit/propagation/events/NegateDetectorTest.java @@ -39,7 +39,7 @@ public class NegateDetectorTest { public void testInit() { //setup EventDetector a = Mockito.mock(EventDetector.class); - Mockito.when(a.getMaxCheckInterval()).thenReturn(s-> AbstractDetector.DEFAULT_MAXCHECK); + Mockito.when(a.getMaxCheckInterval()).thenReturn(AdaptableInterval.of(AbstractDetector.DEFAULT_MAXCHECK)); Mockito.when(a.getThreshold()).thenReturn(AbstractDetector.DEFAULT_THRESHOLD); EventHandler c = Mockito.mock(EventHandler.class); NegateDetector detector = new NegateDetector(a).withHandler(c); @@ -62,7 +62,7 @@ public void testInit() { public void testG() { //setup EventDetector a = Mockito.mock(EventDetector.class); - Mockito.when(a.getMaxCheckInterval()).thenReturn(s-> AbstractDetector.DEFAULT_MAXCHECK); + Mockito.when(a.getMaxCheckInterval()).thenReturn(AdaptableInterval.of(AbstractDetector.DEFAULT_MAXCHECK)); Mockito.when(a.getThreshold()).thenReturn(AbstractDetector.DEFAULT_THRESHOLD); NegateDetector detector = new NegateDetector(a); SpacecraftState s = Mockito.mock(SpacecraftState.class); @@ -80,7 +80,7 @@ public void testG() { public void testCreate() { //setup EventDetector a = Mockito.mock(EventDetector.class); - Mockito.when(a.getMaxCheckInterval()).thenReturn(s-> AbstractDetector.DEFAULT_MAXCHECK); + Mockito.when(a.getMaxCheckInterval()).thenReturn(AdaptableInterval.of(AbstractDetector.DEFAULT_MAXCHECK)); Mockito.when(a.getThreshold()).thenReturn(AbstractDetector.DEFAULT_THRESHOLD); NegateDetector detector = new NegateDetector(a); diff --git a/src/test/java/org/orekit/propagation/events/OrDetectorTest.java b/src/test/java/org/orekit/propagation/events/OrDetectorTest.java index 4a74ed9f4e..f573969df1 100644 --- a/src/test/java/org/orekit/propagation/events/OrDetectorTest.java +++ b/src/test/java/org/orekit/propagation/events/OrDetectorTest.java @@ -123,10 +123,10 @@ public void testCancellation() { public void testInit() { // setup EventDetector a = Mockito.mock(EventDetector.class); - Mockito.when(a.getMaxCheckInterval()).thenReturn(s -> AbstractDetector.DEFAULT_MAXCHECK); + Mockito.when(a.getMaxCheckInterval()).thenReturn(AdaptableInterval.of(AbstractDetector.DEFAULT_MAXCHECK)); Mockito.when(a.getThreshold()).thenReturn(AbstractDetector.DEFAULT_THRESHOLD); EventDetector b = Mockito.mock(EventDetector.class); - Mockito.when(b.getMaxCheckInterval()).thenReturn(s -> AbstractDetector.DEFAULT_MAXCHECK); + Mockito.when(b.getMaxCheckInterval()).thenReturn(AdaptableInterval.of(AbstractDetector.DEFAULT_MAXCHECK)); Mockito.when(b.getThreshold()).thenReturn(AbstractDetector.DEFAULT_THRESHOLD); EventHandler c = Mockito.mock(EventHandler.class); BooleanDetector or = BooleanDetector.orCombine(a, b).withHandler(c); @@ -179,7 +179,7 @@ public double getThreshold() { @Override public AdaptableInterval getMaxCheckInterval() { - return s -> AbstractDetector.DEFAULT_MAXCHECK; + return AdaptableInterval.of(AbstractDetector.DEFAULT_MAXCHECK); } @Override diff --git a/src/test/java/org/orekit/propagation/events/RelativeDistanceDetectorTest.java b/src/test/java/org/orekit/propagation/events/RelativeDistanceDetectorTest.java new file mode 100644 index 0000000000..52a97b72e1 --- /dev/null +++ b/src/test/java/org/orekit/propagation/events/RelativeDistanceDetectorTest.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.events; + +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.FramesFactory; +import org.orekit.orbits.CartesianOrbit; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.analytical.KeplerianPropagator; +import org.orekit.propagation.events.handlers.EventHandler; +import org.orekit.propagation.events.handlers.StopOnIncreasing; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.Constants; +import org.orekit.utils.PVCoordinatesProvider; +import org.orekit.utils.TimeStampedPVCoordinates; + +class RelativeDistanceDetectorTest { + + @Test + void testCreate() { + // GIVEN + final double distanceThreshold = 1.; + final RelativeDistanceDetector distanceDetector = new RelativeDistanceDetector( + Mockito.mock(PVCoordinatesProvider.class), distanceThreshold); + final EventHandler expectedHandler = new StopOnIncreasing(); + // WHEN + final RelativeDistanceDetector detector = distanceDetector.create(distanceDetector.getMaxCheckInterval(), + distanceDetector.getThreshold(), distanceDetector.getMaxIterationCount(), expectedHandler); + // THEN + Assertions.assertEquals(expectedHandler, detector.getHandler()); + } + + @Test + void testGetDistanceThreshold() { + // GIVEN + final double expectedDistanceThreshold = 1.; + final RelativeDistanceDetector distanceDetector = new RelativeDistanceDetector( + Mockito.mock(PVCoordinatesProvider.class), expectedDistanceThreshold); + // WHEN + final double actualDistanceThreshold = distanceDetector.getDistanceThreshold(); + // THEN + Assertions.assertEquals(expectedDistanceThreshold, actualDistanceThreshold); + } + + @Test + void testG() { + // GIVEN + final double zeroDistanceThreshold = 0.; + final CartesianOrbit initialOrbit = createOrbit(); + final KeplerianPropagator propagator = new KeplerianPropagator(initialOrbit); + final RelativeDistanceDetector distanceDetector = new RelativeDistanceDetector(propagator, + zeroDistanceThreshold); + // WHEN + final double g = distanceDetector.g(new SpacecraftState(initialOrbit)); + // THEN + Assertions.assertEquals(0., g, 1e-8); + } + + private CartesianOrbit createOrbit() { + final Vector3D position = new Vector3D(1e7, 2e3, 1e4); + final Vector3D velocity = new Vector3D(0., 6e3, -1e2); + final TimeStampedPVCoordinates pvCoordinates = new TimeStampedPVCoordinates(AbsoluteDate.ARBITRARY_EPOCH, + position, velocity); + return new CartesianOrbit(pvCoordinates, FramesFactory.getGCRF(), Constants.EGM96_EARTH_MU); + } + +} 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 4ba9a53cfd..d0192b6071 100644 --- a/src/test/java/org/orekit/propagation/events/handlers/EventHandlerTest.java +++ b/src/test/java/org/orekit/propagation/events/handlers/EventHandlerTest.java @@ -82,7 +82,7 @@ public double getThreshold() { @Override public AdaptableInterval getMaxCheckInterval() { - return s -> 0; + return AdaptableInterval.of(0); } @Override diff --git a/src/test/java/org/orekit/propagation/events/handlers/FieldRecallLastOccurrenceTest.java b/src/test/java/org/orekit/propagation/events/handlers/FieldRecallLastOccurrenceTest.java new file mode 100644 index 0000000000..f7ce34c621 --- /dev/null +++ b/src/test/java/org/orekit/propagation/events/handlers/FieldRecallLastOccurrenceTest.java @@ -0,0 +1,97 @@ +/* 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.complex.ComplexField; +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; +import org.orekit.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; + +class FieldRecallLastOccurrenceTest { + + private static final Action ACTION = Action.CONTINUE; + + @Test + void testEventOccurred() { + // 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 + final Action action = recallLastOccurrence.eventOccurred(mockedState, null, true); + // THEN + Assertions.assertEquals(expectedDate, recallLastOccurrence.getLastOccurrence()); + Assertions.assertEquals(ACTION, action); + } + + @Test + void testResetState() { + // GIVEN + final TestHandler testHandler = new TestHandler(); + final FieldRecallLastOccurrence recallLastOccurrence = new FieldRecallLastOccurrence<>(testHandler); + final FieldSpacecraftState mockedState = mockState(FieldAbsoluteDate.getArbitraryEpoch(ComplexField.getInstance())); + // WHEN + final FieldSpacecraftState actualState = recallLastOccurrence.resetState(null, mockedState); + // THEN + Assertions.assertEquals(mockedState, actualState); + Assertions.assertNull(recallLastOccurrence.getLastOccurrence()); + } + + @Test + void testInit() { + // GIVEN + final TestHandler testHandler = new TestHandler(); + final FieldRecallLastOccurrence recallLastOccurrence = new FieldRecallLastOccurrence<>(testHandler); + final FieldSpacecraftState mockedState = mockState(new FieldAbsoluteDate<>(ComplexField.getInstance(), + AbsoluteDate.FUTURE_INFINITY)); + // WHEN + recallLastOccurrence.init(mockedState, FieldAbsoluteDate.getArbitraryEpoch(ComplexField.getInstance()), null); + // THEN + Assertions.assertTrue(testHandler.isInitialized); + } + + @SuppressWarnings("unchecked") + private FieldSpacecraftState mockState(final FieldAbsoluteDate date) { + final FieldSpacecraftState mockedState = Mockito.mock(FieldSpacecraftState.class); + Mockito.when(mockedState.getDate()).thenReturn(date); + return mockedState; + } + + private static class TestHandler implements FieldEventHandler { + + boolean isInitialized = false; + + @Override + public void init(FieldSpacecraftState initialState, FieldAbsoluteDate target, FieldEventDetector detector) { + isInitialized = true; + } + + @Override + public Action eventOccurred(FieldSpacecraftState s, FieldEventDetector detector, boolean increasing) { + return ACTION; + } + + } + +} diff --git a/src/test/java/org/orekit/propagation/events/handlers/FieldRecordAndContinueTest.java b/src/test/java/org/orekit/propagation/events/handlers/FieldRecordAndContinueTest.java index 12016abf58..39f21b352a 100644 --- a/src/test/java/org/orekit/propagation/events/handlers/FieldRecordAndContinueTest.java +++ b/src/test/java/org/orekit/propagation/events/handlers/FieldRecordAndContinueTest.java @@ -53,7 +53,6 @@ public void testGetEvents() { FieldAbsoluteDate date = new FieldAbsoluteDate<>(field, AbsoluteDate.J2000_EPOCH); Binary64 zero = date.getField().getZero(); - @SuppressWarnings("unchecked") FieldDateDetector detector = new FieldDateDetector<>(field, date); Frame eci = FramesFactory.getGCRF(); FieldOrbit orbit = new FieldKeplerianOrbit<>( diff --git a/src/test/java/org/orekit/propagation/events/handlers/RecallLastOccurrenceTest.java b/src/test/java/org/orekit/propagation/events/handlers/RecallLastOccurrenceTest.java new file mode 100644 index 0000000000..5a45b08aa8 --- /dev/null +++ b/src/test/java/org/orekit/propagation/events/handlers/RecallLastOccurrenceTest.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.propagation.events.handlers; + +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.EventDetector; +import org.orekit.time.AbsoluteDate; + +class RecallLastOccurrenceTest { + + private static final Action ACTION = Action.CONTINUE; + + @Test + void testEventOccurred() { + // GIVEN + final TestHandler testHandler = new TestHandler(); + final RecallLastOccurrence recallLastOccurrence = new RecallLastOccurrence(testHandler); + final AbsoluteDate expectedDate = AbsoluteDate.J2000_EPOCH; + final SpacecraftState mockedState = mockState(expectedDate); + // WHEN + final Action action = recallLastOccurrence.eventOccurred(mockedState, null, true); + // THEN + Assertions.assertEquals(expectedDate, recallLastOccurrence.getLastOccurrence()); + Assertions.assertEquals(ACTION, action); + } + + @Test + void testResetState() { + // GIVEN + final TestHandler testHandler = new TestHandler(); + final RecallLastOccurrence recallLastOccurrence = new RecallLastOccurrence(testHandler); + final SpacecraftState mockedState = mockState(AbsoluteDate.ARBITRARY_EPOCH); + // WHEN + final SpacecraftState actualState = recallLastOccurrence.resetState(null, mockedState); + // THEN + Assertions.assertEquals(mockedState, actualState); + Assertions.assertNull(recallLastOccurrence.getLastOccurrence()); + } + + @Test + void testInitBackward() { + // GIVEN + final TestHandler testHandler = new TestHandler(); + final RecallLastOccurrence recallLastOccurrence = new RecallLastOccurrence(testHandler); + final SpacecraftState mockedState = mockState(AbsoluteDate.FUTURE_INFINITY); + // WHEN + recallLastOccurrence.init(mockedState, AbsoluteDate.ARBITRARY_EPOCH, null); + // THEN + Assertions.assertTrue(testHandler.isInitialized); + Assertions.assertEquals(AbsoluteDate.FUTURE_INFINITY, recallLastOccurrence.getLastOccurrence()); + } + + @Test + void testInitForward() { + // GIVEN + final TestHandler testHandler = new TestHandler(); + final RecallLastOccurrence recallLastOccurrence = new RecallLastOccurrence(testHandler); + final SpacecraftState mockedState = mockState(AbsoluteDate.PAST_INFINITY); + // WHEN + recallLastOccurrence.init(mockedState, AbsoluteDate.ARBITRARY_EPOCH, null); + // THEN + Assertions.assertTrue(testHandler.isInitialized); + Assertions.assertEquals(AbsoluteDate.PAST_INFINITY, recallLastOccurrence.getLastOccurrence()); + } + + private SpacecraftState mockState(final AbsoluteDate date) { + final SpacecraftState mockedState = Mockito.mock(SpacecraftState.class); + Mockito.when(mockedState.getDate()).thenReturn(date); + return mockedState; + } + + private static class TestHandler implements EventHandler { + + boolean isInitialized = false; + + @Override + public void init(SpacecraftState initialState, AbsoluteDate target, EventDetector detector) { + isInitialized = true; + } + + @Override + public Action eventOccurred(SpacecraftState s, EventDetector detector, boolean increasing) { + return ACTION; + } + + } + +} diff --git a/src/test/java/org/orekit/propagation/events/intervals/ApsideDetectionAdaptableIntervalFactoryTest.java b/src/test/java/org/orekit/propagation/events/intervals/ApsideDetectionAdaptableIntervalFactoryTest.java new file mode 100644 index 0000000000..baea5d0305 --- /dev/null +++ b/src/test/java/org/orekit/propagation/events/intervals/ApsideDetectionAdaptableIntervalFactoryTest.java @@ -0,0 +1,196 @@ +/* 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.intervals; + +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.propagation.Propagator; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.analytical.KeplerianPropagator; +import org.orekit.propagation.events.*; +import org.orekit.propagation.events.handlers.StopOnEvent; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.Constants; + +class ApsideDetectionAdaptableIntervalFactoryTest { + + @Test + void testGetForwardApsideDetectionAdaptableInterval() { + // GIVEN + final Orbit initialOrbit = createOrbit(1.); + final AdaptableInterval forwardAdaptableInterval = ApsideDetectionAdaptableIntervalFactory + .getForwardApsideDetectionAdaptableInterval(); + // WHEN + final double value = forwardAdaptableInterval.currentInterval(new SpacecraftState(initialOrbit)); + // THEN + Assertions.assertTrue(value <= initialOrbit.getKeplerianPeriod() / 2); + } + + @Test + void testGetBackwardApsideDetectionAdaptableInterval() { + // GIVEN + final Orbit initialOrbit = createOrbit(1.); + final AdaptableInterval backwardAdaptableInterval = ApsideDetectionAdaptableIntervalFactory + .getBackwardApsideDetectionAdaptableInterval(); + // WHEN + final double value = backwardAdaptableInterval.currentInterval(new SpacecraftState(initialOrbit)); + // THEN + Assertions.assertTrue(value <= initialOrbit.getKeplerianPeriod() / 2); + } + + @Test + void testGetForwardPeriapsisDetectionAdaptableInterval() { + // GIVEN + final Orbit initialOrbit = createOrbit(1.); + final EventSlopeFilter periapsisDetector = createPeriapsisDetector(initialOrbit); + final AdaptableInterval forwardAdaptableInterval = ApsideDetectionAdaptableIntervalFactory + .getForwardPeriapsisDetectionAdaptableInterval(); + final AdaptableIntervalWithCounter forwardAdaptableIntervalWithCounter = new AdaptableIntervalWithCounter( + forwardAdaptableInterval); + final Propagator propagator = createPropagatorWithDetector(initialOrbit, + periapsisDetector.withMaxCheck(forwardAdaptableIntervalWithCounter)); + // WHEN + final AbsoluteDate targetDate = initialOrbit.getDate().shiftedBy(initialOrbit.getKeplerianPeriod() * 2); + final SpacecraftState terminalState = propagator.propagate(targetDate); + // THEN + Assertions.assertNotEquals(targetDate, terminalState.getDate()); + final int countWithDefaultAdaptableInterval = countWithConstantAdaptableInterval(initialOrbit, targetDate, + periapsisDetector); + Assertions.assertTrue(countWithDefaultAdaptableInterval > forwardAdaptableIntervalWithCounter.count); + } + + @Test + void testGetBackwardPeriapsisDetectionAdaptableInterval() { + // GIVEN + final Orbit initialOrbit = createOrbit(6.); + final EventSlopeFilter periapsisDetector = createPeriapsisDetector(initialOrbit); + final AdaptableInterval backwardAdaptableInterval = ApsideDetectionAdaptableIntervalFactory + .getBackwardPeriapsisDetectionAdaptableInterval(); + final AdaptableIntervalWithCounter backwardAdaptableIntervalWithCounter = new AdaptableIntervalWithCounter( + backwardAdaptableInterval); + final Propagator propagator = createPropagatorWithDetector(initialOrbit, + periapsisDetector.withMaxCheck(backwardAdaptableIntervalWithCounter)); + // WHEN + final AbsoluteDate targetDate = initialOrbit.getDate().shiftedBy(-initialOrbit.getKeplerianPeriod() * 2); + final SpacecraftState terminalState = propagator.propagate(targetDate); + // THEN + Assertions.assertNotEquals(targetDate, terminalState.getDate()); + final int countWithDefaultAdaptableInterval = countWithConstantAdaptableInterval(initialOrbit, targetDate, + periapsisDetector); + Assertions.assertTrue(countWithDefaultAdaptableInterval > backwardAdaptableIntervalWithCounter.count); + } + + @Test + void testGetForwardApoapsisDetectionAdaptableInterval() { + // GIVEN + final Orbit initialOrbit = createOrbit(4.); + final EventSlopeFilter apoapsisDetector = createApoapsisDetector(initialOrbit); + final AdaptableInterval forwardAdaptableInterval = ApsideDetectionAdaptableIntervalFactory + .getForwardApoapsisDetectionAdaptableInterval(); + final AdaptableIntervalWithCounter forwardAdaptableIntervalWithCounter = new AdaptableIntervalWithCounter( + forwardAdaptableInterval); + final Propagator propagator = createPropagatorWithDetector(initialOrbit, + apoapsisDetector.withMaxCheck(forwardAdaptableIntervalWithCounter)); + // WHEN + final AbsoluteDate targetDate = initialOrbit.getDate().shiftedBy(initialOrbit.getKeplerianPeriod() * 2); + final SpacecraftState terminalState = propagator.propagate(targetDate); + // THEN + Assertions.assertNotEquals(targetDate, terminalState.getDate()); + final int countWithDefaultAdaptableInterval = countWithConstantAdaptableInterval(initialOrbit, targetDate, + apoapsisDetector); + Assertions.assertTrue(countWithDefaultAdaptableInterval > forwardAdaptableIntervalWithCounter.count); + } + + @Test + void testGetBackwardApoapsisDetectionAdaptableInterval() { + // GIVEN + final Orbit initialOrbit = createOrbit(3.); + final EventSlopeFilter apoapsisDetector = createApoapsisDetector(initialOrbit); + final AdaptableInterval backwardAdaptableInterval = ApsideDetectionAdaptableIntervalFactory + .getBackwardApoapsisDetectionAdaptableInterval(); + final AdaptableIntervalWithCounter backwardAdaptableIntervalWithCounter = new AdaptableIntervalWithCounter( + backwardAdaptableInterval); + final Propagator propagator = createPropagatorWithDetector(initialOrbit, + apoapsisDetector.withMaxCheck(backwardAdaptableIntervalWithCounter)); + // WHEN + final AbsoluteDate targetDate = initialOrbit.getDate().shiftedBy(-initialOrbit.getKeplerianPeriod() * 2); + final SpacecraftState terminalState = propagator.propagate(targetDate); + // THEN + Assertions.assertNotEquals(targetDate, terminalState.getDate()); + final int countWithDefaultAdaptableInterval = countWithConstantAdaptableInterval(initialOrbit, targetDate, + apoapsisDetector); + Assertions.assertTrue(countWithDefaultAdaptableInterval > backwardAdaptableIntervalWithCounter.count); + } + + + private Propagator createPropagatorWithDetector(final Orbit initialOrbit, + final EventDetector eventDetector) { + final KeplerianPropagator propagator = new KeplerianPropagator(initialOrbit); + propagator.addEventDetector(eventDetector); + return propagator; + } + + private EventSlopeFilter createPeriapsisDetector(final Orbit initialOrbit) { + return new EventSlopeFilter<>(new ApsideDetector(initialOrbit), FilterType.TRIGGER_ONLY_DECREASING_EVENTS) + .withHandler(new StopOnEvent()); + } + + private EventSlopeFilter createApoapsisDetector(final Orbit initialOrbit) { + return new EventSlopeFilter<>(new ApsideDetector(initialOrbit), FilterType.TRIGGER_ONLY_INCREASING_EVENTS) + .withHandler(new StopOnEvent()); + } + + private AdaptableInterval getConstantAdaptableInterval() { + return state -> state.getOrbit().getKeplerianPeriod() / 3.; + } + + private Orbit createOrbit(final double meanAnomaly) { + return new KeplerianOrbit(1e7, 0.001, 1., 2., 3., meanAnomaly, PositionAngleType.MEAN, + FramesFactory.getGCRF(), AbsoluteDate.ARBITRARY_EPOCH, Constants.EGM96_EARTH_MU); + } + + private int countWithConstantAdaptableInterval(final Orbit initialOrbit, final AbsoluteDate targetDate, + final EventSlopeFilter apsideDetector) { + final AdaptableIntervalWithCounter adaptableIntervalWithCounter = new AdaptableIntervalWithCounter( + getConstantAdaptableInterval()); + final Propagator otherPropagator = createPropagatorWithDetector(initialOrbit, + apsideDetector.withMaxCheck(adaptableIntervalWithCounter)); + otherPropagator.propagate(targetDate); + return adaptableIntervalWithCounter.count; + } + + private static class AdaptableIntervalWithCounter implements AdaptableInterval { + + private final AdaptableInterval interval; + int count = 0; + + AdaptableIntervalWithCounter(final AdaptableInterval adaptableInterval) { + this.interval = adaptableInterval; + } + + @Override + public double currentInterval(SpacecraftState state) { + count++; + return interval.currentInterval(state); + } + } + +} diff --git a/src/test/java/org/orekit/propagation/integration/AbstractGradientConverterTest.java b/src/test/java/org/orekit/propagation/integration/AbstractGradientConverterTest.java index 32f0318965..8b0f6b4bee 100644 --- a/src/test/java/org/orekit/propagation/integration/AbstractGradientConverterTest.java +++ b/src/test/java/org/orekit/propagation/integration/AbstractGradientConverterTest.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2024 Romain Serra +/* 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. @@ -17,6 +17,7 @@ package org.orekit.propagation.integration; import org.hipparchus.analysis.differentiation.Gradient; +import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -27,7 +28,11 @@ import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.SpacecraftState; import org.orekit.time.AbsoluteDate; -import org.orekit.utils.*; +import org.orekit.utils.Constants; +import org.orekit.utils.PVCoordinates; +import org.orekit.utils.ParameterDriver; +import org.orekit.utils.ParameterDriversProvider; +import org.orekit.utils.TimeStampedPVCoordinates; import java.util.ArrayList; import java.util.List; @@ -101,7 +106,7 @@ private SpacecraftState mockState(final boolean isOrbitDefined, final Frame fram final SpacecraftState state = Mockito.mock(SpacecraftState.class); Mockito.when(state.getDate()).thenReturn(AbsoluteDate.ARBITRARY_EPOCH); final TimeStampedPVCoordinates pvCoordinates = new TimeStampedPVCoordinates(state.getDate(), - new PVCoordinates()); + new PVCoordinates(Vector3D.PLUS_I)); Mockito.when(state.getPVCoordinates()).thenReturn(pvCoordinates); Mockito.when(state.getPosition()).thenReturn(pvCoordinates.getPosition()); Mockito.when(state.isOrbitDefined()).thenReturn(isOrbitDefined); @@ -141,4 +146,4 @@ protected TestGradientConverter(int freeStateParameters) { } -} \ No newline at end of file +} diff --git a/src/test/java/org/orekit/propagation/integration/AbstractIntegratedPropagatorTest.java b/src/test/java/org/orekit/propagation/integration/AbstractIntegratedPropagatorTest.java index 37ac887532..b07b7593c4 100644 --- a/src/test/java/org/orekit/propagation/integration/AbstractIntegratedPropagatorTest.java +++ b/src/test/java/org/orekit/propagation/integration/AbstractIntegratedPropagatorTest.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2024 Romain Serra +/* 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. diff --git a/src/test/java/org/orekit/propagation/integration/FieldAbstractIntegratedPropagatorTest.java b/src/test/java/org/orekit/propagation/integration/FieldAbstractIntegratedPropagatorTest.java index fc79748995..1551d929a8 100644 --- a/src/test/java/org/orekit/propagation/integration/FieldAbstractIntegratedPropagatorTest.java +++ b/src/test/java/org/orekit/propagation/integration/FieldAbstractIntegratedPropagatorTest.java @@ -1,4 +1,4 @@ -/* Copyright 2002-2024 Romain Serra +/* 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. diff --git a/src/test/java/org/orekit/propagation/integration/FieldIntegratedEphemerisTest.java b/src/test/java/org/orekit/propagation/integration/FieldIntegratedEphemerisTest.java index a5ccb5be85..ed3636b4d1 100644 --- a/src/test/java/org/orekit/propagation/integration/FieldIntegratedEphemerisTest.java +++ b/src/test/java/org/orekit/propagation/integration/FieldIntegratedEphemerisTest.java @@ -214,7 +214,6 @@ private > void doTestNoReset(Field field) numericalPropagator.setInitialState(new FieldSpacecraftState<>(initialOrbit)); numericalPropagator.propagate(finalDate); FieldBoundedPropagator ephemeris = generator.getGeneratedEphemeris(); - @SuppressWarnings("unchecked") FieldDateDetector detector = new FieldDateDetector<>(initialOrbit.getDate().getField(), initialOrbit.getDate().shiftedBy(10)). withHandler((s, d, increasing) -> Action.RESET_STATE); diff --git a/src/test/java/org/orekit/propagation/numerical/FieldNumericalPropagatorTest.java b/src/test/java/org/orekit/propagation/numerical/FieldNumericalPropagatorTest.java index f6e8bc0c10..9ce620d44f 100644 --- a/src/test/java/org/orekit/propagation/numerical/FieldNumericalPropagatorTest.java +++ b/src/test/java/org/orekit/propagation/numerical/FieldNumericalPropagatorTest.java @@ -17,25 +17,35 @@ package org.orekit.propagation.numerical; import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import java.util.stream.IntStream; +import java.util.stream.Stream; import org.hamcrest.MatcherAssert; import org.hipparchus.CalculusFieldElement; 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.exception.LocalizedCoreFormats; +import org.hipparchus.exception.MathRuntimeException; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.ode.FieldODEIntegrator; 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; import org.hipparchus.util.MathArrays; 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.OrekitMatchers; @@ -59,30 +69,27 @@ import org.orekit.frames.FramesFactory; import org.orekit.models.earth.atmosphere.DTM2000; import org.orekit.models.earth.atmosphere.data.MarshallSolarActivityFutureEstimation; -import org.orekit.orbits.FieldCartesianOrbit; -import org.orekit.orbits.FieldEquinoctialOrbit; -import org.orekit.orbits.FieldKeplerianOrbit; -import org.orekit.orbits.FieldOrbit; -import org.orekit.orbits.OrbitType; -import org.orekit.orbits.PositionAngleType; +import org.orekit.orbits.*; import org.orekit.propagation.FieldAdditionalStateProvider; import org.orekit.propagation.FieldBoundedPropagator; import org.orekit.propagation.FieldEphemerisGenerator; -import org.orekit.propagation.FieldSpacecraftState; 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.FieldEventDetector; 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.integration.FieldAbstractIntegratedPropagator; -import org.orekit.propagation.integration.FieldAdditionalDerivativesProvider; -import org.orekit.propagation.integration.FieldCombinedDerivatives; +import org.orekit.propagation.events.FieldEventDetector; +import org.orekit.propagation.integration.*; import org.orekit.propagation.sampling.FieldOrekitStepHandler; import org.orekit.propagation.sampling.FieldOrekitStepInterpolator; +import org.orekit.time.AbsoluteDate; import org.orekit.time.DateComponents; import org.orekit.time.FieldAbsoluteDate; import org.orekit.time.FieldTimeStamped; @@ -92,15 +99,15 @@ import org.orekit.utils.Constants; import org.orekit.utils.FieldPVCoordinates; import org.orekit.utils.IERSConventions; +import org.orekit.utils.ParameterDriver; import org.orekit.utils.TimeStampedFieldPVCoordinates; - public class FieldNumericalPropagatorTest { private double mu; @Test - public void testIssue1032() { + void testIssue1032() { doTestIssue1032(Binary64Field.getInstance()); } @@ -110,15 +117,13 @@ private > void doTestIssue1032(Field field) } @Test - public void testNotInitialised1() { - Assertions.assertThrows(OrekitException.class, () -> { - doTestNotInitialised1(Binary64Field.getInstance()); - }); + void testNotInitialised1() { + Assertions.assertThrows(OrekitException.class, () -> doTestNotInitialised1(Binary64Field.getInstance())); } private > void doTestNotInitialised1(Field field) { // setup - final FieldAbsoluteDate initDate = FieldAbsoluteDate.getJ2000Epoch(field); + final FieldAbsoluteDate initDate = FieldAbsoluteDate.getJ2000Epoch(field); final FieldAbstractIntegratedPropagator notInitialised = new FieldNumericalPropagator<>(field, new ClassicalRungeKuttaFieldIntegrator<>(field, field.getZero().add(10.0))); @@ -126,10 +131,8 @@ private > void doTestNotInitialised1(Field } @Test - public void testNotInitialised2() { - Assertions.assertThrows(OrekitException.class, () -> { - doTestNotInitialised2(Binary64Field.getInstance()); - }); + void testNotInitialised2() { + Assertions.assertThrows(OrekitException.class, () -> doTestNotInitialised2(Binary64Field.getInstance())); } private > void doTestNotInitialised2(Field field) { @@ -141,11 +144,11 @@ private > void doTestNotInitialised2(Field } @Test - public void testEventAtEndOfEphemeris() { + void testEventAtEndOfEphemeris() { doTestEventAtEndOfEphemeris(Binary64Field.getInstance()); } - private , D extends FieldEventDetector> void doTestEventAtEndOfEphemeris(Field field) { + private > void doTestEventAtEndOfEphemeris(Field field) { T zero = field.getZero(); FieldNumericalPropagator propagator = createPropagator(field); @@ -157,7 +160,7 @@ private , D extends FieldEventDetector> voi final FieldEphemerisGenerator generator = propagator.getEphemerisGenerator(); propagator.propagate(end); FieldBoundedPropagator ephemeris = generator.getGeneratedEphemeris(); - CountingHandler handler = new CountingHandler(); + CountingHandler handler = new CountingHandler<>(); FieldDateDetector detector = new FieldDateDetector<>(field, toArray(end)). withMaxCheck(10). withThreshold(zero.newInstance(1e-9)). @@ -175,11 +178,11 @@ private , D extends FieldEventDetector> voi } @Test - public void testEventAtBeginningOfEphemeris() { + void testEventAtBeginningOfEphemeris() { doTestEventAtBeginningOfEphemeris(Binary64Field.getInstance()); } - private , D extends FieldEventDetector> void doTestEventAtBeginningOfEphemeris(Field field) { + private > void doTestEventAtBeginningOfEphemeris(Field field) { T zero = field.getZero(); FieldNumericalPropagator propagator = createPropagator(field); @@ -192,7 +195,7 @@ private , D extends FieldEventDetector> voi final FieldEphemerisGenerator generator = propagator.getEphemerisGenerator(); propagator.propagate(end); FieldBoundedPropagator ephemeris = generator.getGeneratedEphemeris(); - CountingHandler handler = new CountingHandler(); + CountingHandler handler = new CountingHandler<>(); // events directly on propagation start date are not triggered, // so move the event date slightly after FieldAbsoluteDate eventDate = initDate.shiftedBy(FastMath.ulp(100.0) / 10.0); @@ -211,12 +214,11 @@ private , D extends FieldEventDetector> voi Assertions.assertEquals(2, handler.eventCount); } - public class CountingHandler , T extends CalculusFieldElement> + static class CountingHandler > implements FieldEventHandler { /** - * number of calls to {@link #eventOccurred(FieldSpacecraftState, - * FieldEventDetector, boolean)}. + * number of calls to eventOccurred. */ private int eventCount = 0; @@ -238,7 +240,7 @@ public FieldSpacecraftState resetState(FieldEventDetector detector, } @Test - public void testCloseEventDates() { + void testCloseEventDates() { doTestCloseEventDates(Binary64Field.getInstance()); } @@ -252,11 +254,11 @@ private > void doTestCloseEventDates(Field FieldDateDetector d1 = new FieldDateDetector<>(field, toArray(initDate.shiftedBy(15))). withMaxCheck(10). withThreshold(zero.newInstance(1)). - withHandler(new FieldContinueOnEvent()); + withHandler(new FieldContinueOnEvent<>()); FieldDateDetector d2 = new FieldDateDetector<>(field, toArray(initDate.shiftedBy(15.5))). withMaxCheck(10). withThreshold(zero.newInstance(1)). - withHandler(new FieldContinueOnEvent()); + withHandler(new FieldContinueOnEvent<>()); propagator.addEventDetector(d1); propagator.addEventDetector(d2); @@ -269,7 +271,7 @@ private > void doTestCloseEventDates(Field } @Test - public void testEphemerisDates() { + void testEphemerisDates() { doTestEphemerisDates(Binary64Field.getInstance()); } @@ -319,7 +321,7 @@ private > void doTestEphemerisDates(Field f } @Test - public void testEphemerisDatesBackward() { + void testEphemerisDatesBackward() { doTestEphemerisDatesBackward(Binary64Field.getInstance()); } @@ -364,7 +366,7 @@ private > void doTestEphemerisDatesBackward(Fi } @Test - public void testNoExtrapolation() { + void testNoExtrapolation() { doTestNoExtrapolation(Binary64Field.getInstance()); } @@ -415,7 +417,7 @@ private > void doTestNoExtrapolation(Field } @Test - public void testKepler() { + void testKepler() { doTestKepler(Binary64Field.getInstance()); } @@ -457,7 +459,7 @@ private > void doTestKepler(Field field) { } @Test - public void testCartesian() { + void testCartesian() { doTestCartesian(Binary64Field.getInstance()); } @@ -500,7 +502,7 @@ private > void doTestCartesian(Field field } @Test - public void testPropagationTypesElliptical() { + void testPropagationTypesElliptical() { doTestPropagationTypesElliptical(Binary64Field.getInstance()); } @@ -579,7 +581,7 @@ private > void doTestPropagationTypesElliptic } @Test - public void testPropagationTypesHyperbolic() { + void testPropagationTypesHyperbolic() { doTestPropagationTypesHyperbolic(Binary64Field.getInstance()); } @@ -665,10 +667,8 @@ private > FieldPVCoordinates propagateInTyp } @Test - public void testException() { - Assertions.assertThrows(OrekitException.class, () -> { - doTestException(Binary64Field.getInstance()); - }); + void testException() { + Assertions.assertThrows(OrekitException.class, () -> doTestException(Binary64Field.getInstance())); } private > void doTestException(Field field) { @@ -694,13 +694,7 @@ private > void doTestException(Field field) propagator.setStepHandler(new FieldOrekitStepHandler() { private int countDown = 3; - private FieldAbsoluteDate previousCall = null; - public void init(FieldSpacecraftState s0, FieldAbsoluteDate t) { - } public void handleStep(FieldOrekitStepInterpolator interpolator) { - if (previousCall != null) { - System.out.println(interpolator.getCurrentState().getDate().compareTo(previousCall) < 0); - } if (--countDown == 0) { throw new OrekitException(LocalizedCoreFormats.SIMPLE_MESSAGE, "dummy error"); } @@ -711,7 +705,7 @@ public void handleStep(FieldOrekitStepInterpolator interpolator) { } @Test - public void testStopEvent() { + void testStopEvent() { doTestStopEvent(Binary64Field.getInstance()); } @@ -737,8 +731,7 @@ private > void doTestStopEvent(Field field) propagator.setInitialState(initialState); final FieldAbsoluteDate stopDate = initDate.shiftedBy(1000); - CheckingHandler checking = new CheckingHandler(Action.STOP); - @SuppressWarnings("unchecked") + CheckingHandler checking = new CheckingHandler<>(Action.STOP); FieldDateDetector detector = new FieldDateDetector<>(field, stopDate).withHandler(checking); propagator.addEventDetector(detector); Assertions.assertEquals(1, propagator.getEventsDetectors().size()); @@ -752,7 +745,7 @@ private > void doTestStopEvent(Field field) } @Test - public void testResetStateEvent() { + void testResetStateEvent() { doTestResetStateEvent(Binary64Field.getInstance()); } @@ -782,7 +775,6 @@ public FieldSpacecraftState resetState(FieldEventDetector detector, FieldS return new FieldSpacecraftState<>(oldState.getOrbit(), oldState.getAttitude(), oldState.getMass().subtract(200.0)); } }; - @SuppressWarnings("unchecked") FieldDateDetector detector = new FieldDateDetector<>(field, resetDate).withHandler(checking); propagator.addEventDetector(detector); checking.assertEvent(false); @@ -792,7 +784,7 @@ public FieldSpacecraftState resetState(FieldEventDetector detector, FieldS } @Test - public void testResetDerivativesEvent() { + void testResetDerivativesEvent() { doTestResetDerivativesEvent(Binary64Field.getInstance()); } @@ -817,8 +809,7 @@ private > void doTestResetDerivativesEvent(Fie propagator.setOrbitType(type); propagator.setInitialState(initialState); final FieldAbsoluteDate resetDate = initDate.shiftedBy(1000); - CheckingHandler checking = new CheckingHandler(Action.RESET_DERIVATIVES); - @SuppressWarnings("unchecked") + CheckingHandler checking = new CheckingHandler<>(Action.RESET_DERIVATIVES); FieldDateDetector detector = new FieldDateDetector<>(field, resetDate).withHandler(checking); propagator.addEventDetector(detector); final double dt = 3200; @@ -839,7 +830,7 @@ private > void doTestResetDerivativesEvent(Fie } @Test - public void testContinueEvent() { + void testContinueEvent() { doTestContinueEvent(Binary64Field.getInstance()); } @@ -868,8 +859,7 @@ private > void doTestContinueEvent(Field f final FieldAbsoluteDate resetDate = initDate.shiftedBy(1000); - CheckingHandler checking = new CheckingHandler(Action.CONTINUE); - @SuppressWarnings("unchecked") + CheckingHandler checking = new CheckingHandler<>(Action.CONTINUE); FieldDateDetector detector = new FieldDateDetector<>(field, resetDate).withHandler(checking); propagator.addEventDetector(detector); final double dt = 3200; @@ -890,7 +880,7 @@ private > void doTestContinueEvent(Field f } @Test - public void testAdditionalStateEvent() { + void testAdditionalStateEvent() { doTestAdditionalStateEvent(Binary64Field.getInstance()); } @@ -983,8 +973,8 @@ public T[] getAdditionalState(FieldSpacecraftState state) { Assertions.assertEquals(2, propagator.getManagedAdditionalStates().length); propagator.setInitialState(propagator.getInitialState().addAdditionalState("linear", zero.add(1.5))); - CheckingHandler checking = new CheckingHandler(Action.STOP); - propagator.addEventDetector(new AdditionalStateLinearDetector(zero.add(10.0), zero.add(1.0e-8)).withHandler(checking)); + CheckingHandler checking = new CheckingHandler<>(Action.STOP); + propagator.addEventDetector(new AdditionalStateLinearDetector<>(zero.add(10.0), zero.add(1.0e-8)).withHandler(checking)); final double dt = 3200; checking.assertEvent(false); @@ -1000,7 +990,7 @@ private static class AdditionalStateLinearDetector, T> { public AdditionalStateLinearDetector(T maxCheck, T threshold) { - this(s -> maxCheck.getReal(), threshold, DEFAULT_MAX_ITER, new FieldStopOnEvent()); + this(FieldAdaptableInterval.of(maxCheck.getReal()), threshold, DEFAULT_MAX_ITER, new FieldStopOnEvent<>()); } private AdditionalStateLinearDetector(FieldAdaptableInterval maxCheck, T threshold, int maxIter, @@ -1011,7 +1001,7 @@ private AdditionalStateLinearDetector(FieldAdaptableInterval maxCheck, T thre 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<>(newMaxCheck, newThreshold, newMaxIter, newHandler); } public T g(FieldSpacecraftState s) { @@ -1022,7 +1012,7 @@ public T g(FieldSpacecraftState s) { @Test - public void testResetAdditionalStateEvent() { + void testResetAdditionalStateEvent() { doTestResetAdditionalStateEvent(Binary64Field.getInstance()); } @@ -1056,8 +1046,8 @@ public FieldSpacecraftState resetState(FieldEventDetector detector, FieldS } }; - propagator.addEventDetector(new AdditionalStateLinearDetector(field.getZero().add(10.0), - field.getZero().add(1.0e-8)).withHandler(checking)); + propagator.addEventDetector(new AdditionalStateLinearDetector<>(field.getZero().add(10.0), + field.getZero().add(1.0e-8)).withHandler(checking)); final double dt = 3200; checking.assertEvent(false); @@ -1070,7 +1060,7 @@ public FieldSpacecraftState resetState(FieldEventDetector detector, FieldS } @Test - public void testEventDetectionBug() { + void testEventDetectionBug() { doTestEventDetectionBug(Binary64Field.getInstance()); } @@ -1079,8 +1069,6 @@ private > void doTestEventDetectionBug(final T zero = field.getZero(); TimeScale utc = TimeScalesFactory.getUTC(); FieldAbsoluteDate initialDate = new FieldAbsoluteDate<>(field, 2005, 1, 1, 0, 0, 0.0, utc); - T duration = zero.add(100000.0); - FieldAbsoluteDate endDate = new FieldAbsoluteDate<>(initialDate, duration); // Initialization of the frame EME2000 Frame EME2000 = FramesFactory.getEME2000(); @@ -1096,8 +1084,8 @@ private > void doTestEventDetectionBug(final EME2000, initialDate, zero.add(mu)); - duration = geo.getKeplerianPeriod(); - endDate = new FieldAbsoluteDate<>(initialDate, duration); + T duration = geo.getKeplerianPeriod(); + FieldAbsoluteDate endDate = new FieldAbsoluteDate<>(initialDate, duration); // Numerical Integration final double minStep = 0.001; @@ -1137,7 +1125,7 @@ private > void doTestEventDetectionBug(final } @Test - public void testEphemerisGenerationIssue14() { + void testEphemerisGenerationIssue14() { doTestEphemerisGenerationIssue14(Binary64Field.getInstance()); } @@ -1174,7 +1162,7 @@ private > void doTestEphemerisGenerationIssue1 } @Test - public void testEphemerisAdditionalState() { + void testEphemerisAdditionalState() { doTestEphemerisAdditionalState(Binary64Field.getInstance()); } @@ -1248,7 +1236,7 @@ public FieldCombinedDerivatives combinedDerivatives(FieldSpacecraftState s } @Test - public void testIssue157() { + void testIssue157() { doTestIssue157(Binary64Field.getInstance()); } @@ -1272,7 +1260,7 @@ private > void doTestIssue157(final Field f } @Test - public void testIssue704() { + void testIssue704() { doTestIssue704(Binary64Field.getInstance()); } @@ -1313,7 +1301,7 @@ private > void doTestIssue704(final Field f } @Test - public void testShiftKeplerianEllipticTrueWithoutDerivatives() { + void testShiftKeplerianEllipticTrueWithoutDerivatives() { doTestShiftKeplerianEllipticTrueWithoutDerivatives(Binary64Field.getInstance()); } @@ -1323,7 +1311,7 @@ private > void doTestShiftKeplerianEllipticTru } @Test - public void testShiftKeplerianEllipticTrueWithDerivatives() { + void testShiftKeplerianEllipticTrueWithDerivatives() { doTestShiftKeplerianEllipticTrueWithDerivatives(Binary64Field.getInstance()); } @@ -1333,7 +1321,7 @@ private > void doTestShiftKeplerianEllipticTru } @Test - public void testShiftKeplerianEllipticEccentricWithoutDerivatives() { + void testShiftKeplerianEllipticEccentricWithoutDerivatives() { doTestShiftKeplerianEllipticEccentricWithoutDerivatives(Binary64Field.getInstance()); } @@ -1343,7 +1331,7 @@ private > void doTestShiftKeplerianEllipticEcc } @Test - public void testShiftKeplerianEllipticEcentricWithDerivatives() { + void testShiftKeplerianEllipticEcentricWithDerivatives() { doTestShiftKeplerianEllipticEcentricWithDerivatives(Binary64Field.getInstance()); } @@ -1353,7 +1341,7 @@ private > void doTestShiftKeplerianEllipticEce } @Test - public void testShiftKeplerianEllipticMeanWithoutDerivatives() { + void testShiftKeplerianEllipticMeanWithoutDerivatives() { doTestShiftKeplerianEllipticMeanWithoutDerivatives(Binary64Field.getInstance()); } @@ -1363,7 +1351,7 @@ private > void doTestShiftKeplerianEllipticMea } @Test - public void testShiftKeplerianEllipticMeanWithDerivatives() { + void testShiftKeplerianEllipticMeanWithDerivatives() { doTestShiftKeplerianEllipticMeanWithDerivatives(Binary64Field.getInstance()); } @@ -1373,7 +1361,7 @@ private > void doTestShiftKeplerianEllipticMea } @Test - public void testShiftKeplerianHyperbolicTrueWithoutDerivatives() { + void testShiftKeplerianHyperbolicTrueWithoutDerivatives() { doTestShiftKeplerianHyperbolicTrueWithoutDerivatives(Binary64Field.getInstance()); } @@ -1383,7 +1371,7 @@ private > void doTestShiftKeplerianHyperbolicT } @Test - public void testShiftKeplerianHyperbolicTrueWithDerivatives() { + void testShiftKeplerianHyperbolicTrueWithDerivatives() { doTestShiftKeplerianHyperbolicTrueWithDerivatives(Binary64Field.getInstance()); } @@ -1393,7 +1381,7 @@ private > void doTestShiftKeplerianHyperbolicT } @Test - public void testShiftKeplerianHyperbolicEccentricWithoutDerivatives() { + void testShiftKeplerianHyperbolicEccentricWithoutDerivatives() { doTestShiftKeplerianHyperbolicEccentricWithoutDerivatives(Binary64Field.getInstance()); } @@ -1403,7 +1391,7 @@ private > void doTestShiftKeplerianHyperbolicE } @Test - public void testShiftKeplerianHyperbolicEcentricWithDerivatives() { + void testShiftKeplerianHyperbolicEcentricWithDerivatives() { doTestShiftKeplerianHyperbolicEcentricWithDerivatives(Binary64Field.getInstance()); } @@ -1413,7 +1401,7 @@ private > void doTestShiftKeplerianHyperbolicE } @Test - public void testShiftKeplerianHyperbolicMeanWithoutDerivatives() { + void testShiftKeplerianHyperbolicMeanWithoutDerivatives() { doTestShiftKeplerianHyperbolicMeanWithoutDerivatives(Binary64Field.getInstance()); } @@ -1423,7 +1411,7 @@ private > void doTestShiftKeplerianHyperbolicM } @Test - public void testShiftKeplerianHyperbolicMeanWithDerivatives() { + void testShiftKeplerianHyperbolicMeanWithDerivatives() { doTestShiftKeplerianHyperbolicMeanWithDerivatives(Binary64Field.getInstance()); } @@ -1433,7 +1421,7 @@ private > void doTestShiftKeplerianHyperbolicM } @Test - public void testShiftCartesianEllipticTrueWithoutDerivatives() { + void testShiftCartesianEllipticTrueWithoutDerivatives() { doTestShiftCartesianEllipticTrueWithoutDerivatives(Binary64Field.getInstance()); } @@ -1443,7 +1431,7 @@ private > void doTestShiftCartesianEllipticTru } @Test - public void testShiftCartesianEllipticTrueWithDerivatives() { + void testShiftCartesianEllipticTrueWithDerivatives() { doTestShiftCartesianEllipticTrueWithDerivatives(Binary64Field.getInstance()); } @@ -1453,7 +1441,7 @@ private > void doTestShiftCartesianEllipticTru } @Test - public void testShiftCartesianEllipticEccentricWithoutDerivatives() { + void testShiftCartesianEllipticEccentricWithoutDerivatives() { doTestShiftCartesianEllipticEccentricWithoutDerivatives(Binary64Field.getInstance()); } @@ -1463,7 +1451,7 @@ private > void doTestShiftCartesianEllipticEcc } @Test - public void testShiftCartesianEllipticEcentricWithDerivatives() { + void testShiftCartesianEllipticEcentricWithDerivatives() { doTestShiftCartesianEllipticEcentricWithDerivatives(Binary64Field.getInstance()); } @@ -1473,7 +1461,7 @@ private > void doTestShiftCartesianEllipticEce } @Test - public void testShiftCartesianEllipticMeanWithoutDerivatives() { + void testShiftCartesianEllipticMeanWithoutDerivatives() { doTestShiftCartesianEllipticMeanWithoutDerivatives(Binary64Field.getInstance()); } @@ -1483,7 +1471,7 @@ private > void doTestShiftCartesianEllipticMea } @Test - public void testShiftCartesianEllipticMeanWithDerivatives() { + void testShiftCartesianEllipticMeanWithDerivatives() { doTestShiftCartesianEllipticMeanWithDerivatives(Binary64Field.getInstance()); } @@ -1493,7 +1481,7 @@ private > void doTestShiftCartesianEllipticMea } @Test - public void testShiftCartesianHyperbolicTrueWithoutDerivatives() { + void testShiftCartesianHyperbolicTrueWithoutDerivatives() { doTestShiftCartesianHyperbolicTrueWithoutDerivatives(Binary64Field.getInstance()); } @@ -1503,7 +1491,7 @@ private > void doTestShiftCartesianHyperbolicT } @Test - public void testShiftCartesianHyperbolicTrueWithDerivatives() { + void testShiftCartesianHyperbolicTrueWithDerivatives() { doTestShiftCartesianHyperbolicTrueWithDerivatives(Binary64Field.getInstance()); } @@ -1513,7 +1501,7 @@ private > void doTestShiftCartesianHyperbolicT } @Test - public void testShiftCartesianHyperbolicEccentricWithoutDerivatives() { + void testShiftCartesianHyperbolicEccentricWithoutDerivatives() { doTestShiftCartesianHyperbolicEccentricWithoutDerivatives(Binary64Field.getInstance()); } @@ -1523,7 +1511,7 @@ private > void doTestShiftCartesianHyperbolicE } @Test - public void testShiftCartesianHyperbolicEcentricWithDerivatives() { + void testShiftCartesianHyperbolicEcentricWithDerivatives() { doTestShiftCartesianHyperbolicEcentricWithDerivatives(Binary64Field.getInstance()); } @@ -1533,7 +1521,7 @@ private > void doTestShiftCartesianHyperbolicE } @Test - public void testShiftCartesianHyperbolicMeanWithoutDerivatives() { + void testShiftCartesianHyperbolicMeanWithoutDerivatives() { doTestShiftCartesianHyperbolicMeanWithoutDerivatives(Binary64Field.getInstance()); } @@ -1543,7 +1531,7 @@ private > void doTestShiftCartesianHyperbolicM } @Test - public void testShiftCartesianHyperbolicMeanWithDerivatives() { + void testShiftCartesianHyperbolicMeanWithDerivatives() { doTestShiftCartesianHyperbolicMeanWithDerivatives(Binary64Field.getInstance()); } @@ -1553,7 +1541,7 @@ private > void doTestShiftCartesianHyperbolicM } @Test - public void testShiftCircularTrueWithoutDerivatives() { + void testShiftCircularTrueWithoutDerivatives() { doTestShiftCircularTrueWithoutDerivatives(Binary64Field.getInstance()); } @@ -1563,7 +1551,7 @@ private > void doTestShiftCircularTrueWithoutD } @Test - public void testShiftCircularTrueWithDerivatives() { + void testShiftCircularTrueWithDerivatives() { doTestShiftCircularTrueWithDerivatives(Binary64Field.getInstance()); } @@ -1573,7 +1561,7 @@ private > void doTestShiftCircularTrueWithDeri } @Test - public void testShiftCircularEccentricWithoutDerivatives() { + void testShiftCircularEccentricWithoutDerivatives() { doTestShiftCircularEccentricWithoutDerivatives(Binary64Field.getInstance()); } @@ -1583,7 +1571,7 @@ private > void doTestShiftCircularEccentricWit } @Test - public void testShiftCircularEcentricWithDerivatives() { + void testShiftCircularEcentricWithDerivatives() { doTestShiftCircularEcentricWithDerivatives(Binary64Field.getInstance()); } @@ -1593,7 +1581,7 @@ private > void doTestShiftCircularEcentricWith } @Test - public void testShiftCircularMeanWithoutDerivatives() { + void testShiftCircularMeanWithoutDerivatives() { doTestShiftCircularMeanWithoutDerivatives(Binary64Field.getInstance()); } @@ -1603,7 +1591,7 @@ private > void doTestShiftCircularMeanWithoutD } @Test - public void testShiftCircularMeanWithDerivatives() { + void testShiftCircularMeanWithDerivatives() { doTestShiftCircularMeanWithDerivatives(Binary64Field.getInstance()); } @@ -1613,7 +1601,7 @@ private > void doTestShiftCircularMeanWithDeri } @Test - public void testShiftEquinoctialTrueWithoutDerivatives() { + void testShiftEquinoctialTrueWithoutDerivatives() { doTestShiftEquinoctialTrueWithoutDerivatives(Binary64Field.getInstance()); } @@ -1623,7 +1611,7 @@ private > void doTestShiftEquinoctialTrueWitho } @Test - public void testShiftEquinoctialTrueWithDerivatives() { + void testShiftEquinoctialTrueWithDerivatives() { doTestShiftEquinoctialTrueWithDerivatives(Binary64Field.getInstance()); } @@ -1633,7 +1621,7 @@ private > void doTestShiftEquinoctialTrueWithD } @Test - public void testShiftEquinoctialEccentricWithoutDerivatives() { + void testShiftEquinoctialEccentricWithoutDerivatives() { doTestShiftEquinoctialEccentricWithoutDerivatives(Binary64Field.getInstance()); } @@ -1643,7 +1631,7 @@ private > void doTestShiftEquinoctialEccentric } @Test - public void testShiftEquinoctialEcentricWithDerivatives() { + void testShiftEquinoctialEcentricWithDerivatives() { doTtestShiftEquinoctialEcentricWithDerivatives(Binary64Field.getInstance()); } @@ -1653,7 +1641,7 @@ private > void doTtestShiftEquinoctialEcentric } @Test - public void testShiftEquinoctialMeanWithoutDerivatives() { + void testShiftEquinoctialMeanWithoutDerivatives() { doTestShiftEquinoctialMeanWithoutDerivatives(Binary64Field.getInstance()); } @@ -1663,7 +1651,7 @@ private > void doTestShiftEquinoctialMeanWitho } @Test - public void testShiftEquinoctialMeanWithDerivatives() { + void testShiftEquinoctialMeanWithDerivatives() { doTestShiftEquinoctialMeanWithDerivatives(Binary64Field.getInstance()); } @@ -1673,7 +1661,7 @@ private > void doTestShiftEquinoctialMeanWithD } @Test - public void testAdditionalDerivatives() { + void testAdditionalDerivatives() { doTestAdditionalDerivatives(Binary64Field.getInstance()); } @@ -1758,7 +1746,7 @@ public T[] getInitialState() { } @Test - public void testInfinitePropagation() { + void testInfinitePropagation() { doTestInfinitePropagation(Binary64Field.getInstance()); } @@ -1772,7 +1760,7 @@ private > void doTestInfinitePropagation(Fiel // Stop condition T convergenceThreshold = field.getZero().add(1e-9); - propagator.addEventDetector(new FieldDateDetector(field, propagator.getInitialState().getDate().shiftedBy(60)). + propagator.addEventDetector(new FieldDateDetector<>(field, propagator.getInitialState().getDate().shiftedBy(60)). withMaxCheck(1e10).withThreshold(convergenceThreshold)); // Propagate until the stop condition is reached @@ -1799,6 +1787,72 @@ void getIntegratorNameTest() { Assertions.assertEquals(expectedName, actualName); } + @Test + void testIssue1395() { + // GIVEN + final ComplexField complexField = ComplexField.getInstance(); + final FieldOrbit initialOrbit = createEllipticOrbit(complexField); + final ClassicalRungeKuttaFieldIntegrator rungeKuttaIntegrator = new ClassicalRungeKuttaFieldIntegrator<>(complexField, + complexField.getZero().newInstance(10.)); + final FieldNumericalPropagator numericalPropagator = new FieldNumericalPropagator<>(complexField, + rungeKuttaIntegrator); + final FieldSpacecraftState state = new FieldSpacecraftState<>(initialOrbit); + final String name = "test"; + numericalPropagator.setInitialState(state.addAdditionalState(name, Complex.ZERO)); + numericalPropagator.addAdditionalDerivativesProvider(mockDerivativeProvider(name)); + numericalPropagator.addForceModel(createForceModelBasedOnAdditionalState(name)); + // WHEN & THEN + final FieldAbsoluteDate epoch = initialOrbit.getDate(); + final FieldSpacecraftState propagateState = Assertions.assertDoesNotThrow(() -> + numericalPropagator.propagate(epoch.shiftedBy(10.))); + Assertions.assertNotEquals(epoch, propagateState.getDate()); + } + + @SuppressWarnings("unchecked") + private static FieldAdditionalDerivativesProvider mockDerivativeProvider(final String name) { + final FieldAdditionalDerivativesProvider mockedProvider = Mockito.mock(FieldAdditionalDerivativesProvider.class); + final int dimension = 1; + Mockito.when(mockedProvider.getDimension()).thenReturn(dimension); + Mockito.when(mockedProvider.getName()).thenReturn(name); + final Complex[] yDot = new Complex[dimension]; + yDot[0] = Complex.ZERO; + final FieldCombinedDerivatives combinedDerivatives = new FieldCombinedDerivatives<>(yDot, null); + Mockito.when(mockedProvider.combinedDerivatives(Mockito.any(FieldSpacecraftState.class))) + .thenReturn(combinedDerivatives); + return mockedProvider; + } + + private static ForceModel createForceModelBasedOnAdditionalState(final String name) { + return new ForceModel() { + + @Override + public void init(SpacecraftState initialState, AbsoluteDate target) { + ForceModel.super.init(initialState, target); + initialState.getAdditionalState(name); + } + + @Override + public boolean dependsOnPositionOnly() { + return false; + } + + @Override + public Vector3D acceleration(SpacecraftState s, double[] parameters) { + return null; // not used + } + + @Override + public > FieldVector3D acceleration(FieldSpacecraftState s, T[] parameters) { + return FieldVector3D.getZero(s.getDate().getField()); + } + + @Override + public List getParametersDrivers() { + return new ArrayList<>(); + } + }; + } + private static > void doTestShift(final FieldCartesianOrbit orbit, final OrbitType orbitType, final PositionAngleType angleType, final boolean withDerivatives, final double error60s, final double error120s, @@ -1827,7 +1881,7 @@ private static > void doTestShift(final FieldC dates[3] = reference.shiftedBy(300.0); dates[4] = reference.shiftedBy(600.0); dates[5] = reference.shiftedBy(900.0); - np.addEventDetector(new FieldDateDetector(field, (FieldTimeStamped[]) dates). + np.addEventDetector(new FieldDateDetector<>(field, (FieldTimeStamped[]) dates). withMaxCheck(30). withThreshold(zero.newInstance(1.0e-9)). withHandler(checker)); @@ -1991,7 +2045,7 @@ private static > FieldCartesianOrbit create return new FieldCartesianOrbit<>(pv, frame, zero.add(mu)); } - private class CheckingHandler> implements FieldEventHandler { + private static class CheckingHandler> implements FieldEventHandler { private final Action actionOnEvent; private boolean gotHere; @@ -2045,6 +2099,82 @@ private > FieldTimeStamped[] toArray(final return array; } + @Test + @DisplayName("Test for regression in Hipparchus (issue #1281 in Orekit and #290 in Hipparchus)") + void testIssue1281() { + final GradientField field = GradientField.getField(1); + final FieldAbsoluteDate fieldEpoch = FieldAbsoluteDate.getArbitraryEpoch(field); + final FieldPVCoordinates fieldPVCoordinates = new FieldPVCoordinates<>( + new FieldVector3D<>(field, new Vector3D(10000.e3, 0, 0)), + new FieldVector3D<>(field, new Vector3D(0, 7.5e3, 0))); + final FieldCartesianOrbit fieldOrbit = new FieldCartesianOrbit<>(fieldPVCoordinates, + FramesFactory.getGCRF(), fieldEpoch, field.getZero().add(Constants.EGM96_EARTH_MU)); + final ClassicalRungeKuttaFieldIntegrator fieldIntegrator = new ClassicalRungeKuttaFieldIntegrator<>(field, + field.getZero().add(5.)); + final FieldNumericalPropagator fieldNumericalPropagator = new FieldNumericalPropagator<>( + field, fieldIntegrator); + fieldNumericalPropagator.resetInitialState(new FieldSpacecraftState<>(fieldOrbit)); + fieldNumericalPropagator.setResetAtEnd(true); + + final AbsoluteDate epoch = fieldEpoch.toAbsoluteDate(); + final TestForceIssue1281 testForce = new TestForceIssue1281(epoch.shiftedBy(10.), + epoch.shiftedBy(100.), epoch.shiftedBy(1000.)); + fieldNumericalPropagator.addForceModel(testForce); + + final Gradient variable = Gradient.variable(1, 0, 0.); + try { + for (AbsoluteDate date : testForce.dates) { + final FieldAbsoluteDate fieldDate = new FieldAbsoluteDate<>(field, date).shiftedBy(variable); + fieldNumericalPropagator.propagate(fieldDate); + } + } catch (final MathRuntimeException exception) { + Assertions.fail("Regression w.r.t. Orekit 11.3", exception); + } + + } + + private static class TestForceIssue1281 implements ForceModel { + + private final AbsoluteDate[] dates; + + TestForceIssue1281(AbsoluteDate ...dates) { + this.dates = dates; + } + + @Override + public boolean dependsOnPositionOnly() { + return true; + } + + @Override + public Stream getEventDetectors() { + return Arrays.stream(dates).map(DateDetector::new) + .map(d -> d.withHandler((a, b, c) -> Action.RESET_DERIVATIVES)); + } + + @Override + public > Stream> getFieldEventDetectors(Field field) { + return Arrays.stream(dates).map(date -> new FieldDateDetector<>(field, new FieldAbsoluteDate<>(field, date))) + .map(d -> d.withHandler((a, b, c) -> Action.RESET_DERIVATIVES)); + } + + @Override + public Vector3D acceleration(SpacecraftState s, double[] parameters) { + return Vector3D.ZERO; + } + + @Override + public > FieldVector3D acceleration(FieldSpacecraftState s, T[] parameters) { + return FieldVector3D.getZero(s.getDate().getField()); + } + + @Override + public List getParametersDrivers() { + return new ArrayList<>(); + } + } + + @BeforeEach public void setUp() { Utils.setDataRoot("regular-data:potential/shm-format"); diff --git a/src/test/java/org/orekit/propagation/numerical/NumericalPropagatorTest.java b/src/test/java/org/orekit/propagation/numerical/NumericalPropagatorTest.java index 8ee8b39f31..5763e0c98e 100644 --- a/src/test/java/org/orekit/propagation/numerical/NumericalPropagatorTest.java +++ b/src/test/java/org/orekit/propagation/numerical/NumericalPropagatorTest.java @@ -34,6 +34,7 @@ import org.hipparchus.Field; import org.hipparchus.exception.LocalizedCoreFormats; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; +import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.ode.ODEIntegrator; import org.hipparchus.ode.events.Action; @@ -49,7 +50,7 @@ import org.mockito.Mockito; import org.orekit.OrekitMatchers; import org.orekit.Utils; -import org.orekit.attitudes.LofOffset; +import org.orekit.attitudes.*; import org.orekit.bodies.CelestialBodyFactory; import org.orekit.bodies.OneAxisEllipsoid; import org.orekit.errors.OrekitException; @@ -103,16 +104,20 @@ import org.orekit.propagation.sampling.OrekitStepInterpolator; import org.orekit.time.AbsoluteDate; import org.orekit.time.DateComponents; +import org.orekit.time.FieldAbsoluteDate; import org.orekit.time.TimeComponents; import org.orekit.time.TimeScale; import org.orekit.time.TimeScalesFactory; +import org.orekit.utils.AngularCoordinates; import org.orekit.utils.Constants; +import org.orekit.utils.FieldPVCoordinatesProvider; import org.orekit.utils.IERSConventions; import org.orekit.utils.PVCoordinates; +import org.orekit.utils.PVCoordinatesProvider; import org.orekit.utils.ParameterDriver; import org.orekit.utils.TimeStampedPVCoordinates; -public class NumericalPropagatorTest { +class NumericalPropagatorTest { private double mu; private AbsoluteDate initDate; @@ -120,12 +125,12 @@ public class NumericalPropagatorTest { private NumericalPropagator propagator; @Test - public void testIssue1032() { + void testIssue1032() { Assertions.assertEquals(PropagationType.OSCULATING, propagator.getPropagationType()); } @Test - public void testEventsWithTimeRangePropagation() { + void testEventsWithTimeRangePropagation() { final AtomicInteger counter = new AtomicInteger(0); final double dt = 60.0; final EventDetector singleDetector = new DateDetector(initDate.shiftedBy(dt / 2)). @@ -151,7 +156,7 @@ public Stream getEventDetectors() { } @Test - public void testForceModelInitialized() { + void testForceModelInitialized() { // setup // mutable holders SpacecraftState[] actualState = new SpacecraftState[1]; @@ -179,7 +184,7 @@ public void init(SpacecraftState initialState, AbsoluteDate target) { } @Test - public void testEphemerisModeWithHandler() { + void testEphemerisModeWithHandler() { // setup AbsoluteDate end = initDate.shiftedBy(90 * 60); @@ -201,7 +206,7 @@ public void testEphemerisModeWithHandler() { /** test for issue #238 */ @Test - public void testEventAtEndOfEphemeris() { + void testEventAtEndOfEphemeris() { // setup // choose duration that will round up when expressed as a double AbsoluteDate end = initDate.shiftedBy(100) @@ -229,7 +234,7 @@ public void testEventAtEndOfEphemeris() { /** test for issue #238 */ @Test - public void testEventAtBeginningOfEphemeris() { + void testEventAtBeginningOfEphemeris() { // setup // choose duration that will round up when expressed as a double AbsoluteDate end = initDate.shiftedBy(100) @@ -286,7 +291,7 @@ public SpacecraftState resetState(EventDetector detector, * each other. */ @Test - public void testCloseEventDates() { + void testCloseEventDates() { // setup DateDetector d1 = new DateDetector(initDate.shiftedBy(15)). withMaxCheck(10). @@ -308,7 +313,7 @@ public void testCloseEventDates() { } @Test - public void testEphemerisDates() { + void testEphemerisDates() { //setup TimeScale tai = TimeScalesFactory.getTAI(); AbsoluteDate initialDate = new AbsoluteDate("2015-07-01", tai); @@ -348,7 +353,7 @@ public void testEphemerisDates() { } @Test - public void testEphemerisDatesBackward() { + void testEphemerisDatesBackward() { //setup TimeScale tai = TimeScalesFactory.getTAI(); AbsoluteDate initialDate = new AbsoluteDate("2015-07-05", tai); @@ -388,7 +393,7 @@ public void testEphemerisDatesBackward() { } @Test - public void testNoExtrapolation() { + void testNoExtrapolation() { // Propagate of the initial at the initial date final SpacecraftState finalState = propagator.propagate(initDate); @@ -412,7 +417,7 @@ public void testNoExtrapolation() { } @Test - public void testNotInitialised1() { + void testNotInitialised1() { Assertions.assertThrows(OrekitException.class, () -> { final AbstractIntegratedPropagator notInitialised = new NumericalPropagator(new ClassicalRungeKuttaIntegrator(10.0)); @@ -421,7 +426,7 @@ public void testNotInitialised1() { } @Test - public void testNotInitialised2() { + void testNotInitialised2() { Assertions.assertThrows(OrekitException.class, () -> { final AbstractIntegratedPropagator notInitialised = new NumericalPropagator(new ClassicalRungeKuttaIntegrator(10.0)); @@ -430,7 +435,7 @@ public void testNotInitialised2() { } @Test - public void testKepler() { + void testKepler() { // Propagation of the initial at t + dt final double dt = 3200; @@ -449,7 +454,7 @@ public void testKepler() { } @Test - public void testCartesian() { + void testCartesian() { // Propagation of the initial at t + dt final double dt = 3200; @@ -469,7 +474,7 @@ public void testCartesian() { } @Test - public void testPropagationTypesElliptical() throws ParseException, IOException { + void testPropagationTypesElliptical() throws ParseException, IOException { // setup AbsoluteDate initDate = new AbsoluteDate(); SpacecraftState initialState; @@ -543,7 +548,7 @@ public void testPropagationTypesElliptical() throws ParseException, IOException } @Test - public void testPropagationTypesHyperbolic() throws ParseException, IOException { + void testPropagationTypesHyperbolic() throws ParseException, IOException { SpacecraftState state = new SpacecraftState(new KeplerianOrbit(-10000000.0, 2.5, 0.3, 0, 0, 0.0, @@ -609,7 +614,7 @@ private PVCoordinates propagateInType(SpacecraftState state, double dP, } @Test - public void testException() { + void testException() { Assertions.assertThrows(OrekitException.class, () -> { propagator.setStepHandler(new OrekitStepHandler() { private int countDown = 3; @@ -630,7 +635,7 @@ public void handleStep(OrekitStepInterpolator interpolator) { } @Test - public void testStopEvent() { + void testStopEvent() { final AbsoluteDate stopDate = initDate.shiftedBy(1000); CheckingHandler checking = new CheckingHandler(Action.STOP); propagator.addEventDetector(new DateDetector(stopDate).withHandler(checking)); @@ -645,7 +650,7 @@ public void testStopEvent() { } @Test - public void testResetStateEvent() { + void testResetStateEvent() { final AbsoluteDate resetDate = initDate.shiftedBy(1000); CheckingHandler checking = new CheckingHandler(Action.RESET_STATE) { public SpacecraftState resetState(EventDetector detector, SpacecraftState oldState) { @@ -660,7 +665,7 @@ public SpacecraftState resetState(EventDetector detector, SpacecraftState oldSta } @Test - public void testResetDerivativesEvent() { + void testResetDerivativesEvent() { final AbsoluteDate resetDate = initDate.shiftedBy(1000); CheckingHandler checking = new CheckingHandler(Action.RESET_DERIVATIVES); propagator.addEventDetector(new DateDetector(resetDate).withHandler(checking)); @@ -682,7 +687,7 @@ public void testResetDerivativesEvent() { } @Test - public void testContinueEvent() { + void testContinueEvent() { final AbsoluteDate resetDate = initDate.shiftedBy(1000); CheckingHandler checking = new CheckingHandler(Action.CONTINUE); propagator.addEventDetector(new DateDetector(resetDate).withHandler(checking)); @@ -704,7 +709,7 @@ public void testContinueEvent() { } @Test - public void testAdditionalStateEvent() { + void testAdditionalStateEvent() { propagator.addAdditionalDerivativesProvider(new AdditionalDerivativesProvider() { public String getName() { @@ -789,7 +794,7 @@ public double[] getAdditionalState(SpacecraftState state) { private static class AdditionalStateLinearDetector extends AbstractDetector { public AdditionalStateLinearDetector(double maxCheck, double threshold) { - this(s -> maxCheck, threshold, DEFAULT_MAX_ITER, new StopOnEvent()); + this(AdaptableInterval.of(maxCheck), threshold, DEFAULT_MAX_ITER, new StopOnEvent()); } private AdditionalStateLinearDetector(AdaptableInterval maxCheck, double threshold, int maxIter, EventHandler handler) { @@ -808,7 +813,7 @@ public double g(SpacecraftState s) { } @Test - public void testResetAdditionalStateEvent() { + void testResetAdditionalStateEvent() { propagator.addAdditionalDerivativesProvider(new AdditionalDerivativesProvider() { public String getName() { @@ -844,7 +849,7 @@ public SpacecraftState resetState(EventDetector detector, SpacecraftState oldSta } @Test - public void testEventDetectionBug() throws IOException, ParseException { + void testEventDetectionBug() throws IOException, ParseException { TimeScale utc = TimeScalesFactory.getUTC(); AbsoluteDate initialDate = new AbsoluteDate(2005, 1, 1, 0, 0, 0.0, utc); @@ -904,7 +909,7 @@ public void testEventDetectionBug() throws IOException, ParseException { } @Test - public void testEphemerisGenerationIssue14() throws IOException { + void testEphemerisGenerationIssue14() throws IOException { // Propagation of the initial at t + dt final double dt = 3200; @@ -935,7 +940,7 @@ public void testEphemerisGenerationIssue14() throws IOException { } @Test - public void testEphemerisAdditionalState() throws IOException { + void testEphemerisAdditionalState() throws IOException { // Propagation of the initial at t + dt final double dt = -3200; @@ -999,7 +1004,7 @@ public CombinedDerivatives combinedDerivatives(SpacecraftState s) { } @Test - public void testIssue157() { + void testIssue157() { try { Orbit orbit = new KeplerianOrbit(13378000, 0.05, 0, 0, FastMath.PI, 0, PositionAngleType.MEAN, FramesFactory.getTOD(false), @@ -1013,7 +1018,7 @@ public void testIssue157() { } @Test - public void testIssue704() { + void testIssue704() { // Coordinates final Orbit orbit = initialState.getOrbit(); @@ -1065,7 +1070,7 @@ public Action eventOccurred(SpacecraftState s, EventDetector detector, boolean i } @Test - public void testParallelismIssue258() throws InterruptedException, ExecutionException, FileNotFoundException { + void testParallelismIssue258() throws InterruptedException, ExecutionException, FileNotFoundException { Utils.setDataRoot("regular-data:atmosphere:potential/grgs-format"); GravityFieldFactory.addPotentialCoefficientsReader(new GRGSFormatReader("grim4s4_gr", true)); @@ -1121,217 +1126,217 @@ public void testParallelismIssue258() throws InterruptedException, ExecutionExce } @Test - public void testShiftKeplerianEllipticTrueWithoutDerivatives() { + void testShiftKeplerianEllipticTrueWithoutDerivatives() { doTestShift(createEllipticOrbit(), OrbitType.KEPLERIAN, PositionAngleType.TRUE, false, 18.1, 72.0, 437.3, 1601.1, 3141.8); } @Test - public void testShiftKeplerianEllipticTrueWithDerivatives() { + void testShiftKeplerianEllipticTrueWithDerivatives() { doTestShift(createEllipticOrbit(), OrbitType.KEPLERIAN, PositionAngleType.TRUE, true, 1.14, 9.1, 140.3, 1066.7, 3306.9); } @Test - public void testShiftKeplerianEllipticEccentricWithoutDerivatives() { + void testShiftKeplerianEllipticEccentricWithoutDerivatives() { doTestShift(createEllipticOrbit(), OrbitType.KEPLERIAN, PositionAngleType.ECCENTRIC, false, 18.1, 72.0, 437.3, 1601.1, 3141.8); } @Test - public void testShiftKeplerianEllipticEcentricWithDerivatives() { + void testShiftKeplerianEllipticEcentricWithDerivatives() { doTestShift(createEllipticOrbit(), OrbitType.KEPLERIAN, PositionAngleType.ECCENTRIC, true, 1.14, 9.1, 140.3, 1066.7, 3306.9); } @Test - public void testShiftKeplerianEllipticMeanWithoutDerivatives() { + void testShiftKeplerianEllipticMeanWithoutDerivatives() { doTestShift(createEllipticOrbit(), OrbitType.KEPLERIAN, PositionAngleType.MEAN, false, 18.1, 72.0, 437.3, 1601.1, 3141.8); } @Test - public void testShiftKeplerianEllipticMeanWithDerivatives() { + void testShiftKeplerianEllipticMeanWithDerivatives() { doTestShift(createEllipticOrbit(), OrbitType.KEPLERIAN, PositionAngleType.MEAN, true, 1.14, 9.1, 140.3, 1066.7, 3306.9); } @Test - public void testShiftKeplerianHyperbolicTrueWithoutDerivatives() { + void testShiftKeplerianHyperbolicTrueWithoutDerivatives() { doTestShift(createHyperbolicOrbit(), OrbitType.KEPLERIAN, PositionAngleType.TRUE, false, 0.484, 1.94, 12.1, 48.3, 108.5); } @Test - public void testShiftKeplerianHyperbolicTrueWithDerivatives() { + void testShiftKeplerianHyperbolicTrueWithDerivatives() { doTestShift(createHyperbolicOrbit(), OrbitType.KEPLERIAN, PositionAngleType.TRUE, true, 1.38e-4, 1.10e-3, 1.72e-2, 1.37e-1, 4.62e-1); } @Test - public void testShiftKeplerianHyperbolicEccentricWithoutDerivatives() { + void testShiftKeplerianHyperbolicEccentricWithoutDerivatives() { doTestShift(createHyperbolicOrbit(), OrbitType.KEPLERIAN, PositionAngleType.ECCENTRIC, false, 0.484, 1.94, 12.1, 48.3, 108.5); } @Test - public void testShiftKeplerianHyperbolicEcentricWithDerivatives() { + void testShiftKeplerianHyperbolicEcentricWithDerivatives() { doTestShift(createHyperbolicOrbit(), OrbitType.KEPLERIAN, PositionAngleType.ECCENTRIC, true, 1.38e-4, 1.10e-3, 1.72e-2, 1.37e-1, 4.62e-1); } @Test - public void testShiftKeplerianHyperbolicMeanWithoutDerivatives() { + void testShiftKeplerianHyperbolicMeanWithoutDerivatives() { doTestShift(createHyperbolicOrbit(), OrbitType.KEPLERIAN, PositionAngleType.MEAN, false, 0.484, 1.94, 12.1, 48.3, 108.5); } @Test - public void testShiftKeplerianHyperbolicMeanWithDerivatives() { + void testShiftKeplerianHyperbolicMeanWithDerivatives() { doTestShift(createHyperbolicOrbit(), OrbitType.KEPLERIAN, PositionAngleType.MEAN, true, 1.38e-4, 1.10e-3, 1.72e-2, 1.37e-1, 4.62e-1); } @Test - public void testShiftCartesianEllipticTrueWithoutDerivatives() { + void testShiftCartesianEllipticTrueWithoutDerivatives() { doTestShift(createEllipticOrbit(), OrbitType.CARTESIAN, PositionAngleType.TRUE, false, 18.1, 72.0, 437.3, 1601.1, 3141.8); } @Test - public void testShiftCartesianEllipticTrueWithDerivatives() { + void testShiftCartesianEllipticTrueWithDerivatives() { doTestShift(createEllipticOrbit(), OrbitType.CARTESIAN, PositionAngleType.TRUE, true, 1.14, 9.1, 140.3, 1066.7, 3306.9); } @Test - public void testShiftCartesianEllipticEccentricWithoutDerivatives() { + void testShiftCartesianEllipticEccentricWithoutDerivatives() { doTestShift(createEllipticOrbit(), OrbitType.CARTESIAN, PositionAngleType.ECCENTRIC, false, 18.1, 72.0, 437.3, 1601.1, 3141.8); } @Test - public void testShiftCartesianEllipticEcentricWithDerivatives() { + void testShiftCartesianEllipticEcentricWithDerivatives() { doTestShift(createEllipticOrbit(), OrbitType.CARTESIAN, PositionAngleType.ECCENTRIC, true, 1.14, 9.1, 140.3, 1066.7, 3306.9); } @Test - public void testShiftCartesianEllipticMeanWithoutDerivatives() { + void testShiftCartesianEllipticMeanWithoutDerivatives() { doTestShift(createEllipticOrbit(), OrbitType.CARTESIAN, PositionAngleType.MEAN, false, 18.1, 72.0, 437.3, 1601.1, 3141.8); } @Test - public void testShiftCartesianEllipticMeanWithDerivatives() { + void testShiftCartesianEllipticMeanWithDerivatives() { doTestShift(createEllipticOrbit(), OrbitType.CARTESIAN, PositionAngleType.MEAN, true, 1.14, 9.1, 140.3, 1066.7, 3306.9); } @Test - public void testShiftCartesianHyperbolicTrueWithoutDerivatives() { + void testShiftCartesianHyperbolicTrueWithoutDerivatives() { doTestShift(createHyperbolicOrbit(), OrbitType.CARTESIAN, PositionAngleType.TRUE, false, 0.48, 1.93, 12.1, 48.3, 108.5); } @Test - public void testShiftCartesianHyperbolicTrueWithDerivatives() { + void testShiftCartesianHyperbolicTrueWithDerivatives() { doTestShift(createHyperbolicOrbit(), OrbitType.CARTESIAN, PositionAngleType.TRUE, true, 1.38e-4, 1.10e-3, 1.72e-2, 1.37e-1, 4.62e-1); } @Test - public void testShiftCartesianHyperbolicEccentricWithoutDerivatives() { + void testShiftCartesianHyperbolicEccentricWithoutDerivatives() { doTestShift(createHyperbolicOrbit(), OrbitType.CARTESIAN, PositionAngleType.ECCENTRIC, false, 0.48, 1.93, 12.1, 48.3, 108.5); } @Test - public void testShiftCartesianHyperbolicEcentricWithDerivatives() { + void testShiftCartesianHyperbolicEcentricWithDerivatives() { doTestShift(createHyperbolicOrbit(), OrbitType.CARTESIAN, PositionAngleType.ECCENTRIC, true, 1.38e-4, 1.10e-3, 1.72e-2, 1.37e-1, 4.62e-1); } @Test - public void testShiftCartesianHyperbolicMeanWithoutDerivatives() { + void testShiftCartesianHyperbolicMeanWithoutDerivatives() { doTestShift(createHyperbolicOrbit(), OrbitType.CARTESIAN, PositionAngleType.MEAN, false, 0.48, 1.93, 12.1, 48.3, 108.5); } @Test - public void testShiftCartesianHyperbolicMeanWithDerivatives() { + void testShiftCartesianHyperbolicMeanWithDerivatives() { doTestShift(createHyperbolicOrbit(), OrbitType.CARTESIAN, PositionAngleType.MEAN, true, 1.38e-4, 1.10e-3, 1.72e-2, 1.37e-1, 4.62e-1); } @Test - public void testShiftCircularTrueWithoutDerivatives() { + void testShiftCircularTrueWithoutDerivatives() { doTestShift(createEllipticOrbit(), OrbitType.CIRCULAR, PositionAngleType.TRUE, false, 18.1, 72.0, 437.3, 1601.1, 3141.8); } @Test - public void testShiftCircularTrueWithDerivatives() { + void testShiftCircularTrueWithDerivatives() { doTestShift(createEllipticOrbit(), OrbitType.CIRCULAR, PositionAngleType.TRUE, true, 1.14, 9.1, 140.3, 1066.7, 3306.9); } @Test - public void testShiftCircularEccentricWithoutDerivatives() { + void testShiftCircularEccentricWithoutDerivatives() { doTestShift(createEllipticOrbit(), OrbitType.CIRCULAR, PositionAngleType.ECCENTRIC, false, 18.1, 72.0, 437.3, 1601.1, 3141.8); } @Test - public void testShiftCircularEcentricWithDerivatives() { + void testShiftCircularEcentricWithDerivatives() { doTestShift(createEllipticOrbit(), OrbitType.CIRCULAR, PositionAngleType.ECCENTRIC, true, 1.14, 9.1, 140.3, 1066.7, 3306.9); } @Test - public void testShiftCircularMeanWithoutDerivatives() { + void testShiftCircularMeanWithoutDerivatives() { doTestShift(createEllipticOrbit(), OrbitType.CIRCULAR, PositionAngleType.MEAN, false, 18.1, 72.0, 437.3, 1601.1, 3141.8); } @Test - public void testShiftCircularMeanWithDerivatives() { + void testShiftCircularMeanWithDerivatives() { doTestShift(createEllipticOrbit(), OrbitType.CIRCULAR, PositionAngleType.MEAN, true, 1.14, 9.1, 140.3, 1066.7, 3306.9); } @Test - public void testShiftEquinoctialTrueWithoutDerivatives() { + void testShiftEquinoctialTrueWithoutDerivatives() { doTestShift(createEllipticOrbit(), OrbitType.EQUINOCTIAL, PositionAngleType.TRUE, false, 18.1, 72.0, 437.3, 1601.1, 3141.8); } @Test - public void testShiftEquinoctialTrueWithDerivatives() { + void testShiftEquinoctialTrueWithDerivatives() { doTestShift(createEllipticOrbit(), OrbitType.EQUINOCTIAL, PositionAngleType.TRUE, true, 1.14, 9.1, 140.3, 1066.7, 3306.9); } @Test - public void testShiftEquinoctialEccentricWithoutDerivatives() { + void testShiftEquinoctialEccentricWithoutDerivatives() { doTestShift(createEllipticOrbit(), OrbitType.EQUINOCTIAL, PositionAngleType.ECCENTRIC, false, 18.1, 72.0, 437.3, 1601.1, 3141.8); } @Test - public void testShiftEquinoctialEcentricWithDerivatives() { + void testShiftEquinoctialEcentricWithDerivatives() { doTestShift(createEllipticOrbit(), OrbitType.EQUINOCTIAL, PositionAngleType.ECCENTRIC, true, 1.14, 9.1, 140.3, 1066.7, 3306.9); } @Test - public void testShiftEquinoctialMeanWithoutDerivatives() { + void testShiftEquinoctialMeanWithoutDerivatives() { doTestShift(createEllipticOrbit(), OrbitType.EQUINOCTIAL, PositionAngleType.MEAN, false, 18.1, 72.0, 437.3, 1601.1, 3141.8); } @Test - public void testShiftEquinoctialMeanWithDerivatives() { + void testShiftEquinoctialMeanWithDerivatives() { doTestShift(createEllipticOrbit(), OrbitType.EQUINOCTIAL, PositionAngleType.MEAN, true, 1.14, 9.1, 140.3, 1066.7, 3306.9); } @@ -1449,7 +1454,7 @@ public Action eventOccurred(final SpacecraftState s, final EventDetector detecto *

          */ @Test - public void testEventAndStepHandlerDeactivationIssue449() { + void testEventAndStepHandlerDeactivationIssue449() { // Setup RecordAndContinue recordAndContinue = new RecordAndContinue(); @@ -1480,7 +1485,7 @@ public void testEventAndStepHandlerDeactivationIssue449() { } @Test - public void testResetStateForward() { + void testResetStateForward() { final Frame eme2000 = FramesFactory.getEME2000(); final AbsoluteDate date = new AbsoluteDate(new DateComponents(2008, 6, 23), new TimeComponents(14, 0, 0), @@ -1514,7 +1519,7 @@ public void testResetStateForward() { } @Test - public void testResetStateBackward() { + void testResetStateBackward() { final Frame eme2000 = FramesFactory.getEME2000(); final AbsoluteDate date = new AbsoluteDate(new DateComponents(2008, 6, 23), new TimeComponents(14, 0, 0), @@ -1548,7 +1553,7 @@ public void testResetStateBackward() { } @Test - public void testAdditionalDerivatives() { + void testAdditionalDerivatives() { final Frame eme2000 = FramesFactory.getEME2000(); final AbsoluteDate date = new AbsoluteDate(new DateComponents(2008, 6, 23), @@ -1558,7 +1563,7 @@ public void testAdditionalDerivatives() { eme2000, date, Constants.EIGEN5C_EARTH_MU); NumericalPropagatorBuilder builder = new NumericalPropagatorBuilder(initialOrbit, new DormandPrince853IntegratorBuilder(0.02, 0.2, 1.), PositionAngleType.TRUE, 10); - NumericalPropagator propagator = builder.buildPropagator(builder.getSelectedNormalizedParameters()); + NumericalPropagator propagator = (NumericalPropagator) builder.buildPropagator(); IntStream. range(0, 2). @@ -1615,7 +1620,7 @@ public double[] getInitialState() { } @Test - public void testInfinitePropagation() { + void testInfinitePropagation() { Utils.setDataRoot("regular-data:atmosphere:potential/grgs-format"); GravityFieldFactory.addPotentialCoefficientsReader(new GRGSFormatReader("grim4s4_gr", true)); @@ -1649,6 +1654,67 @@ void getIntegratorNameTest() { Assertions.assertEquals(expectedName, actualName); } + @Test + void testIssue1395() { + // GIVEN + final Orbit initialOrbit = createEllipticOrbit(); + final ClassicalRungeKuttaIntegrator rungeKuttaIntegrator = new ClassicalRungeKuttaIntegrator(10); + final NumericalPropagator numericalPropagator = new NumericalPropagator(rungeKuttaIntegrator); + final SpacecraftState state = new SpacecraftState(initialOrbit); + final String name = "test"; + numericalPropagator.setInitialState(state.addAdditionalState(name, 0.)); + numericalPropagator.addAdditionalDerivativesProvider(mockDerivativeProvider(name)); + numericalPropagator.addForceModel(createForceModelBasedOnAdditionalState(name)); + // WHEN & THEN + final AbsoluteDate epoch = initialOrbit.getDate(); + final SpacecraftState propagateState = Assertions.assertDoesNotThrow(() -> + numericalPropagator.propagate(epoch.shiftedBy(10.))); + Assertions.assertNotEquals(epoch, propagateState.getDate()); + } + + private static AdditionalDerivativesProvider mockDerivativeProvider(final String name) { + final AdditionalDerivativesProvider mockedProvider = Mockito.mock(AdditionalDerivativesProvider.class); + final int dimension = 1; + Mockito.when(mockedProvider.getDimension()).thenReturn(dimension); + Mockito.when(mockedProvider.getName()).thenReturn(name); + final double[] yDot = new double[dimension]; + final CombinedDerivatives combinedDerivatives = new CombinedDerivatives(yDot, null); + Mockito.when(mockedProvider.combinedDerivatives(Mockito.any(SpacecraftState.class))) + .thenReturn(combinedDerivatives); + return mockedProvider; + } + + private static ForceModel createForceModelBasedOnAdditionalState(final String name) { + return new ForceModel() { + + @Override + public void init(SpacecraftState initialState, AbsoluteDate target) { + ForceModel.super.init(initialState, target); + initialState.getAdditionalState(name); + } + + @Override + public boolean dependsOnPositionOnly() { + return false; + } + + @Override + public Vector3D acceleration(SpacecraftState s, double[] parameters) { + return Vector3D.ZERO; + } + + @Override + public > FieldVector3D acceleration(FieldSpacecraftState s, T[] parameters) { + return null; // not used + } + + @Override + public List getParametersDrivers() { + return new ArrayList<>(); + } + }; + } + /** Record the dates treated by the handler. * If they are out of an interval defined by a start and final date. */ @@ -1765,6 +1831,79 @@ private CartesianOrbit createHyperbolicOrbit() { return new CartesianOrbit(pv, frame, mu); } + @Test + void testFinalAttitudeWithForcesNeedingRates() { + final ForceModel dummyForceDependingOnRates = new ForceModel() { + @Override + public boolean dependsOnAttitudeRate() { + return true; + } + + @Override + public boolean dependsOnPositionOnly() { + return false; + } + + @Override + public Vector3D acceleration(SpacecraftState s, double[] parameters) { + return Vector3D.ZERO; + } + + @Override + public > FieldVector3D acceleration(FieldSpacecraftState s, T[] parameters) { + return null; + } + + @Override + public List getParametersDrivers() { + return Collections.emptyList(); + } + }; + final List forceModels = new ArrayList<>(); + forceModels.add(dummyForceDependingOnRates); + testTemplateFinalAttitudeWithoutForcesNeedingRates(forceModels); + } + + @Test + void testFinalAttitudeWithoutForcesNeedingRates() { + testTemplateFinalAttitudeWithoutForcesNeedingRates(new ArrayList<>()); + } + + private void testTemplateFinalAttitudeWithoutForcesNeedingRates(final List forceModels) { + // GIVEN + final AttitudeProvider attitudeProvider = createAttitudeProviderWithNonZeroRates(); + propagator.setAttitudeProvider(attitudeProvider); + for (final ForceModel forceModel : forceModels) { + propagator.addForceModel(forceModel); + } + // WHEN + final SpacecraftState state = propagator.propagate(propagator.getInitialState().getDate().shiftedBy(10.)); + // THEN + final Attitude attitude = state.getAttitude(); + Assertions.assertNotEquals(Vector3D.ZERO, attitude.getSpin()); + Assertions.assertNotEquals(Vector3D.ZERO, attitude.getRotationAcceleration()); + } + + private AttitudeProvider createAttitudeProviderWithNonZeroRates() { + return new AttitudeProvider() { + @Override + public Attitude getAttitude(PVCoordinatesProvider pvProv, AbsoluteDate date, Frame frame) { + return createAttitudeWithNonZeroRates(date, frame); + } + + @Override + public > FieldAttitude getAttitude(FieldPVCoordinatesProvider pvProv, FieldAbsoluteDate date, Frame frame) { + return null; + } + }; + } + + private Attitude createAttitudeWithNonZeroRates(final AbsoluteDate date, final Frame frame) { + final AngularCoordinates angularCoordinates = new AngularCoordinates(Rotation.IDENTITY, + Vector3D.PLUS_K, Vector3D.MINUS_I); + return new Attitude(date, frame, angularCoordinates); + } + @BeforeEach public void setUp() { Utils.setDataRoot("regular-data:potential/shm-format"); diff --git a/src/test/java/org/orekit/propagation/numerical/PickupHandler.java b/src/test/java/org/orekit/propagation/numerical/PickUpHandler.java similarity index 100% rename from src/test/java/org/orekit/propagation/numerical/PickupHandler.java rename to src/test/java/org/orekit/propagation/numerical/PickUpHandler.java diff --git a/src/test/java/org/orekit/propagation/numerical/StateTransitionMatrixGeneratorTest.java b/src/test/java/org/orekit/propagation/numerical/StateTransitionMatrixGeneratorTest.java index 5ca8103da0..844b2c3836 100644 --- a/src/test/java/org/orekit/propagation/numerical/StateTransitionMatrixGeneratorTest.java +++ b/src/test/java/org/orekit/propagation/numerical/StateTransitionMatrixGeneratorTest.java @@ -18,6 +18,7 @@ import static org.hamcrest.CoreMatchers.is; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -27,6 +28,7 @@ import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; import org.hipparchus.analysis.differentiation.DerivativeStructure; +import org.hipparchus.analysis.differentiation.GradientField; import org.hipparchus.exception.LocalizedCoreFormats; import org.hipparchus.geometry.euclidean.threed.FieldRotation; import org.hipparchus.geometry.euclidean.threed.FieldVector3D; @@ -40,7 +42,9 @@ 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.attitudes.Attitude; import org.orekit.attitudes.AttitudeProvider; @@ -59,11 +63,7 @@ import org.orekit.forces.maneuvers.trigger.DateBasedManeuverTriggers; import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; -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; +import org.orekit.orbits.*; import org.orekit.propagation.AdditionalStateProvider; import org.orekit.propagation.FieldSpacecraftState; import org.orekit.propagation.MatricesHarvester; @@ -84,7 +84,7 @@ import org.orekit.utils.ParameterDriver; /** Unit tests for {@link StateTransitionMatrixGenerator}. */ -public class StateTransitionMatrixGeneratorTest { +class StateTransitionMatrixGeneratorTest { @BeforeEach public void setUp() { @@ -93,7 +93,7 @@ public void setUp() { } @Test - public void testInterrupt() { + void testInterrupt() { final AbsoluteDate firing = new AbsoluteDate(new DateComponents(2004, 1, 2), new TimeComponents(4, 15, 34.080), TimeScalesFactory.getUTC()); @@ -177,7 +177,7 @@ public CombinedDerivatives combinedDerivatives(SpacecraftState s) { * check {@link StateTransitionMatrixGenerator#generate(SpacecraftState)} correctly sets the satellite velocity. */ @Test - public void testComputeDerivativesStateVelocity() { + void testComputeDerivativesStateVelocity() { //setup /** arbitrary date */ @@ -211,7 +211,7 @@ public void testComputeDerivativesStateVelocity() { } @Test - public void testPropagationTypesElliptical() { + void testPropagationTypesElliptical() { NormalizedSphericalHarmonicsProvider provider = GravityFieldFactory.getNormalizedProvider(5, 5); ForceModel gravityField = @@ -252,7 +252,7 @@ public void testPropagationTypesElliptical() { } @Test - public void testPropagationTypesHyperbolic() { + void testPropagationTypesHyperbolic() { NormalizedSphericalHarmonicsProvider provider = GravityFieldFactory.getNormalizedProvider(5, 5); ForceModel gravityField = @@ -293,7 +293,39 @@ public void testPropagationTypesHyperbolic() { } @Test - public void testAccelerationPartialNewtonOnly() { + @DisplayName("Coverage test for unlikely case where full attitude provider is needed.") + void testCombinedDerivativesWithFullAttitudeProvider() { + // GIVEN + final KeplerianOrbit orbit = + new KeplerianOrbit(8000000.0, 0.01, 0.1, 0.7, 0, 1.2, PositionAngleType.TRUE, + FramesFactory.getEME2000(), AbsoluteDate.J2000_EPOCH, Constants.EIGEN5C_EARTH_MU); + final AttitudeProvider attitudeProvider = new FrameAlignedProvider(orbit.getFrame()); + final ForceModel mockedForceModel = mockForceModelDependingOnAttitudeRate(); + final List forceModels = new ArrayList<>(); + forceModels.add(mockedForceModel); + final String name = "stm"; + final StateTransitionMatrixGenerator transitionMatrixGenerator = new StateTransitionMatrixGenerator(name, + forceModels, attitudeProvider); + SpacecraftState state = new SpacecraftState(orbit); + state = state.addAdditionalState(name, new double[36]); + // WHEN + final CombinedDerivatives combinedDerivatives = transitionMatrixGenerator.combinedDerivatives(state); + // THEN + Assertions.assertNull(combinedDerivatives.getMainStateDerivativesIncrements()); + } + + @SuppressWarnings("unchecked") + private ForceModel mockForceModelDependingOnAttitudeRate() { + final ForceModel mockedForceModel = Mockito.mock(ForceModel.class); + Mockito.when(mockedForceModel.dependsOnAttitudeRate()).thenReturn(true); + Mockito.when(mockedForceModel.dependsOnPositionOnly()).thenReturn(false); + Mockito.when(mockedForceModel.acceleration(Mockito.any(FieldSpacecraftState.class), Mockito.any())) + .thenReturn(FieldVector3D.getZero(GradientField.getField(6))); + return mockedForceModel; + } + + @Test + void testAccelerationPartialNewtonOnly() { NormalizedSphericalHarmonicsProvider provider = GravityFieldFactory.getNormalizedProvider(5, 5); ForceModel newton = new NewtonianAttraction(provider.getMu()); @@ -325,7 +357,7 @@ public void testAccelerationPartialNewtonOnly() { } @Test - public void testAccelerationPartialGravity5x5() { + void testAccelerationPartialGravity5x5() { NormalizedSphericalHarmonicsProvider provider = GravityFieldFactory.getNormalizedProvider(5, 5); ForceModel gravityField = @@ -358,7 +390,7 @@ public void testAccelerationPartialGravity5x5() { } @Test - public void testMultiSat() { + void testMultiSat() { NormalizedSphericalHarmonicsProvider provider = GravityFieldFactory.getNormalizedProvider(5, 5); Frame itrf = FramesFactory.getITRF(IERSConventions.IERS_2010, true); @@ -422,7 +454,7 @@ public void testMultiSat() { } @Test - public void testParallelStm() { + void testParallelStm() { double a = 7187990.1979844316; double e = 0.5e-4; @@ -459,7 +491,7 @@ public void testParallelStm() { } @Test - public void testNotInitialized() { + void testNotInitialized() { Orbit initialOrbit = new KeplerianOrbit(8000000.0, 0.01, 0.1, 0.7, 0, 1.2, PositionAngleType.TRUE, FramesFactory.getEME2000(), AbsoluteDate.J2000_EPOCH, @@ -476,7 +508,7 @@ public void testNotInitialized() { } @Test - public void testMismatchedDimensions() { + void testMismatchedDimensions() { Orbit initialOrbit = new KeplerianOrbit(8000000.0, 0.01, 0.1, 0.7, 0, 1.2, PositionAngleType.TRUE, FramesFactory.getEME2000(), AbsoluteDate.J2000_EPOCH, diff --git a/src/test/java/org/orekit/propagation/sampling/FieldStepHandlerMultiplexerTest.java b/src/test/java/org/orekit/propagation/sampling/FieldStepHandlerMultiplexerTest.java index f8ab02483f..34c12bd417 100644 --- a/src/test/java/org/orekit/propagation/sampling/FieldStepHandlerMultiplexerTest.java +++ b/src/test/java/org/orekit/propagation/sampling/FieldStepHandlerMultiplexerTest.java @@ -1,3 +1,20 @@ +/* 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.sampling; import org.hipparchus.Field; 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 c1b3ed5075..8e4cc05df9 100644 --- a/src/test/java/org/orekit/propagation/semianalytical/dsst/DSSTPropagatorTest.java +++ b/src/test/java/org/orekit/propagation/semianalytical/dsst/DSSTPropagatorTest.java @@ -50,6 +50,7 @@ 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.LofOffset; @@ -1487,9 +1488,8 @@ public void updateShortPeriodTerms(double[] parameters, SpacecraftState... meanStates) { } - @SafeVarargs @Override - public final > void updateShortPeriodTerms( + public > void updateShortPeriodTerms( T[] parameters, FieldSpacecraftState... meanStates) { } diff --git a/src/test/java/org/orekit/propagation/semianalytical/dsst/DSSTStateTransitionMatrixGeneratorTest.java b/src/test/java/org/orekit/propagation/semianalytical/dsst/DSSTStateTransitionMatrixGeneratorTest.java index 25749e845c..a13baf026f 100644 --- a/src/test/java/org/orekit/propagation/semianalytical/dsst/DSSTStateTransitionMatrixGeneratorTest.java +++ b/src/test/java/org/orekit/propagation/semianalytical/dsst/DSSTStateTransitionMatrixGeneratorTest.java @@ -60,7 +60,7 @@ import java.util.Collections; /** Unit tests for {@link DSSTStateTransitionMatrixGenerator}. */ -public class DSSTStateTransitionMatrixGeneratorTest { +class DSSTStateTransitionMatrixGeneratorTest { @BeforeEach public void setUp() { @@ -69,7 +69,7 @@ public void setUp() { } @Test - public void testInterrupt() { + void testInterrupt() { UnnormalizedSphericalHarmonicsProvider provider = GravityFieldFactory.getUnnormalizedProvider(5, 5); double dt = 900; @@ -142,18 +142,18 @@ public CombinedDerivatives combinedDerivatives(SpacecraftState s) { OrekitMatchers.matrixCloseTo(stm1, 2e-11 * stm1.getNorm1())); Assertions.assertEquals(0.0, stm2.subtract(stm1).getNorm1(), 2.0e-11 * stm1.getNorm1()); MatcherAssert.assertThat(jacobian2, - OrekitMatchers.matrixCloseTo(jacobian1, 4e-12)); - Assertions.assertEquals(0.0, jacobian2.subtract(jacobian1).getNorm1(), 1.0e-9 * jacobian1.getNorm1()); + OrekitMatchers.matrixCloseTo(jacobian1, 4e-11)); + Assertions.assertEquals(0.0, jacobian2.subtract(jacobian1).getNorm1(), 1.0e-8 * jacobian1.getNorm1()); } @Test - public void testPropagationTypesElliptical() throws FileNotFoundException, UnsupportedEncodingException, OrekitException { + void testPropagationTypesElliptical() throws FileNotFoundException, UnsupportedEncodingException, OrekitException { doTestPropagation(PropagationType.MEAN, 7.0e-16); } @Test - public void testPropagationTypesEllipticalWithShortPeriod() throws FileNotFoundException, UnsupportedEncodingException, OrekitException { + void testPropagationTypesEllipticalWithShortPeriod() throws FileNotFoundException, UnsupportedEncodingException, OrekitException { doTestPropagation(PropagationType.OSCULATING, 3.3e-4); } @@ -312,7 +312,7 @@ private void initializeShortPeriod(final MatricesHarvester harvester, final DSST * In OSCULATING cas, first and last lines are compared to reference values. */ @Test - public void testIssue713() { + void testIssue713() { UnnormalizedSphericalHarmonicsProvider provider = GravityFieldFactory.getUnnormalizedProvider(5, 5); double dP = 0.001; 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 eb95297aff..d0334582a5 100644 --- a/src/test/java/org/orekit/propagation/semianalytical/dsst/FieldDSSTPropagatorTest.java +++ b/src/test/java/org/orekit/propagation/semianalytical/dsst/FieldDSSTPropagatorTest.java @@ -671,7 +671,6 @@ private > void doTestStopEvent(Field field) final FieldAbsoluteDate stopDate = state.getDate().shiftedBy(1000); CheckingHandler, T> checking = new CheckingHandler, T>(Action.STOP); - @SuppressWarnings("unchecked") FieldDateDetector detector = new FieldDateDetector<>(field, stopDate).withHandler(checking); dsstPropagator.addEventDetector(detector); checking.assertEvent(false); @@ -691,7 +690,6 @@ private > void doTestContinueEvent(Field fi final FieldAbsoluteDate resetDate = state.getDate().shiftedBy(1000); CheckingHandler, T> checking = new CheckingHandler, T>(Action.CONTINUE); - @SuppressWarnings("unchecked") FieldDateDetector detector = new FieldDateDetector<>(field, resetDate).withHandler(checking); dsstPropagator.addEventDetector(detector); final double dt = 3200; diff --git a/src/test/java/org/orekit/propagation/semianalytical/dsst/FieldDSSTSolarRadiationPressureTest.java b/src/test/java/org/orekit/propagation/semianalytical/dsst/FieldDSSTSolarRadiationPressureTest.java index d7906122ac..d8db0bc011 100644 --- a/src/test/java/org/orekit/propagation/semianalytical/dsst/FieldDSSTSolarRadiationPressureTest.java +++ b/src/test/java/org/orekit/propagation/semianalytical/dsst/FieldDSSTSolarRadiationPressureTest.java @@ -81,10 +81,10 @@ import java.util.Arrays; import java.util.List; -public class FieldDSSTSolarRadiationPressureTest { +class FieldDSSTSolarRadiationPressureTest { @Test - public void testGetMeanElementRate() { + void testGetMeanElementRate() { doTestGetMeanElementRate(Binary64Field.getInstance()); } @@ -167,7 +167,7 @@ private > void doTestGetMeanElementRate(final } @Test - public void testShortPeriodTerms() { + void testShortPeriodTerms() { doTestShortPeriodTerms(Binary64Field.getInstance()); } @@ -229,17 +229,17 @@ private > void doTestShortPeriodTerms(final Fi } } - Assertions.assertEquals(0.3668654523023674, y[0].getReal(), 1.0e-16); + Assertions.assertEquals(0.3668654523023674, y[0].getReal(), 1.0e-15); Assertions.assertEquals(-2.5673332283029E-10, y[1].getReal(), 1.0e-23); Assertions.assertEquals(-3.84959877691874E-9, y[2].getReal(), 1.0e-23); Assertions.assertEquals(-3.069285299519465E-9, y[3].getReal(), 1.0e-23); Assertions.assertEquals(-4.90887054227722E-9, y[4].getReal(), 1.0e-23); - Assertions.assertEquals(-2.38549338428378E-9, y[5].getReal(), 1.0e-23); + Assertions.assertEquals(-2.38549338428378E-9, y[5].getReal(), 1.0e-20); } @Test @SuppressWarnings("unchecked") - public void testShortPeriodTermsStateDerivatives() { + void testShortPeriodTermsStateDerivatives() { // Initial spacecraft state final AbsoluteDate initDate = new AbsoluteDate(new DateComponents(2003, 05, 21), new TimeComponents(1, 0, 0.), @@ -361,12 +361,12 @@ public void testShortPeriodTermsStateDerivatives() { } @Test - public void testSRPParametersDerivatives() throws ParseException, IOException { + void testSRPParametersDerivatives() throws ParseException, IOException { doTestShortPeriodTermsParametersDerivatives(RadiationSensitive.REFLECTION_COEFFICIENT, 9.e-15); } @Test - public void testMuParametersDerivatives() throws ParseException, IOException { + void testMuParametersDerivatives() throws ParseException, IOException { doTestShortPeriodTermsParametersDerivatives(DSSTNewtonianAttraction.CENTRAL_ATTRACTION_COEFFICIENT, 5.e-11); } diff --git a/src/test/java/org/orekit/propagation/semianalytical/dsst/forces/AbstractGaussianContributionTest.java b/src/test/java/org/orekit/propagation/semianalytical/dsst/forces/AbstractGaussianContributionTest.java new file mode 100644 index 0000000000..8f26b73981 --- /dev/null +++ b/src/test/java/org/orekit/propagation/semianalytical/dsst/forces/AbstractGaussianContributionTest.java @@ -0,0 +1,145 @@ +/* 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.semianalytical.dsst.forces; + +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.hipparchus.util.MathArrays; +import org.hipparchus.util.MathUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.orekit.attitudes.FrameAlignedProvider; +import org.orekit.forces.ForceModel; +import org.orekit.frames.FramesFactory; +import org.orekit.orbits.EquinoctialOrbit; +import org.orekit.orbits.FieldEquinoctialOrbit; +import org.orekit.orbits.FieldOrbit; +import org.orekit.orbits.PositionAngleType; +import org.orekit.propagation.FieldSpacecraftState; +import org.orekit.propagation.SpacecraftState; +import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements; +import org.orekit.propagation.semianalytical.dsst.utilities.FieldAuxiliaryElements; +import org.orekit.time.AbsoluteDate; +import org.orekit.utils.Constants; +import org.orekit.utils.ParameterDriver; + +import java.util.Collections; +import java.util.List; + +class AbstractGaussianContributionTest { + + @Test + @DisplayName("Coverage test for attitude building when not depending on rate") + void testGetMeanElementRateWhenNotDependingOnAttitudeRate() { + testTemplateGetMeanElementRate(false); + } + + @Test + @DisplayName("Coverage test for attitude building when depending on rate") + void testGetMeanElementRateWhenDependingOnAttitudeRate() { + testTemplateGetMeanElementRate(true); + } + + void testTemplateGetMeanElementRate(final boolean dependsOnAttitudeRate) { + // GIVEN + final double mu = Constants.EIGEN5C_EARTH_MU; + final EquinoctialOrbit orbit = new EquinoctialOrbit(7e6, 0., 0.001, 0.01, 0., 0., PositionAngleType.ECCENTRIC, + FramesFactory.getGCRF(), AbsoluteDate.ARBITRARY_EPOCH, mu); + final ForceModel mockedForce = Mockito.mock(ForceModel.class); + Mockito.when(mockedForce.dependsOnAttitudeRate()).thenReturn(dependsOnAttitudeRate); + Mockito.when(mockedForce.acceleration(Mockito.any(SpacecraftState.class), Mockito.any())).thenReturn(Vector3D.ZERO); + final TestContribution testContribution = new TestContribution("", 1., mockedForce, + mu); + final SpacecraftState state = new SpacecraftState(orbit); + final AuxiliaryElements elements = new AuxiliaryElements(orbit, 1); + testContribution.registerAttitudeProvider(new FrameAlignedProvider(orbit.getFrame())); + // WHEN + final double[] rates = testContribution.getMeanElementRate(state, elements, new double[1]); + // THEN + Assertions.assertEquals(6, rates.length); + } + + @Test + @DisplayName("Coverage test for Field attitude building when depending on rate") + void testFieldGetMeanElementRateWhenDependingOnAttitudeRate() { + testTemplateFieldGetMeanElementRate(true); + } + + @Test + @DisplayName("Coverage test for Field attitude building when not depending on rate") + void testFieldGetMeanElementRateWhenNotDependingOnAttitudeRate() { + testTemplateFieldGetMeanElementRate(false); + } + + @SuppressWarnings("unchecked") + void testTemplateFieldGetMeanElementRate(final boolean dependsOnAttitudeRate) { + // GIVEN + final Field field = ComplexField.getInstance(); + final double mu = Constants.EIGEN5C_EARTH_MU; + final EquinoctialOrbit orbit = new EquinoctialOrbit(7e6, 0., 0.001, 0.01, 0., 0., PositionAngleType.ECCENTRIC, + FramesFactory.getGCRF(), AbsoluteDate.ARBITRARY_EPOCH, mu); + final FieldOrbit fieldOrbit = new FieldEquinoctialOrbit<>(field, orbit); + final ForceModel mockedForce = Mockito.mock(ForceModel.class); + Mockito.when(mockedForce.dependsOnAttitudeRate()).thenReturn(dependsOnAttitudeRate); + Mockito.when(mockedForce.acceleration(Mockito.any(FieldSpacecraftState.class), Mockito.any())) + .thenReturn(FieldVector3D.getZero(field)); + final TestContribution testContribution = new TestContribution("", 1., mockedForce, + mu); + final FieldAuxiliaryElements elements = new FieldAuxiliaryElements<>(fieldOrbit, 1); + testContribution.registerAttitudeProvider(new FrameAlignedProvider(orbit.getFrame())); + final Complex[] parameters = MathArrays.buildArray(field, 1); + parameters[0] = field.getOne(); + // WHEN + final Complex[] rates = testContribution.getMeanElementRate(new FieldSpacecraftState<>(fieldOrbit), elements, parameters); + // THEN + Assertions.assertEquals(6, rates.length); + } + + private static class TestContribution extends AbstractGaussianContribution { + + protected TestContribution(String coefficientsKeyPrefix, double threshold, ForceModel contribution, double mu) { + super(coefficientsKeyPrefix, threshold, contribution, mu); + } + + @Override + protected List getParametersDriversWithoutMu() { + return Collections.emptyList(); + } + + @Override + protected double[] getLLimits(SpacecraftState state, AuxiliaryElements auxiliaryElements) { + return new double[] {0., 1.}; + } + + @Override + protected > T[] getLLimits(FieldSpacecraftState state, FieldAuxiliaryElements auxiliaryElements) { + final Field field = state.getDate().getField(); + final T[] array = MathArrays.buildArray(field, 2); + array[0] = field.getZero(); + array[1] = field.getOne(); + return array; + + } + } + +} diff --git a/src/test/java/org/orekit/propagation/semianalytical/dsst/DSSTAtmosphericDragTest.java b/src/test/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTAtmosphericDragTest.java similarity index 68% rename from src/test/java/org/orekit/propagation/semianalytical/dsst/DSSTAtmosphericDragTest.java rename to src/test/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTAtmosphericDragTest.java index 5dea7e889d..68bd74d8f3 100644 --- a/src/test/java/org/orekit/propagation/semianalytical/dsst/DSSTAtmosphericDragTest.java +++ b/src/test/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTAtmosphericDragTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.orekit.propagation.semianalytical.dsst; +package org.orekit.propagation.semianalytical.dsst.forces; import java.io.IOException; import java.text.ParseException; @@ -45,14 +45,12 @@ import org.orekit.frames.LOFType; import org.orekit.models.earth.atmosphere.Atmosphere; import org.orekit.models.earth.atmosphere.HarrisPriester; +import org.orekit.models.earth.atmosphere.SimpleExponentialAtmosphere; import org.orekit.orbits.EquinoctialOrbit; 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.DSSTAtmosphericDrag; -import org.orekit.propagation.semianalytical.dsst.forces.DSSTForceModel; -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.DateComponents; @@ -62,10 +60,10 @@ import org.orekit.utils.IERSConventions; import org.orekit.utils.TimeStampedAngularCoordinates; -public class DSSTAtmosphericDragTest { +class DSSTAtmosphericDragTest { @Test - public void testGetMeanElementRate() throws IllegalArgumentException, OrekitException { + void testGetMeanElementRate() throws IllegalArgumentException, OrekitException { // Central Body geopotential 2x0 final UnnormalizedSphericalHarmonicsProvider provider = @@ -137,7 +135,7 @@ public void testGetMeanElementRate() throws IllegalArgumentException, OrekitExce } @Test - public void testShortPeriodTerms() throws IllegalArgumentException, OrekitException { + void testShortPeriodTerms() throws IllegalArgumentException, OrekitException { final AbsoluteDate initDate = new AbsoluteDate(new DateComponents(2003, 03, 21), new TimeComponents(1, 0, 0.), TimeScalesFactory.getUTC()); @@ -199,6 +197,80 @@ public void testShortPeriodTerms() throws IllegalArgumentException, OrekitExcept Assertions.assertEquals( 2.53842752377756570e-8, y[5], 1.e-23); } + @Test + public void testSetAtmosphereUpperLimit() { + + // Orbit above 1000 km altitude. + final Frame eme2000Frame = FramesFactory.getEME2000(); + final AbsoluteDate initDate = new AbsoluteDate(2003, 07, 01, 0, 0, 0, TimeScalesFactory.getUTC()); + final double mu = 3.986004415E14; + final Orbit orbit = new EquinoctialOrbit(8204535.84810944, + -0.001119677138261611, + 5.333650671984143E-4, + 0.847841707880348, + 0.7998014061193262, + 3.897842092486239, + PositionAngleType.TRUE, + eme2000Frame, + initDate, + mu); + + // Drag Force Model + final OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, + Constants.WGS84_EARTH_FLATTENING, + CelestialBodyFactory.getEarth().getBodyOrientedFrame()); + final Atmosphere atm = new SimpleExponentialAtmosphere(earth, 2.6e-10, 200000, 26000); + final double cd = 2.0; + final double area = 25.0; + DSSTAtmosphericDrag drag = new DSSTAtmosphericDrag(atm, cd, area, mu); + + // Attitude of the satellite + Rotation rotation = new Rotation(1.0, 0.0, 0.0, 0.0, false); + Vector3D rotationRate = new Vector3D(0., 0., 0.); + Vector3D rotationAcceleration = new Vector3D(0., 0., 0.); + TimeStampedAngularCoordinates orientation = new TimeStampedAngularCoordinates(initDate, rotation, rotationRate, rotationAcceleration); + final Attitude att = new Attitude(eme2000Frame, orientation); + + final SpacecraftState state = new SpacecraftState(orbit, att, 1000.0); + final AuxiliaryElements auxiliaryElements = new AuxiliaryElements(state.getOrbit(), 1); + + // Force model parameters + final double[] parameters = drag.getParameters(orbit.getDate()); + // Initialize force model + drag.initializeShortPeriodTerms(auxiliaryElements, PropagationType.MEAN, parameters); + + // Register the attitude provider to the force model + AttitudeProvider attitudeProvider = new FrameAlignedProvider(rotation); + drag.registerAttitudeProvider(attitudeProvider); + + // Check max atmosphere altitude + final double atmosphericMaxConstant = 1000000.0; //DSSTAtmosphericDrag.ATMOSPHERE_ALTITUDE_MAX + Assertions.assertEquals(atmosphericMaxConstant + Constants.WGS84_EARTH_EQUATORIAL_RADIUS, drag.getRbar(), 1e-9); + + // Compute and check that the mean element rates are zero + final double[] daidt = drag.getMeanElementRate(state, auxiliaryElements, parameters); + Assertions.assertEquals(0.0, daidt[0]); + Assertions.assertEquals(0.0, daidt[1]); + Assertions.assertEquals(0.0, daidt[2]); + Assertions.assertEquals(0.0, daidt[3]); + Assertions.assertEquals(0.0, daidt[4]); + Assertions.assertEquals(0.0, daidt[5]); + + // Increase atmosphere limit + final double expectedAtmosphereLimit = 2000000.0 + Constants.WGS84_EARTH_EQUATORIAL_RADIUS; + drag.setRbar(expectedAtmosphereLimit); + Assertions.assertEquals(expectedAtmosphereLimit, drag.getRbar()); + + // Compute and check the mean element rate + final double[] daidtNew = drag.getMeanElementRate(state, auxiliaryElements, parameters); + Assertions.assertEquals(-3.7465296003917817E-28, daidtNew[0], 1.0e-33); + Assertions.assertEquals(7.316039091705292E-36, daidtNew[1], 1.0e-41); + Assertions.assertEquals(-2.195983299144844E-36, daidtNew[2], 1.0e-41); + Assertions.assertEquals(-9.80724158695418E-37, daidtNew[3], 1.0e-42); + Assertions.assertEquals(-9.059767879911556E-37, daidtNew[4], 1.0e-42); + Assertions.assertEquals(1.4486591760431082E-38, daidtNew[5], 1.0e-43); + } + @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/DSSTNewtonianAttractionTest.java b/src/test/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTNewtonianAttractionTest.java similarity index 91% rename from src/test/java/org/orekit/propagation/semianalytical/dsst/DSSTNewtonianAttractionTest.java rename to src/test/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTNewtonianAttractionTest.java index e5bfce31ec..2c18494b5b 100644 --- a/src/test/java/org/orekit/propagation/semianalytical/dsst/DSSTNewtonianAttractionTest.java +++ b/src/test/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTNewtonianAttractionTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.orekit.propagation.semianalytical.dsst; +package org.orekit.propagation.semianalytical.dsst.forces; import org.hipparchus.util.FastMath; import org.junit.jupiter.api.Assertions; @@ -26,8 +26,6 @@ import org.orekit.orbits.EquinoctialOrbit; import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.SpacecraftState; -import org.orekit.propagation.semianalytical.dsst.forces.DSSTForceModel; -import org.orekit.propagation.semianalytical.dsst.forces.DSSTNewtonianAttraction; import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements; import org.orekit.time.AbsoluteDate; import org.orekit.time.TimeScalesFactory; @@ -36,12 +34,12 @@ import java.text.ParseException; import java.util.Arrays; -public class DSSTNewtonianAttractionTest { +class DSSTNewtonianAttractionTest { private static final double eps = 1.0e-19; @Test - public void testGetMeanElementRate() throws IllegalArgumentException { + void testGetMeanElementRate() throws IllegalArgumentException { final Frame earthFrame = FramesFactory.getEME2000(); diff --git a/src/test/java/org/orekit/propagation/semianalytical/dsst/DSSTSolarRadiationPressureTest.java b/src/test/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTSolarRadiationPressureTest.java similarity index 95% rename from src/test/java/org/orekit/propagation/semianalytical/dsst/DSSTSolarRadiationPressureTest.java rename to src/test/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTSolarRadiationPressureTest.java index 71aac982f4..418d3157ef 100644 --- a/src/test/java/org/orekit/propagation/semianalytical/dsst/DSSTSolarRadiationPressureTest.java +++ b/src/test/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTSolarRadiationPressureTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.orekit.propagation.semianalytical.dsst; +package org.orekit.propagation.semianalytical.dsst.forces; import org.hipparchus.geometry.euclidean.threed.Rotation; import org.hipparchus.geometry.euclidean.threed.RotationOrder; @@ -39,9 +39,6 @@ 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.DSSTSolarRadiationPressure; -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.DateComponents; @@ -57,10 +54,10 @@ import java.util.Arrays; import java.util.List; -public class DSSTSolarRadiationPressureTest { +class DSSTSolarRadiationPressureTest { @Test - public void testGetMeanElementRate() throws IllegalArgumentException { + void testGetMeanElementRate() throws IllegalArgumentException { final Frame earthFrame = FramesFactory.getGCRF(); final AbsoluteDate initDate = new AbsoluteDate(2003, 9, 16, 0, 0, 0, TimeScalesFactory.getUTC()); @@ -133,7 +130,7 @@ public void testGetMeanElementRate() throws IllegalArgumentException { } @Test - public void testShortPeriodTerms() throws IllegalArgumentException { + void testShortPeriodTerms() throws IllegalArgumentException { final AbsoluteDate initDate = new AbsoluteDate(new DateComponents(2003, 03, 21), new TimeComponents(1, 0, 0.), TimeScalesFactory.getUTC()); diff --git a/src/test/java/org/orekit/propagation/semianalytical/dsst/DSSTTesseralTest.java b/src/test/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTTesseralTest.java similarity index 96% rename from src/test/java/org/orekit/propagation/semianalytical/dsst/DSSTTesseralTest.java rename to src/test/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTTesseralTest.java index 03f4da09e0..d15707dbcf 100644 --- a/src/test/java/org/orekit/propagation/semianalytical/dsst/DSSTTesseralTest.java +++ b/src/test/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTTesseralTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.orekit.propagation.semianalytical.dsst; +package org.orekit.propagation.semianalytical.dsst.forces; import java.io.IOException; import java.lang.reflect.InvocationTargetException; @@ -44,18 +44,15 @@ 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 { +class DSSTTesseralTest { @Test - public void testGetMeanElementRate() { + void testGetMeanElementRate() { // Central Body geopotential 4x4 final UnnormalizedSphericalHarmonicsProvider provider = @@ -113,7 +110,7 @@ public void testGetMeanElementRate() { } @Test - public void testShortPeriodTerms() throws IllegalArgumentException { + void testShortPeriodTerms() throws IllegalArgumentException { Utils.setDataRoot("regular-data:potential/icgem-format"); GravityFieldFactory.addPotentialCoefficientsReader(new ICGEMFormatReader("^eigen-6s-truncated$", false)); @@ -162,7 +159,7 @@ public void testShortPeriodTerms() throws IllegalArgumentException { } @Test - public void testIssue625() { + void testIssue625() { // Central Body geopotential 4x4 final UnnormalizedSphericalHarmonicsProvider provider = @@ -219,7 +216,7 @@ public void testIssue625() { } @Test - public void testIssue736() { + void testIssue736() { // Central Body geopotential 4x4 final UnnormalizedSphericalHarmonicsProvider provider = @@ -265,7 +262,7 @@ public void testIssue736() { * when the order is lower or equal to 3. */ @Test - public void testIssue672() { + void testIssue672() { // GIVEN // ----- @@ -323,7 +320,7 @@ public void testIssue672() { * order of the gravity field (0 in this case). This last behavior was added for non-regression purposes. */ @Test - public void testIssue672OutOfRangeException() { + void testIssue672OutOfRangeException() { // Throwing exception // ------------------ @@ -373,7 +370,7 @@ public void testIssue672OutOfRangeException() { } @Test - public void testOutOfRangeException() { + void testOutOfRangeException() { // Central Body geopotential 1x0 final UnnormalizedSphericalHarmonicsProvider provider = GravityFieldFactory.getUnnormalizedProvider(1, 0); @@ -393,7 +390,7 @@ public void testOutOfRangeException() { } @Test - public void testGetMaxEccPow() + void testGetMaxEccPow() throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { final UnnormalizedSphericalHarmonicsProvider provider = GravityFieldFactory.getUnnormalizedProvider(4, 4);; diff --git a/src/test/java/org/orekit/propagation/semianalytical/dsst/DSSTThirdBodyTest.java b/src/test/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTThirdBodyTest.java similarity index 93% rename from src/test/java/org/orekit/propagation/semianalytical/dsst/DSSTThirdBodyTest.java rename to src/test/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTThirdBodyTest.java index 26af00b5bf..ee729ce904 100644 --- a/src/test/java/org/orekit/propagation/semianalytical/dsst/DSSTThirdBodyTest.java +++ b/src/test/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTThirdBodyTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.orekit.propagation.semianalytical.dsst; +package org.orekit.propagation.semianalytical.dsst.forces; import org.hipparchus.util.FastMath; import org.junit.jupiter.api.Assertions; @@ -29,9 +29,6 @@ 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.DSSTThirdBody; -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.DateComponents; @@ -45,12 +42,12 @@ import java.util.Collection; import java.util.List; -public class DSSTThirdBodyTest { +class DSSTThirdBodyTest { private static final double eps = 3.5e-25; @Test - public void testGetMeanElementRate() throws IllegalArgumentException { + void testGetMeanElementRate() throws IllegalArgumentException { final Frame earthFrame = FramesFactory.getEME2000(); final AbsoluteDate initDate = new AbsoluteDate(2003, 07, 01, 0, 0, 00.000, TimeScalesFactory.getUTC()); @@ -103,7 +100,7 @@ public void testGetMeanElementRate() throws IllegalArgumentException { } @Test - public void testShortPeriodTerms() throws IllegalArgumentException { + void testShortPeriodTerms() throws IllegalArgumentException { final SpacecraftState meanState = getGEOState(); final DSSTForceModel moon = new DSSTThirdBody(CelestialBodyFactory.getMoon(), meanState.getMu()); diff --git a/src/test/java/org/orekit/propagation/semianalytical/dsst/DSSTZonalTest.java b/src/test/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTZonalTest.java similarity index 95% rename from src/test/java/org/orekit/propagation/semianalytical/dsst/DSSTZonalTest.java rename to src/test/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTZonalTest.java index 7654aaff08..e05516412c 100644 --- a/src/test/java/org/orekit/propagation/semianalytical/dsst/DSSTZonalTest.java +++ b/src/test/java/org/orekit/propagation/semianalytical/dsst/forces/DSSTZonalTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.orekit.propagation.semianalytical.dsst; +package org.orekit.propagation.semianalytical.dsst.forces; import org.hipparchus.exception.LocalizedCoreFormats; import org.hipparchus.util.FastMath; @@ -33,9 +33,6 @@ 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.DSSTZonal; -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.DateComponents; @@ -48,10 +45,10 @@ import java.util.Arrays; import java.util.List; -public class DSSTZonalTest { +class DSSTZonalTest { @Test - public void testGetMeanElementRate() { + void testGetMeanElementRate() { // Central Body geopotential 4x4 final UnnormalizedSphericalHarmonicsProvider provider = @@ -107,7 +104,7 @@ public void testGetMeanElementRate() { } @Test - public void testShortPeriodTerms() { + void testShortPeriodTerms() { final SpacecraftState meanState = getGEOState(); final UnnormalizedSphericalHarmonicsProvider provider = GravityFieldFactory.getUnnormalizedProvider(2, 0); @@ -140,7 +137,7 @@ public void testShortPeriodTerms() { } @Test - public void testIssue625() { + void testIssue625() { Utils.setDataRoot("regular-data:potential/grgs-format"); GravityFieldFactory.addPotentialCoefficientsReader(new GRGSFormatReader("grim4s4_gr", true)); @@ -195,7 +192,7 @@ public void testIssue625() { } @Test - public void testOutOfRangeException() { + void testOutOfRangeException() { try { @SuppressWarnings("unused") final DSSTZonal zonal = new DSSTZonal(GravityFieldFactory.getUnnormalizedProvider(1, 0)); diff --git a/src/test/java/org/orekit/propagation/semianalytical/dsst/FieldDSSTNewtonianAttractionTest.java b/src/test/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTNewtonianAttractionTest.java similarity index 93% rename from src/test/java/org/orekit/propagation/semianalytical/dsst/FieldDSSTNewtonianAttractionTest.java rename to src/test/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTNewtonianAttractionTest.java index 54e021ad94..205893379d 100644 --- a/src/test/java/org/orekit/propagation/semianalytical/dsst/FieldDSSTNewtonianAttractionTest.java +++ b/src/test/java/org/orekit/propagation/semianalytical/dsst/forces/FieldDSSTNewtonianAttractionTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.orekit.propagation.semianalytical.dsst; +package org.orekit.propagation.semianalytical.dsst.forces; import org.hipparchus.CalculusFieldElement; import org.hipparchus.Field; @@ -30,8 +30,6 @@ import org.orekit.orbits.FieldEquinoctialOrbit; import org.orekit.orbits.PositionAngleType; import org.orekit.propagation.FieldSpacecraftState; -import org.orekit.propagation.semianalytical.dsst.forces.DSSTForceModel; -import org.orekit.propagation.semianalytical.dsst.forces.DSSTNewtonianAttraction; import org.orekit.propagation.semianalytical.dsst.utilities.FieldAuxiliaryElements; import org.orekit.time.FieldAbsoluteDate; import org.orekit.time.TimeScalesFactory; @@ -40,12 +38,12 @@ import java.text.ParseException; import java.util.Arrays; -public class FieldDSSTNewtonianAttractionTest { +class FieldDSSTNewtonianAttractionTest { private static final double eps = 1.0e-19; @Test - public void testGetMeanElementRate() { + void testGetMeanElementRate() { doTestGetMeanElementRate(Binary64Field.getInstance()); } diff --git a/src/test/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Alfriend1999MaxTest.java b/src/test/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Alfriend1999MaxTest.java index a2e0085792..2b022bec84 100644 --- a/src/test/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Alfriend1999MaxTest.java +++ b/src/test/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Alfriend1999MaxTest.java @@ -29,8 +29,10 @@ import org.hipparchus.util.Binary64; import org.hipparchus.util.Binary64Field; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.orekit.Utils; import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; import org.orekit.frames.LOFType; @@ -61,6 +63,11 @@ class Alfriend1999MaxTest { */ private final ShortTermEncounter2DPOCMethod method = new Alfriend1999Max(); + @BeforeAll + static void initializeOrekitData() { + Utils.setDataRoot("regular-data"); + } + /** * This method use the data from the appendix (p.13) of "Armellin, R. (2021). Collision Avoidance Maneuver Optimization * with a Multiple-Impulse Convex Formulation." diff --git a/src/test/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Alfriend1999Test.java b/src/test/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Alfriend1999Test.java index 4bb48ee7ad..1b1ced5116 100644 --- a/src/test/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Alfriend1999Test.java +++ b/src/test/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Alfriend1999Test.java @@ -29,8 +29,10 @@ import org.hipparchus.util.Binary64; import org.hipparchus.util.Binary64Field; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.orekit.Utils; import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; import org.orekit.frames.LOFType; @@ -61,6 +63,11 @@ class Alfriend1999Test { */ private final ShortTermEncounter2DPOCMethod method = new Alfriend1999(); + @BeforeAll + static void initializeOrekitData() { + Utils.setDataRoot("regular-data"); + } + /** * This method use the data from the appendix (p.13) of "Armellin, R. (2021). Collision Avoidance Maneuver Optimization * with a Multiple-Impulse Convex Formulation." @@ -247,8 +254,8 @@ void testReturnAcceptableStatisticsAboutMaximumProbabilityOfCollisionWithArmelli armellinDataRowList); // THEN - Assertions.assertTrue(statistics.getMean() <= 8.844620688058309E-10); - Assertions.assertTrue(statistics.getStandardDeviation() <= 3.606826996118531E-9); + Assertions.assertTrue(statistics.getMean() <= 8.8446207E-10); + Assertions.assertTrue(statistics.getStandardDeviation() <= 3.6068271E-9); } /** diff --git a/src/test/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Chan1997Test.java b/src/test/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Chan1997Test.java index 3ba1238f2f..fbbe6959f0 100644 --- a/src/test/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Chan1997Test.java +++ b/src/test/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Chan1997Test.java @@ -20,8 +20,10 @@ import org.hipparchus.analysis.differentiation.DerivativeStructure; import org.hipparchus.util.Binary64; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.orekit.Utils; import org.orekit.ssa.metrics.FieldProbabilityOfCollision; import org.orekit.ssa.metrics.ProbabilityOfCollision; @@ -32,6 +34,11 @@ class Chan1997Test { */ private final ShortTermEncounter2DPOCMethod method = new Chan1997(); + @BeforeAll + static void initializeOrekitData() { + Utils.setDataRoot("regular-data"); + } + @Test @DisplayName("Chan test case 01") void ChanTestCase01() { diff --git a/src/test/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/FieldShortTermEncounter2DDefinitionTest.java b/src/test/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/FieldShortTermEncounter2DDefinitionTest.java index 63fd778a3f..1d8cc275f9 100644 --- a/src/test/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/FieldShortTermEncounter2DDefinitionTest.java +++ b/src/test/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/FieldShortTermEncounter2DDefinitionTest.java @@ -27,9 +27,11 @@ import org.hipparchus.util.Binary64Field; import org.hipparchus.util.FastMath; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.mockito.Mockito; +import org.orekit.Utils; import org.orekit.errors.OrekitException; import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; @@ -55,6 +57,11 @@ class FieldShortTermEncounter2DDefinitionTest { */ private final double DEFAULT_ZERO_THRESHOLD = 5e-14; + @BeforeAll + static void initializeOrekitData() { + Utils.setDataRoot("regular-data"); + } + @Test @DisplayName("Test the combined radius (sum of each collision object sphere equivalent radius)") public void testGiveTheSumOfEachCollisionObjectRadius() { diff --git a/src/test/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Patera2005Test.java b/src/test/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Patera2005Test.java index 73fde269d6..04b36e7084 100644 --- a/src/test/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Patera2005Test.java +++ b/src/test/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/Patera2005Test.java @@ -899,7 +899,7 @@ void testComputeProbabilityFromACdmField() { 1e-15); // THEN - Assertions.assertEquals(0.0034965176443840836, result.getValue().getReal(), 1e-19); + Assertions.assertEquals(0.0034965176443840836, result.getValue().getReal(), 2e-18); } diff --git a/src/test/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/ShortTermEncounter2DDefinitionTest.java b/src/test/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/ShortTermEncounter2DDefinitionTest.java index befa38f3a7..d134d5ec8a 100644 --- a/src/test/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/ShortTermEncounter2DDefinitionTest.java +++ b/src/test/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/ShortTermEncounter2DDefinitionTest.java @@ -24,9 +24,11 @@ import org.hipparchus.stat.descriptive.DescriptiveStatistics; import org.hipparchus.util.FastMath; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.mockito.Mockito; +import org.orekit.Utils; import org.orekit.errors.OrekitException; import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; @@ -49,6 +51,11 @@ class ShortTermEncounter2DDefinitionTest { */ private final double DEFAULTZEROTHRESHOLD = 5e-14; + @BeforeAll + static void initializeOrekitData() { + Utils.setDataRoot("regular-data"); + } + @Test @DisplayName("Test the combined radius (sum of each collision object sphere equivalent radius)") public void testGiveTheSumOfEachCollisionObjectRadius() { diff --git a/src/test/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/ShortTermEncounter2DPOCMethodTypeTest.java b/src/test/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/ShortTermEncounter2DPOCMethodTypeTest.java index dc9eeb4702..888b0803be 100644 --- a/src/test/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/ShortTermEncounter2DPOCMethodTypeTest.java +++ b/src/test/java/org/orekit/ssa/collision/shorttermencounter/probability/twod/ShortTermEncounter2DPOCMethodTypeTest.java @@ -17,12 +17,19 @@ package org.orekit.ssa.collision.shorttermencounter.probability.twod; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.orekit.Utils; import org.orekit.files.ccsds.definitions.PocMethodType; class ShortTermEncounter2DPOCMethodTypeTest { + @BeforeAll + static void initializeOrekitData() { + Utils.setDataRoot("regular-data"); + } + @Test @DisplayName("Test Alfano2005 enum") void testReturnAlfanoMethod() { diff --git a/src/test/java/org/orekit/time/AbsoluteDateTest.java b/src/test/java/org/orekit/time/AbsoluteDateTest.java index 994937cbba..c26ee666a0 100644 --- a/src/test/java/org/orekit/time/AbsoluteDateTest.java +++ b/src/test/java/org/orekit/time/AbsoluteDateTest.java @@ -23,6 +23,7 @@ import java.text.DecimalFormatSymbols; import java.time.Instant; import java.time.LocalDateTime; +import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; @@ -30,6 +31,7 @@ import java.util.Locale; import java.util.TimeZone; +import java.util.concurrent.TimeUnit; import org.hamcrest.CoreMatchers; import org.hamcrest.MatcherAssert; import org.hipparchus.util.FastMath; @@ -308,12 +310,31 @@ public void test1970() { public void test1970Instant() { Assertions.assertEquals("1970-01-01T00:00:00.000Z", new AbsoluteDate(Instant.EPOCH, utc).toString()); Assertions.assertEquals("1970-01-01T00:00:00.000Z", new AbsoluteDate(Instant.ofEpochMilli(0l), utc).toString()); + Assertions.assertEquals("1970-01-01T00:00:00.000Z", new AbsoluteDate(Instant.EPOCH, (UTCScale) utc).toString()); + Assertions.assertEquals("1970-01-01T00:00:00.000Z", new AbsoluteDate(Instant.ofEpochMilli(0l), (UTCScale) utc).toString()); } @Test public void testInstantAccuracy() { Assertions.assertEquals("1970-01-02T00:16:40.123456789Z", new AbsoluteDate(Instant.ofEpochSecond(87400, 123456789), utc).toString()); Assertions.assertEquals("1970-01-07T00:10:00.123456789Z", new AbsoluteDate(Instant.ofEpochSecond(519000, 123456789), utc).toString()); + Assertions.assertEquals("1970-01-02T00:16:40.123456789Z", new AbsoluteDate(Instant.ofEpochSecond(87400, 123456789), (UTCScale) utc).toString()); + Assertions.assertEquals("1970-01-07T00:10:00.123456789Z", new AbsoluteDate(Instant.ofEpochSecond(519000, 123456789), (UTCScale) utc).toString()); + } + + @Test + public void testToInstant() { + Assertions.assertEquals(Instant.ofEpochSecond(0), new AbsoluteDate("1970-01-01T00:00:00.000Z", utc).toInstant()); + Assertions.assertEquals(Instant.ofEpochSecond(0), new AbsoluteDate("1970-01-01T00:00:00.000Z", utc).toInstant(TimeScalesFactory.getTimeScales())); + + Instant expectedInstant = Instant.ofEpochSecond(519000, 123456789); + Assertions.assertEquals(expectedInstant, new AbsoluteDate("1970-01-07T00:10:00.123456789Z", utc).toInstant()); + Assertions.assertEquals(expectedInstant, new AbsoluteDate("1970-01-07T00:10:00.123456789Z", utc).toInstant(TimeScalesFactory.getTimeScales())); + + Assertions.assertEquals(OffsetDateTime.parse("2024-05-15T09:32:36.123456789Z", DateTimeFormatter.ISO_DATE_TIME).toInstant(), + new AbsoluteDate("2024-05-15T09:32:36.123456789Z", utc).toInstant()); + Assertions.assertEquals(OffsetDateTime.parse("2024-05-15T09:32:36.123456789Z", DateTimeFormatter.ISO_DATE_TIME).toInstant(), + new AbsoluteDate("2024-05-15T09:32:36.123456789Z", utc).toInstant(TimeScalesFactory.getTimeScales())); } @Test @@ -357,6 +378,31 @@ public void testJDDate() { Assertions.assertEquals(0.0, AbsoluteDate.MODIFIED_JULIAN_EPOCH.durationFrom(date), 1.0e-15); } + /** Test issue 1310: get a date from a JD using a pivot timescale. */ + @Test + public void testIssue1310JDDateInTDB() { + // Given + // ----- + final TDBScale TDBscale = TimeScalesFactory.getTDB(); + final AbsoluteDate refDate = new AbsoluteDate("2023-08-01T00:00:00.000", TDBscale); + + // When + // ---- + final AbsoluteDate wrongDate = AbsoluteDate.createJDDate(2460157, + Constants.JULIAN_DAY / 2.0d, TDBscale); + final AbsoluteDate properDate = AbsoluteDate.createJDDate(2460157, + Constants.JULIAN_DAY/2.0d, TDBscale, TimeScalesFactory.getTT()); + + // Then + // ---- + + // Wrong date is too far from reference date + Assertions.assertEquals(0.0, wrongDate.durationFrom(refDate), 1.270e-05); + + // Proper date is close enough from reference date + Assertions.assertEquals(0.0, properDate.durationFrom(refDate), 2.132e-13); + } + @Test public void testOffsets() { final TimeScale tai = TimeScalesFactory.getTAI(); @@ -1406,17 +1452,17 @@ public void test_issue_943() { // Check equality is as expected for FUTURE INFINITY final AbsoluteDate date5 = AbsoluteDate.FUTURE_INFINITY; final AbsoluteDate date6 = new AbsoluteDate(AbsoluteDate.FUTURE_INFINITY, 0); - Assertions.assertEquals(date5, date6); + Assertions.assertEquals(date5, date6); // Check inequality is as expected final AbsoluteDate date7 = new AbsoluteDate(AbsoluteDate.PAST_INFINITY, 0); final AbsoluteDate date8 = new AbsoluteDate(AbsoluteDate.FUTURE_INFINITY, 0); - Assertions.assertNotEquals(date7, date8); + Assertions.assertNotEquals(date7, date8); // Check inequality is as expected final AbsoluteDate date9 = new AbsoluteDate(AbsoluteDate.ARBITRARY_EPOCH.getEpoch(), Double.POSITIVE_INFINITY); final AbsoluteDate date10 = new AbsoluteDate(AbsoluteDate.ARBITRARY_EPOCH.getEpoch(), Double.POSITIVE_INFINITY); - Assertions.assertEquals(date9, date10); + Assertions.assertEquals(date9, date10); } public void testNegativeOffsetConstructor() { @@ -1457,6 +1503,127 @@ public void testNegativeOffsetShift() { } } + @Test + public void testDurationFromWithTimeUnit() { + AbsoluteDate reference = new AbsoluteDate(2023, 1, 1, 12, 13, 59.12334567, utc); + for (TimeUnit timeUnit : TimeUnit.values()) { + Assertions.assertEquals(0, reference.durationFrom(reference, timeUnit)); + + long dayInTimeUnit = timeUnit.convert((long) Constants.JULIAN_DAY, TimeUnit.SECONDS); + for (int i = 1; i <= 365; i++) { + AbsoluteDate minusDays = reference.shiftedBy(-i * Constants.JULIAN_DAY); + AbsoluteDate plusDays = reference.shiftedBy(i* Constants.JULIAN_DAY); + + + Assertions.assertEquals(i * dayInTimeUnit, reference.durationFrom(minusDays, timeUnit)); + + Assertions.assertEquals(-i * dayInTimeUnit, reference.durationFrom(plusDays, timeUnit)); + } + + for (long ns = 1; ns <= 1_000_000_000; ns += 1_000_000) { + AbsoluteDate minus = reference.shiftedBy(-1e-9 * ns); + AbsoluteDate plus = reference.shiftedBy(1e-9 * ns); + + double deltaInTimeUnit = ns / (double) timeUnit.toNanos(1); + Assertions.assertEquals(FastMath.round(deltaInTimeUnit), reference.durationFrom(minus, timeUnit), + String.format("TimeUnit: %s, ns: %d", timeUnit, ns)); + + Assertions.assertEquals(FastMath.round(-deltaInTimeUnit), reference.durationFrom(plus, timeUnit), + String.format("TimeUnit: %s, ns: %d", timeUnit, ns)); + } + + + } + } + + @Test + public void testConstructWithTimeUnitOffset() { + AbsoluteDate reference = new AbsoluteDate(2023, 1, 1, 12, 13, 59.12334567, utc); + + for (TimeUnit timeUnit : TimeUnit.values()) { + Assertions.assertEquals(0, + FastMath.abs(reference.durationFrom(new AbsoluteDate(reference, 0, timeUnit))), 1e-10); + + long dayInTimeUnit = timeUnit.convert((long) Constants.JULIAN_DAY, TimeUnit.SECONDS); + for (int i = 1; i <= 365; i++) { + AbsoluteDate minusDays = reference.shiftedBy(-i * Constants.JULIAN_DAY); + AbsoluteDate plusDays = reference.shiftedBy(i* Constants.JULIAN_DAY); + + Assertions.assertEquals(0, + FastMath.abs(reference.durationFrom(new AbsoluteDate(minusDays, i * dayInTimeUnit, timeUnit))), + 1e-10, + String.format("TimeUnit: %s", timeUnit)); + Assertions.assertEquals(0, + FastMath.abs(reference.durationFrom(new AbsoluteDate(plusDays, -i * dayInTimeUnit, timeUnit))), + 1e-10, + String.format("TimeUnit: %s", timeUnit)); + } + + for (long ns = 1; ns <= 1_000_000_000; ns += 1_000_000) { + if (timeUnit.convert(1, TimeUnit.SECONDS) < 1) { + //Skip everything larger than one second + continue; + } + AbsoluteDate minus = reference.shiftedBy(-1e-9 * ns); + AbsoluteDate plus = reference.shiftedBy(1e-9 * ns); + + double deltaInTimeUnit = ns / (double) timeUnit.toNanos(1); + Assertions.assertEquals(0, + FastMath.abs(reference.durationFrom(new AbsoluteDate(minus, FastMath.round(deltaInTimeUnit), timeUnit))), + 1.0 / timeUnit.convert(1, TimeUnit.SECONDS), + String.format("TimeUnit: %s, ns: %d", timeUnit, ns)); + Assertions.assertEquals(0, + FastMath.abs(reference.durationFrom(new AbsoluteDate(plus, FastMath.round(-deltaInTimeUnit), timeUnit))), + 1.0 / timeUnit.convert(1, TimeUnit.SECONDS), + String.format("TimeUnit: %s, ns: %d", timeUnit, ns)); + } + } + } + + @Test + public void testShiftedByWithTimeUnit() { + AbsoluteDate reference = new AbsoluteDate(2023, 1, 1, 12, 13, 59.12334567, utc); + + for (TimeUnit timeUnit : TimeUnit.values()) { + Assertions.assertEquals(0, + FastMath.abs(reference.durationFrom(reference.shiftedBy( 0, timeUnit))), 1e-10); + + long dayInTimeUnit = timeUnit.convert((long) Constants.JULIAN_DAY, TimeUnit.SECONDS); + for (int i = 1; i <= 365; i++) { + AbsoluteDate minusDays = reference.shiftedBy(-i * Constants.JULIAN_DAY); + AbsoluteDate plusDays = reference.shiftedBy(i* Constants.JULIAN_DAY); + + Assertions.assertEquals(0, + FastMath.abs(reference.durationFrom(minusDays.shiftedBy(i * dayInTimeUnit, timeUnit))), + 1e-10, + String.format("TimeUnit: %s", timeUnit)); + Assertions.assertEquals(0, + FastMath.abs(reference.durationFrom(plusDays.shiftedBy(-i * dayInTimeUnit, timeUnit))), + 1e-10, + String.format("TimeUnit: %s", timeUnit)); + } + + for (long ns = 1; ns <= 1_000_000_000; ns += 1_000_000) { + if (timeUnit.convert(1, TimeUnit.SECONDS) < 1) { + //Skip everything larger than one second + continue; + } + AbsoluteDate minus = reference.shiftedBy(-1e-9 * ns); + AbsoluteDate plus = reference.shiftedBy(1e-9 * ns); + + double deltaInTimeUnit = ns / (double) timeUnit.toNanos(1); + Assertions.assertEquals(0, + FastMath.abs(reference.durationFrom(minus.shiftedBy(FastMath.round(deltaInTimeUnit), timeUnit))), + 1.0 / timeUnit.convert(1, TimeUnit.SECONDS), + String.format("TimeUnit: %s, ns: %d", timeUnit, ns)); + Assertions.assertEquals(0, + FastMath.abs(reference.durationFrom(plus.shiftedBy(FastMath.round(-deltaInTimeUnit), timeUnit))), + 1.0 / timeUnit.convert(1, TimeUnit.SECONDS), + String.format("TimeUnit: %s, ns: %d", timeUnit, ns)); + } + } + } + @BeforeEach public void setUp() { Utils.setDataRoot("regular-data"); diff --git a/src/test/java/org/orekit/time/ClockOffsetHermiteInterpolatorTest.java b/src/test/java/org/orekit/time/ClockOffsetHermiteInterpolatorTest.java new file mode 100644 index 0000000000..49b1ae9590 --- /dev/null +++ b/src/test/java/org/orekit/time/ClockOffsetHermiteInterpolatorTest.java @@ -0,0 +1,78 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.time; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; + +class ClockOffsetHermiteInterpolatorTest { + + @Test + void testNoRate() { + final AbsoluteDate t0 = AbsoluteDate.GALILEO_EPOCH; + final ClockOffsetHermiteInterpolator interpolator = new ClockOffsetHermiteInterpolator(4); + ClockOffset interpolated = + interpolator.interpolate(t0.shiftedBy(2.5), + Arrays.asList(new ClockOffset(t0.shiftedBy(0), 0.0, Double.NaN, Double.NaN), + new ClockOffset(t0.shiftedBy(1), 1.0, Double.NaN, Double.NaN), + new ClockOffset(t0.shiftedBy(2), 4.0, Double.NaN, Double.NaN), + new ClockOffset(t0.shiftedBy(3), 9.0, Double.NaN, Double.NaN), + new ClockOffset(t0.shiftedBy(4), 16.0, Double.NaN, Double.NaN), + new ClockOffset(t0.shiftedBy(5), 25.0, Double.NaN, Double.NaN))); + Assertions.assertEquals(6.25, interpolated.getOffset(), 1.0e-15); + Assertions.assertEquals(5.00, interpolated.getRate(), 1.0e-15); + Assertions.assertEquals(2.00, interpolated.getAcceleration(), 1.0e-15); + } + + @Test + void testNoAcceleration() { + final AbsoluteDate t0 = AbsoluteDate.GALILEO_EPOCH; + final ClockOffsetHermiteInterpolator interpolator = new ClockOffsetHermiteInterpolator(4); + ClockOffset interpolated = + interpolator.interpolate(t0.shiftedBy(2.5), + Arrays.asList(new ClockOffset(t0.shiftedBy(0), 0.0, 0.0, Double.NaN), + new ClockOffset(t0.shiftedBy(1), 1.0, 2.0, Double.NaN), + new ClockOffset(t0.shiftedBy(2), 4.0, 4.0, Double.NaN), + new ClockOffset(t0.shiftedBy(3), 9.0, 6.0, Double.NaN), + new ClockOffset(t0.shiftedBy(4), 16.0, 8.0, Double.NaN), + new ClockOffset(t0.shiftedBy(5), 25.0, 10.0, Double.NaN))); + Assertions.assertEquals(6.25, interpolated.getOffset(), 1.0e-15); + Assertions.assertEquals(5.00, interpolated.getRate(), 1.0e-15); + Assertions.assertEquals(2.00, interpolated.getAcceleration(), 1.0e-15); + } + + @Test + void testComplete() { + final AbsoluteDate t0 = AbsoluteDate.GALILEO_EPOCH; + final ClockOffsetHermiteInterpolator interpolator = new ClockOffsetHermiteInterpolator(4); + ClockOffset interpolated = + interpolator.interpolate(t0.shiftedBy(2.5), + Arrays.asList(new ClockOffset(t0.shiftedBy(0), 0.0, 0.0, 2.0), + new ClockOffset(t0.shiftedBy(1), 1.0, 2.0, 2.0), + new ClockOffset(t0.shiftedBy(2), 4.0, 4.0, 2.0), + new ClockOffset(t0.shiftedBy(3), 9.0, 6.0, 2.0), + new ClockOffset(t0.shiftedBy(4), 16.0, 8.0, 2.0), + new ClockOffset(t0.shiftedBy(5), 25.0, 10.0, 2.0))); + Assertions.assertEquals(6.25, interpolated.getOffset(), 1.0e-15); + Assertions.assertEquals(5.00, interpolated.getRate(), 1.0e-15); + Assertions.assertEquals(2.00, interpolated.getAcceleration(), 1.0e-15); + } + +} diff --git a/src/test/java/org/orekit/time/ClockTimeScaleTest.java b/src/test/java/org/orekit/time/ClockTimeScaleTest.java new file mode 100644 index 0000000000..df0c236bbc --- /dev/null +++ b/src/test/java/org/orekit/time/ClockTimeScaleTest.java @@ -0,0 +1,84 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.time; + +import org.hipparchus.util.Binary64; +import org.hipparchus.util.Binary64Field; +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.estimation.measurements.QuadraticClockModel; +import org.orekit.gnss.TimeSystem; + +public class ClockTimeScaleTest { + + @Test + public void testGalileo() { + final String name = "Galileo+offset"; + final AbsoluteDate t0 = new AbsoluteDate(2020, 4, 1, + TimeScalesFactory.getUTC()); + final QuadraticClockModel clock = new QuadraticClockModel(t0, + FastMath.scalb(1.0, -10), + FastMath.scalb(1.0, -11), + FastMath.scalb(1.0, -12)); + final ClockTimeScale positive = new ClockTimeScale(name, + TimeSystem.GALILEO.getTimeScale(TimeScalesFactory.getTimeScales()), + clock); + Assertions.assertEquals(name, positive.getName()); + Assertions.assertEquals(1.00 / 1024.0 - 19.0, positive.offsetFromTAI(t0), 1.0e-15); + Assertions.assertEquals(1.75 / 1024.0 - 19.0, positive.offsetFromTAI(t0.shiftedBy(1.0)), 1.0e-15); + Assertions.assertEquals(3.00 / 1024.0 - 19.0, positive.offsetFromTAI(t0.shiftedBy(2.0)), 1.0e-15); + + FieldAbsoluteDate t064 = new FieldAbsoluteDate<>(Binary64Field.getInstance(), t0); + Assertions.assertEquals(1.00 / 1024.0 - 19.0, positive.offsetFromTAI(t064).getReal(), 1.0e-15); + Assertions.assertEquals(1.75 / 1024.0 - 19.0, positive.offsetFromTAI(t064.shiftedBy(1.0)).getReal(), 1.0e-15); + Assertions.assertEquals(3.00 / 1024.0 - 19.0, positive.offsetFromTAI(t064.shiftedBy(2.0)).getReal(), 1.0e-15); + + } + + @Test + public void testUTC() { + final String name = "UTC+offset"; + final AbsoluteDate t0 = new AbsoluteDate(2020, 4, 1, + TimeScalesFactory.getUTC()); + final QuadraticClockModel clock = new QuadraticClockModel(t0, + FastMath.scalb(1.0, -10), + FastMath.scalb(1.0, -11), + FastMath.scalb(1.0, -12)); + final ClockTimeScale positive = new ClockTimeScale(name, + TimeScalesFactory.getUTC(), + clock); + Assertions.assertEquals(name, positive.getName()); + Assertions.assertEquals(1.00 / 1024.0 - 37.0, positive.offsetFromTAI(t0), 1.0e-15); + Assertions.assertEquals(1.75 / 1024.0 - 37.0, positive.offsetFromTAI(t0.shiftedBy(1.0)), 1.0e-15); + Assertions.assertEquals(3.00 / 1024.0 - 37.0, positive.offsetFromTAI(t0.shiftedBy(2.0)), 1.0e-15); + + FieldAbsoluteDate t064 = new FieldAbsoluteDate<>(Binary64Field.getInstance(), t0); + Assertions.assertEquals(1.00 / 1024.0 - 37.0, positive.offsetFromTAI(t064).getReal(), 1.0e-15); + Assertions.assertEquals(1.75 / 1024.0 - 37.0, positive.offsetFromTAI(t064.shiftedBy(1.0)).getReal(), 1.0e-15); + Assertions.assertEquals(3.00 / 1024.0 - 37.0, positive.offsetFromTAI(t064.shiftedBy(2.0)).getReal(), 1.0e-15); + + } + + @BeforeEach + public void setUp() throws Exception { + Utils.setDataRoot("regular-data"); + } + +} diff --git a/src/test/java/org/orekit/time/DateTimeComponentsTest.java b/src/test/java/org/orekit/time/DateTimeComponentsTest.java index 90464256c5..5da98f49e4 100644 --- a/src/test/java/org/orekit/time/DateTimeComponentsTest.java +++ b/src/test/java/org/orekit/time/DateTimeComponentsTest.java @@ -16,11 +16,13 @@ */ package org.orekit.time; +import java.util.concurrent.TimeUnit; import org.hamcrest.CoreMatchers; import org.hamcrest.MatcherAssert; import org.hipparchus.util.FastMath; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.orekit.utils.Constants; public class DateTimeComponentsTest { @@ -215,4 +217,37 @@ public void testToStringWithoutUtcOffsetRoundingUtcOffset() { MatcherAssert.assertThat(dtc.toStringWithoutUtcOffset(60, 14), CoreMatchers.is("2000-12-31T23:59:59.90000000000000")); } + @Test + public void testOffsetFromWithTimeUnit() { + DateTimeComponents reference = new DateTimeComponents(2023, 1, 1, 23, 13, 59.12334567); + for (TimeUnit timeUnit : TimeUnit.values()) { + Assertions.assertEquals(0, reference.offsetFrom(reference, timeUnit)); + + long dayInTimeUnit = timeUnit.convert((long) Constants.JULIAN_DAY, TimeUnit.SECONDS); + for (int i = 1; i <= 365; i++) { + DateTimeComponents minusDays = new DateTimeComponents(reference, -i, TimeUnit.DAYS); + DateTimeComponents plusDays = new DateTimeComponents(reference,i, TimeUnit.DAYS); + + + Assertions.assertEquals(i * dayInTimeUnit, reference.offsetFrom(minusDays, timeUnit)); + + Assertions.assertEquals(-i * dayInTimeUnit, reference.offsetFrom(plusDays, timeUnit)); + } + + for (long ns = 1; ns <= 1_000_000_000; ns += 1_000_000) { + DateTimeComponents minus = new DateTimeComponents(reference,-ns, TimeUnit.NANOSECONDS); + DateTimeComponents plus = new DateTimeComponents(reference,ns, TimeUnit.NANOSECONDS); + + double deltaInTimeUnit = ns / (double) timeUnit.toNanos(1); + Assertions.assertEquals(FastMath.round(deltaInTimeUnit), reference.offsetFrom(minus, timeUnit), + String.format("TimeUnit: %s, ns: %d", timeUnit, ns)); + + Assertions.assertEquals(FastMath.round(-deltaInTimeUnit), reference.offsetFrom(plus, timeUnit), + String.format("TimeUnit: %s, ns: %d", timeUnit, ns)); + } + + + } + } + } diff --git a/src/test/java/org/orekit/time/FieldAbsoluteDateTest.java b/src/test/java/org/orekit/time/FieldAbsoluteDateTest.java index cb323305cd..ef7c36c111 100644 --- a/src/test/java/org/orekit/time/FieldAbsoluteDateTest.java +++ b/src/test/java/org/orekit/time/FieldAbsoluteDateTest.java @@ -18,9 +18,12 @@ import java.io.IOException; import java.time.Instant; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; import java.util.Date; import java.util.TimeZone; +import java.util.concurrent.TimeUnit; import org.hamcrest.CoreMatchers; import org.hamcrest.MatcherAssert; import org.hipparchus.CalculusFieldElement; @@ -166,6 +169,12 @@ public void testInstantAccuracy() { doTestInstantAccuracy(Binary64Field.getInstance()); } + @Test + public void testToInstant() { + doTestToInstant(Binary64Field.getInstance()); + } + + @Test public void testUtcGpsOffset() { doTestUtcGpsOffset(Binary64Field.getInstance()); @@ -186,6 +195,35 @@ public void testJDDate() { doTestJDDate(Binary64Field.getInstance()); } + /** Test issue 1310: get a date from a JD using a pivot timescale. */ + @Test + public void testIssue1310JDDateInTDB() { + + // Given + // ----- + final Field field = Binary64Field.getInstance(); + + final TDBScale TDBscale = TimeScalesFactory.getTDB(); + final FieldAbsoluteDate refDate = new FieldAbsoluteDate<>(field, + new AbsoluteDate("2023-08-01T00:00:00.000", TDBscale)); + + // When + // ---- + final FieldAbsoluteDate wrongDate = FieldAbsoluteDate.createJDDate(2460157, + field.getOne().multiply(Constants.JULIAN_DAY / 2.0d), TDBscale); + final FieldAbsoluteDate properDate = FieldAbsoluteDate.createJDDate(2460157, + field.getOne().multiply(Constants.JULIAN_DAY / 2.0d), TDBscale, TimeScalesFactory.getTT()); + + // Then + // ---- + + // Wrong date is too far from reference date + Assertions.assertEquals(0.0, wrongDate.durationFrom(refDate).getReal(), 1.270e-05); + + // Proper date is close enough from reference date + Assertions.assertEquals(0.0, properDate.durationFrom(refDate).getReal(), 2.132e-13); + } + @Test public void testOffsets() { doTestOffsets(Binary64Field.getInstance()); @@ -397,7 +435,11 @@ public void testHasZeroField() { Assertions.assertFalse(gdConstantDate.shiftedBy(gdDt0).hasZeroField()); Assertions.assertFalse(gdConstantDate.shiftedBy(gdDt1).hasZeroField()); Assertions.assertFalse(gdConstantDate.shiftedBy(gdDt0).shiftedBy(gdDt1).hasZeroField()); - + + // SparseGradient + final FieldAbsoluteDate sgdDate = new FieldAbsoluteDate<>(SparseGradient.createConstant(10.).getField()); + Assertions.assertTrue(sgdDate.hasZeroField()); + // Complex // ------- @@ -431,11 +473,7 @@ public void testHasZeroField() { // FieldTuple final FieldAbsoluteDate> ftpDate = new FieldAbsoluteDate<>(new FieldTuple<>(dsDt0, dsDt1).getField()); Assertions.assertFalse(ftpDate.hasZeroField()); - - // SparseGradient - final FieldAbsoluteDate sgdDate = new FieldAbsoluteDate<>(SparseGradient.createConstant(10.).getField()); - Assertions.assertFalse(sgdDate.hasZeroField()); - + // Tuple final FieldAbsoluteDate tpDate = new FieldAbsoluteDate<>(new Tuple(0., 1., 2.).getField()); Assertions.assertFalse(tpDate.hasZeroField()); @@ -465,6 +503,27 @@ public void testHasZeroField() { Assertions.assertFalse(fu2Date.hasZeroField()); } + @Test + public void testDurationFromWithTimeUnit() { + doTestDurationFromWithTimeUnit(Binary64Field.getInstance()); + } + + @Test + public void testConstructWithTimeUnitOffset() { + doTestConstructWithTimeUnitOffset(Binary64Field.getInstance()); + } + + @Test + public void testShiftedByWithTimeUnit() { + doTestShiftedByWithTimeUnit(Binary64Field.getInstance()); + } + + @Test + public void testToStringWithoutUtcOffset() { + doTestToStringWithoutUtcOffset(Binary64Field.getInstance()); + } + + private > void doTestStandardEpoch(final Field field) { TimeScale tai = TimeScalesFactory.getTAI(); @@ -715,11 +774,30 @@ private > void doTest1970(final Field field private > void doTest1970Instant(final Field field) { Assertions.assertEquals("1970-01-01T00:00:00.000Z", new FieldAbsoluteDate<>(field, Instant.EPOCH, utc).toString()); Assertions.assertEquals("1970-01-01T00:00:00.000Z", new FieldAbsoluteDate<>(field, Instant.ofEpochMilli(0l), utc).toString()); + Assertions.assertEquals("1970-01-01T00:00:00.000Z", new FieldAbsoluteDate<>(field, Instant.EPOCH, (UTCScale) utc).toString()); + Assertions.assertEquals("1970-01-01T00:00:00.000Z", new FieldAbsoluteDate<>(field, Instant.ofEpochMilli(0l), (UTCScale) utc).toString()); } private > void doTestInstantAccuracy(final Field field) { Assertions.assertEquals("1970-01-02T00:16:40.123456789Z", new FieldAbsoluteDate<>(field, Instant.ofEpochSecond(87400, 123456789), utc).toString()); Assertions.assertEquals("1970-01-07T00:10:00.123456789Z", new FieldAbsoluteDate<>(field, Instant.ofEpochSecond(519000, 123456789), utc).toString()); + Assertions.assertEquals("1970-01-02T00:16:40.123456789Z", new FieldAbsoluteDate<>(field, Instant.ofEpochSecond(87400, 123456789), (UTCScale) utc).toString()); + Assertions.assertEquals("1970-01-07T00:10:00.123456789Z", new FieldAbsoluteDate<>(field, Instant.ofEpochSecond(519000, 123456789), (UTCScale) utc).toString()); + } + + public > void doTestToInstant(final Field field) { + Assertions.assertEquals(Instant.ofEpochSecond(0), new FieldAbsoluteDate<>(field, "1970-01-01T00:00:00.000Z", utc).toInstant()); + Assertions.assertEquals(Instant.ofEpochSecond(0), new FieldAbsoluteDate<>(field, "1970-01-01T00:00:00.000Z", utc).toInstant(TimeScalesFactory.getTimeScales())); + + Instant expectedInstant = Instant.ofEpochSecond(519000, 123456789); + Assertions.assertEquals(expectedInstant, new FieldAbsoluteDate<>(field, "1970-01-07T00:10:00.123456789Z", utc).toInstant()); + Assertions.assertEquals(expectedInstant, new FieldAbsoluteDate<>(field, "1970-01-07T00:10:00.123456789Z", utc).toInstant(TimeScalesFactory.getTimeScales())); + + Assertions.assertEquals(OffsetDateTime.parse("2024-05-15T09:32:36.123456789Z", DateTimeFormatter.ISO_DATE_TIME).toInstant(), + new FieldAbsoluteDate(field,"2024-05-15T09:32:36.123456789Z", utc).toInstant()); + Assertions.assertEquals(OffsetDateTime.parse("2024-05-15T09:32:36.123456789Z", DateTimeFormatter.ISO_DATE_TIME).toInstant(), + new FieldAbsoluteDate(field, "2024-05-15T09:32:36.123456789Z", utc).toInstant(TimeScalesFactory.getTimeScales())); + } private > void doTestUtcGpsOffset(final Field field) { @@ -1428,6 +1506,209 @@ private > void doTestNegativeOffsetShift(final } } + private > void doTestDurationFromWithTimeUnit(final Field field) { + FieldAbsoluteDate reference = new FieldAbsoluteDate<>(field, 2023, 1, 1, 12, 13, 59.12334567, utc); + for (TimeUnit timeUnit : TimeUnit.values()) { + Assertions.assertEquals(field.getZero(), reference.durationFrom(reference, timeUnit)); + + long dayInTimeUnit = timeUnit.convert((long) Constants.JULIAN_DAY, TimeUnit.SECONDS); + for (int i = 1; i <= 365; i++) { + FieldAbsoluteDate minusDays = reference.shiftedBy(-i * Constants.JULIAN_DAY); + FieldAbsoluteDate plusDays = reference.shiftedBy(i* Constants.JULIAN_DAY); + + + Assertions.assertEquals(field.getZero().add(i * dayInTimeUnit), reference.durationFrom(minusDays, timeUnit)); + + Assertions.assertEquals(field.getZero().add(-i * dayInTimeUnit), reference.durationFrom(plusDays, timeUnit)); + + AbsoluteDate minusDaysA = minusDays.toAbsoluteDate(); + AbsoluteDate plusDaysA = plusDays.toAbsoluteDate(); + + Assertions.assertEquals(field.getZero().add(i * dayInTimeUnit), reference.durationFrom(minusDaysA, timeUnit)); + + Assertions.assertEquals(field.getZero().add(-i * dayInTimeUnit), reference.durationFrom(plusDaysA, timeUnit)); + } + + for (long ns = 1; ns <= 1_000_000_000; ns += 1_000_000) { + FieldAbsoluteDate minus = reference.shiftedBy(-1e-9 * ns); + FieldAbsoluteDate plus = reference.shiftedBy(1e-9 * ns); + + double deltaInTimeUnit = ns / (double) timeUnit.toNanos(1); + Assertions.assertEquals(field.getZero().add(FastMath.round(deltaInTimeUnit)), reference.durationFrom(minus, timeUnit), + String.format("TimeUnit: %s, ns: %d", timeUnit, ns)); + + Assertions.assertEquals(field.getZero().add(FastMath.round(-deltaInTimeUnit)), reference.durationFrom(plus, timeUnit), + String.format("TimeUnit: %s, ns: %d", timeUnit, ns)); + + AbsoluteDate minusA = minus.toAbsoluteDate(); + AbsoluteDate plusA = plus.toAbsoluteDate(); + + Assertions.assertEquals(field.getZero().add(FastMath.round(deltaInTimeUnit)), reference.durationFrom(minusA, timeUnit), + String.format("TimeUnit: %s, ns: %d", timeUnit, ns)); + + Assertions.assertEquals(field.getZero().add(FastMath.round(-deltaInTimeUnit)), reference.durationFrom(plusA, timeUnit), + String.format("TimeUnit: %s, ns: %d", timeUnit, ns)); + } + + + } + } + + public > void doTestConstructWithTimeUnitOffset(final Field field) { + FieldAbsoluteDate reference = new FieldAbsoluteDate<>(field, 2023, 1, 1, 12, 13, 59.12334567, utc); + + for (TimeUnit timeUnit : TimeUnit.values()) { + Assertions.assertEquals(0, + FastMath.abs(reference.durationFrom(new FieldAbsoluteDate<>(reference, 0, timeUnit)).getReal()), 1e-10); + + long dayInTimeUnit = timeUnit.convert((long) Constants.JULIAN_DAY, TimeUnit.SECONDS); + for (int i = 1; i <= 365; i++) { + FieldAbsoluteDate minusDays = reference.shiftedBy(-i * Constants.JULIAN_DAY); + FieldAbsoluteDate plusDays = reference.shiftedBy(i* Constants.JULIAN_DAY); + + Assertions.assertEquals(0, + FastMath.abs(reference.durationFrom(new FieldAbsoluteDate<>(minusDays, i * dayInTimeUnit, timeUnit)).getReal()), + 1e-10, + String.format("TimeUnit: %s", timeUnit)); + Assertions.assertEquals(0, + FastMath.abs(reference.durationFrom(new FieldAbsoluteDate<>(plusDays, -i * dayInTimeUnit, timeUnit)).getReal()), + 1e-10, + String.format("TimeUnit: %s", timeUnit)); + + Assertions.assertEquals(0, + FastMath.abs(reference.durationFrom(new FieldAbsoluteDate<>(minusDays.toAbsoluteDate(), i * dayInTimeUnit, timeUnit, field)).getReal()), + 1e-10, + String.format("TimeUnit: %s", timeUnit)); + Assertions.assertEquals(0, + FastMath.abs(reference.durationFrom(new FieldAbsoluteDate<>(plusDays.toAbsoluteDate(), -i * dayInTimeUnit, timeUnit, field)).getReal()), + 1e-10, + String.format("TimeUnit: %s", timeUnit)); + } + + for (long ns = 1; ns <= 1_000_000_000; ns += 1_000_000) { + if (timeUnit.convert(1, TimeUnit.SECONDS) < 1) { + //Skip everything larger than one second + continue; + } + FieldAbsoluteDate minus = reference.shiftedBy(-1e-9 * ns); + FieldAbsoluteDate plus = reference.shiftedBy(1e-9 * ns); + + double deltaInTimeUnit = ns / (double) timeUnit.toNanos(1); + Assertions.assertEquals(0, + FastMath.abs(reference.durationFrom(new FieldAbsoluteDate<>(minus, FastMath.round(deltaInTimeUnit), timeUnit)).getReal()), + 1.0 / timeUnit.convert(1, TimeUnit.SECONDS), + String.format("TimeUnit: %s, ns: %d", timeUnit, ns)); + Assertions.assertEquals(0, + FastMath.abs(reference.durationFrom(new FieldAbsoluteDate<>(plus, FastMath.round(-deltaInTimeUnit), timeUnit)).getReal()), + 1.0 / timeUnit.convert(1, TimeUnit.SECONDS), + String.format("TimeUnit: %s, ns: %d", timeUnit, ns)); + + Assertions.assertEquals(0, + FastMath.abs(reference.durationFrom(new FieldAbsoluteDate<>(minus.toAbsoluteDate(), FastMath.round(deltaInTimeUnit), timeUnit, field)).getReal()), + 1.0 / timeUnit.convert(1, TimeUnit.SECONDS), + String.format("TimeUnit: %s, ns: %d", timeUnit, ns)); + Assertions.assertEquals(0, + FastMath.abs(reference.durationFrom(new FieldAbsoluteDate<>(plus.toAbsoluteDate(), FastMath.round(-deltaInTimeUnit), timeUnit, field)).getReal()), + 1.0 / timeUnit.convert(1, TimeUnit.SECONDS), + String.format("TimeUnit: %s, ns: %d", timeUnit, ns)); + } + } + } + + public > void doTestShiftedByWithTimeUnit(final Field field) { + FieldAbsoluteDate reference = new FieldAbsoluteDate<>(field, 2023, 1, 1, 12, 13, 59.12334567, utc); + + for (TimeUnit timeUnit : TimeUnit.values()) { + Assertions.assertEquals(0, + FastMath.abs(reference.durationFrom(reference.shiftedBy(0, timeUnit)).getReal()), 1e-10); + + long dayInTimeUnit = timeUnit.convert((long) Constants.JULIAN_DAY, TimeUnit.SECONDS); + for (int i = 1; i <= 365; i++) { + FieldAbsoluteDate minusDays = reference.shiftedBy(-i * Constants.JULIAN_DAY); + FieldAbsoluteDate plusDays = reference.shiftedBy(i* Constants.JULIAN_DAY); + + Assertions.assertEquals(0, + FastMath.abs(reference.durationFrom(minusDays.shiftedBy( i * dayInTimeUnit, timeUnit)).getReal()), + 1e-10, + String.format("TimeUnit: %s", timeUnit)); + Assertions.assertEquals(0, + FastMath.abs(reference.durationFrom(plusDays.shiftedBy( -i * dayInTimeUnit, timeUnit)).getReal()), + 1e-10, + String.format("TimeUnit: %s", timeUnit)); + + } + + for (long ns = 1; ns <= 1_000_000_000; ns += 1_000_000) { + if (timeUnit.convert(1, TimeUnit.SECONDS) < 1) { + //Skip everything larger than one second + continue; + } + FieldAbsoluteDate minus = reference.shiftedBy(-1e-9 * ns); + FieldAbsoluteDate plus = reference.shiftedBy(1e-9 * ns); + + double deltaInTimeUnit = ns / (double) timeUnit.toNanos(1); + Assertions.assertEquals(0, + FastMath.abs(reference.durationFrom(minus.shiftedBy(FastMath.round(deltaInTimeUnit), timeUnit)).getReal()), + 1.0 / timeUnit.convert(1, TimeUnit.SECONDS), + String.format("TimeUnit: %s, ns: %d", timeUnit, ns)); + Assertions.assertEquals(0, + FastMath.abs(reference.durationFrom(plus.shiftedBy(FastMath.round(-deltaInTimeUnit), timeUnit)).getReal()), + 1.0 / timeUnit.convert(1, TimeUnit.SECONDS), + String.format("TimeUnit: %s, ns: %d", timeUnit, ns)); + } + } + } + + public > void doTestToStringWithoutUtcOffset(final Field field) { + // setup + FieldAbsoluteDate date = new FieldAbsoluteDate(field,2009, 1, 1, utc); + double one = FastMath.nextDown(1.0); + double zeroUlp = FastMath.nextUp(0.0); + double oneUlp = FastMath.ulp(1.0); + //double sixty = FastMath.nextDown(60.0); + double sixtyUlp = FastMath.ulp(60.0); + + // action + // test midnight + checkToStringNoOffset(date, "2009-01-01T00:00:00.000"); + checkToStringNoOffset(date.shiftedBy(1), "2009-01-01T00:00:01.000"); + // test digits and rounding + checkToStringNoOffset(date.shiftedBy(12.3456789123456789), "2009-01-01T00:00:12.346"); + checkToStringNoOffset(date.shiftedBy(0.0123456789123456789), "2009-01-01T00:00:00.012"); + // test min and max values + checkToStringNoOffset(date.shiftedBy(zeroUlp), "2009-01-01T00:00:00.000"); + // Orekit 10.1 rounds up + checkToStringNoOffset(date.shiftedBy(59.0).shiftedBy(one), "2009-01-01T00:01:00.000"); + // Orekit 10.1 rounds up + checkToStringNoOffset(date.shiftedBy(86399).shiftedBy(one), "2009-01-02T00:00:00.000"); + checkToStringNoOffset(date.shiftedBy(oneUlp), "2009-01-01T00:00:00.000"); + checkToStringNoOffset(date.shiftedBy(one), "2009-01-01T00:00:01.000"); + checkToStringNoOffset(date.shiftedBy(-zeroUlp), "2009-01-01T00:00:00.000"); + // test leap + // Orekit 10.1 throw OIAE, 10.2 rounds up + checkToStringNoOffset(date.shiftedBy(-oneUlp), "2009-01-01T00:00:00.000"); + // Orekit 10.1 rounds up + checkToStringNoOffset(date.shiftedBy(-1).shiftedBy(one), "2009-01-01T00:00:00.000"); + checkToStringNoOffset(date.shiftedBy(-0.5), "2008-12-31T23:59:60.500"); + checkToStringNoOffset(date.shiftedBy(-1).shiftedBy(zeroUlp), "2008-12-31T23:59:60.000"); + checkToStringNoOffset(date.shiftedBy(-1), "2008-12-31T23:59:60.000"); + checkToStringNoOffset(date.shiftedBy(-1).shiftedBy(-zeroUlp), "2008-12-31T23:59:60.000"); + checkToStringNoOffset(date.shiftedBy(-1).shiftedBy(-oneUlp), "2008-12-31T23:59:60.000"); + checkToStringNoOffset(date.shiftedBy(-2), "2008-12-31T23:59:59.000"); + // Orekit 10.1 rounds up + checkToStringNoOffset(date.shiftedBy(-1).shiftedBy(-sixtyUlp), "2008-12-31T23:59:60.000"); + checkToStringNoOffset(date.shiftedBy(-61).shiftedBy(zeroUlp), "2008-12-31T23:59:00.000"); + checkToStringNoOffset(date.shiftedBy(-61).shiftedBy(oneUlp), "2008-12-31T23:59:00.000"); + } + + + private > void checkToStringNoOffset(final FieldAbsoluteDate d, final String s) { + MatcherAssert.assertThat(d.toStringWithoutUtcOffset(utc, 3), CoreMatchers.is(s)); + MatcherAssert.assertThat( + d.getComponents(utc).toStringWithoutUtcOffset(utc.minuteDuration(d), 3), + CoreMatchers.is(s)); + } + private > void check(FieldAbsoluteDate date, int year, int month, int day, int hour, int minute, double second, double roundTripUlps, final int secondUlps, final double absTol) { diff --git a/src/test/java/org/orekit/time/FieldClockOffsetHermiteInterpolatorTest.java b/src/test/java/org/orekit/time/FieldClockOffsetHermiteInterpolatorTest.java new file mode 100644 index 0000000000..de916960dd --- /dev/null +++ b/src/test/java/org/orekit/time/FieldClockOffsetHermiteInterpolatorTest.java @@ -0,0 +1,96 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.time; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.util.Binary64Field; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; + +class FieldClockOffsetHermiteInterpolatorTest { + + @Test + void testNoRate() { + doTestNoRate(Binary64Field.getInstance()); + } + + private > void doTestNoRate(final Field field) { + final T zero = field.getZero(); + final FieldAbsoluteDate t0 = FieldAbsoluteDate.getGalileoEpoch(field); + final FieldClockOffsetHermiteInterpolator interpolator = new FieldClockOffsetHermiteInterpolator<>(4); + FieldClockOffset interpolated = + interpolator.interpolate(t0.shiftedBy(2.5), + Arrays.asList(new FieldClockOffset<>(t0.shiftedBy(0), zero.newInstance( 0.0), zero.newInstance(Double.NaN), zero.newInstance(Double.NaN)), + new FieldClockOffset<>(t0.shiftedBy(1), zero.newInstance( 1.0), zero.newInstance(Double.NaN), zero.newInstance(Double.NaN)), + new FieldClockOffset<>(t0.shiftedBy(2), zero.newInstance( 4.0), zero.newInstance(Double.NaN), zero.newInstance(Double.NaN)), + new FieldClockOffset<>(t0.shiftedBy(3), zero.newInstance( 9.0), zero.newInstance(Double.NaN), zero.newInstance(Double.NaN)), + new FieldClockOffset<>(t0.shiftedBy(4), zero.newInstance(16.0), zero.newInstance(Double.NaN), zero.newInstance(Double.NaN)), + new FieldClockOffset<>(t0.shiftedBy(5), zero.newInstance(25.0), zero.newInstance(Double.NaN), zero.newInstance(Double.NaN)))); + Assertions.assertEquals(6.25, interpolated.getOffset().getReal(), 1.0e-15); + Assertions.assertEquals(5.00, interpolated.getRate().getReal(), 1.0e-15); + Assertions.assertEquals(2.00, interpolated.getAcceleration().getReal(), 1.0e-15); + } + + @Test + void testNoAcceleration() { + doTestNoAcceleration(Binary64Field.getInstance()); + } + + private > void doTestNoAcceleration(final Field field) { + final T zero = field.getZero(); + final FieldAbsoluteDate t0 = FieldAbsoluteDate.getGalileoEpoch(field); + final FieldClockOffsetHermiteInterpolator interpolator = new FieldClockOffsetHermiteInterpolator<>(4); + FieldClockOffset interpolated = + interpolator.interpolate(t0.shiftedBy(2.5), + Arrays.asList(new FieldClockOffset<>(t0.shiftedBy(0), zero.newInstance( 0.0), zero.newInstance( 0.0), zero.newInstance(Double.NaN)), + new FieldClockOffset<>(t0.shiftedBy(1), zero.newInstance( 1.0), zero.newInstance( 2.0), zero.newInstance(Double.NaN)), + new FieldClockOffset<>(t0.shiftedBy(2), zero.newInstance( 4.0), zero.newInstance( 4.0), zero.newInstance(Double.NaN)), + new FieldClockOffset<>(t0.shiftedBy(3), zero.newInstance( 9.0), zero.newInstance( 6.0), zero.newInstance(Double.NaN)), + new FieldClockOffset<>(t0.shiftedBy(4), zero.newInstance(16.0), zero.newInstance( 8.0), zero.newInstance(Double.NaN)), + new FieldClockOffset<>(t0.shiftedBy(5), zero.newInstance(25.0), zero.newInstance(10.0), zero.newInstance(Double.NaN)))); + Assertions.assertEquals(6.25, interpolated.getOffset().getReal(), 1.0e-15); + Assertions.assertEquals(5.00, interpolated.getRate().getReal(), 1.0e-15); + Assertions.assertEquals(2.00, interpolated.getAcceleration().getReal(), 1.0e-15); + } + + @Test + void testComplete() { + doTestComplete(Binary64Field.getInstance()); + } + + private > void doTestComplete(final Field field) { + final T zero = field.getZero(); + final FieldAbsoluteDate t0 = FieldAbsoluteDate.getGalileoEpoch(field); + final FieldClockOffsetHermiteInterpolator interpolator = new FieldClockOffsetHermiteInterpolator<>(4); + FieldClockOffset interpolated = + interpolator.interpolate(t0.shiftedBy(2.5), + Arrays.asList(new FieldClockOffset<>(t0.shiftedBy(0), zero.newInstance( 0.0), zero.newInstance( 0.0), zero.newInstance(2.0)), + new FieldClockOffset<>(t0.shiftedBy(1), zero.newInstance( 1.0), zero.newInstance( 2.0), zero.newInstance(2.0)), + new FieldClockOffset<>(t0.shiftedBy(2), zero.newInstance( 4.0), zero.newInstance( 4.0), zero.newInstance(2.0)), + new FieldClockOffset<>(t0.shiftedBy(3), zero.newInstance( 9.0), zero.newInstance( 6.0), zero.newInstance(2.0)), + new FieldClockOffset<>(t0.shiftedBy(4), zero.newInstance(16.0), zero.newInstance( 8.0), zero.newInstance(2.0)), + new FieldClockOffset<>(t0.shiftedBy(5), zero.newInstance(25.0), zero.newInstance(10.0), zero.newInstance(2.0)))); + Assertions.assertEquals(6.25, interpolated.getOffset().getReal(), 1.0e-15); + Assertions.assertEquals(5.00, interpolated.getRate().getReal(), 1.0e-15); + Assertions.assertEquals(2.00, interpolated.getAcceleration().getReal(), 1.0e-15); + } + +} diff --git a/src/test/java/org/orekit/time/GalileoScaleTest.java b/src/test/java/org/orekit/time/GalileoScaleTest.java index 8ada75657c..34ffded22a 100644 --- a/src/test/java/org/orekit/time/GalileoScaleTest.java +++ b/src/test/java/org/orekit/time/GalileoScaleTest.java @@ -16,6 +16,8 @@ */ package org.orekit.time; +import org.hipparchus.util.Binary64; +import org.hipparchus.util.Binary64Field; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -47,7 +49,7 @@ public void test2006() { public void testDuringLeap() { final TimeScale utc = TimeScalesFactory.getUTC(); final TimeScale scale = TimeScalesFactory.getGST(); - final AbsoluteDate before = new AbsoluteDate(new DateComponents(1983, 06, 30), + final AbsoluteDate before = new AbsoluteDate(new DateComponents(1983, 6, 30), new TimeComponents(23, 59, 59), utc); final AbsoluteDate during = before.shiftedBy(1.25); @@ -67,6 +69,18 @@ public void testConstant() { } } + @Test + public void testField() { + TimeScale scale = TimeScalesFactory.getGST(); + double reference = scale.offsetFromTAI(AbsoluteDate.J2000_EPOCH); + final Binary64Field field = Binary64Field.getInstance(); + for (double dt = -10000; dt < 10000; dt += 123.456789) { + FieldAbsoluteDate date = FieldAbsoluteDate.getJ2000Epoch(field). + shiftedBy(dt * Constants.JULIAN_DAY); + Assertions.assertEquals(reference, scale.offsetFromTAI(date).getReal(), 1.0e-15); + } + } + @Test public void testSameAsGPS() { TimeScale gst = TimeScalesFactory.getGST(); diff --git a/src/test/java/org/orekit/time/LazyLoadedTimeScalesTest.java b/src/test/java/org/orekit/time/LazyLoadedTimeScalesTest.java index df76b75334..44e0aa85bb 100644 --- a/src/test/java/org/orekit/time/LazyLoadedTimeScalesTest.java +++ b/src/test/java/org/orekit/time/LazyLoadedTimeScalesTest.java @@ -1,3 +1,20 @@ +/* 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.time; import static org.junit.jupiter.api.Assertions.assertNotNull; diff --git a/src/test/java/org/orekit/time/PerfectClockModelTest.java b/src/test/java/org/orekit/time/PerfectClockModelTest.java new file mode 100644 index 0000000000..33a73dedd1 --- /dev/null +++ b/src/test/java/org/orekit/time/PerfectClockModelTest.java @@ -0,0 +1,67 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.time; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.util.Binary64Field; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.orekit.Utils; + +public class PerfectClockModelTest { + + @Test + public void testZero() { + final AbsoluteDate t0 = new AbsoluteDate(2020, 4, 1, TimeScalesFactory.getUTC()); + final PerfectClockModel clockModel = new PerfectClockModel(); + for (double dt = 0.02; dt < 0.98; dt += 0.02) { + final ClockOffset co = clockModel.getOffset(t0.shiftedBy(dt)); + Assertions.assertEquals(dt, co.getDate().durationFrom(t0), 1.0e-15); + Assertions.assertEquals(0, co.getOffset(), 1.0e-15); + Assertions.assertEquals(0, co.getRate(), 1.0e-15); + Assertions.assertEquals(0, co.getAcceleration(), 1.0e-15); + } + + } + + @Test + public void testZeroField() { + doTestZero(Binary64Field.getInstance()); + } + + public > void doTestZero(final Field field) { + final AbsoluteDate t0 = new AbsoluteDate(2020, 4, 1, TimeScalesFactory.getUTC()); + final FieldAbsoluteDate t0F = new FieldAbsoluteDate<>(field, t0); + final PerfectClockModel clockModel = new PerfectClockModel(); + for (double dt = 0.02; dt < 0.98; dt += 0.02) { + final T dtF = field.getZero().newInstance(dt); + final FieldClockOffset co = clockModel.getOffset(t0F.shiftedBy(dtF)); + Assertions.assertEquals(dt, co.getDate().durationFrom(t0).getReal(), 1.0e-15); + Assertions.assertEquals(0, co.getOffset().getReal(), 1.0e-15); + Assertions.assertEquals(0, co.getRate().getReal(), 1.0e-15); + Assertions.assertEquals(0, co.getAcceleration().getReal(), 1.0e-15); + } + } + + @BeforeEach + public void setUp() throws Exception { + Utils.setDataRoot("regular-data"); + } + +} diff --git a/src/test/java/org/orekit/time/SampledClockModelTest.java b/src/test/java/org/orekit/time/SampledClockModelTest.java new file mode 100644 index 0000000000..ebcb2231ef --- /dev/null +++ b/src/test/java/org/orekit/time/SampledClockModelTest.java @@ -0,0 +1,149 @@ +/* Copyright 2002-2024 Thales Alenia Space + * 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.time; + +import org.hipparchus.CalculusFieldElement; +import org.hipparchus.Field; +import org.hipparchus.analysis.polynomials.PolynomialFunction; +import org.hipparchus.util.Binary64Field; +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 java.util.ArrayList; +import java.util.List; + +public class SampledClockModelTest { + + @Test + public void testCubicNoRate() { + final PolynomialFunction c = new PolynomialFunction(FastMath.scalb(1.0, -6), + FastMath.scalb(1.0, -7), + FastMath.scalb(1.0, -8), + FastMath.scalb(1.0, -9)); + final PolynomialFunction cDot = c.polynomialDerivative(); + final PolynomialFunction cDotDot = cDot.polynomialDerivative(); + final AbsoluteDate t0 = new AbsoluteDate(2020, 4, 1, TimeScalesFactory.getUTC()); + final List sample = new ArrayList<>(); + for (double dt = 0; dt < 10; dt += FastMath.scalb(1, -4)) { + sample.add(new ClockOffset(t0.shiftedBy(dt), c.value(dt), Double.NaN, Double.NaN)); + } + final SampledClockModel clockModel = new SampledClockModel(sample, 4); + for (double dt = 0.02; dt < 0.98; dt += 0.02) { + final ClockOffset co = clockModel.getOffset(t0.shiftedBy(dt)); + Assertions.assertEquals(dt, co.getDate().durationFrom(t0), 1.0e-15); + Assertions.assertEquals(c.value(dt), co.getOffset(), 1.0e-15); + Assertions.assertEquals(cDot.value(dt), co.getRate(), 1.0e-15); + Assertions.assertEquals(cDotDot.value(dt), co.getAcceleration(), 1.0e-15); + } + + Assertions.assertEquals(t0, clockModel.getCache().getEarliest().getDate()); + Assertions.assertEquals(t0.shiftedBy(9.9375), clockModel.getCache().getLatest().getDate()); + Assertions.assertEquals(4, clockModel.getCache().getMaxNeighborsSize()); + Assertions.assertEquals(160, clockModel.getCache().getAll().size()); + + } + + @Test + public void testCubicNoAcceleration() { + final PolynomialFunction c = new PolynomialFunction(FastMath.scalb(1.0, -6), + FastMath.scalb(1.0, -7), + FastMath.scalb(1.0, -8), + FastMath.scalb(1.0, -9)); + final PolynomialFunction cDot = c.polynomialDerivative(); + final PolynomialFunction cDotDot = cDot.polynomialDerivative(); + final AbsoluteDate t0 = new AbsoluteDate(2020, 4, 1, TimeScalesFactory.getUTC()); + final List sample = new ArrayList<>(); + for (double dt = 0; dt < 10; dt += FastMath.scalb(1, -4)) { + sample.add(new ClockOffset(t0.shiftedBy(dt), c.value(dt), cDot.value(dt), Double.NaN)); + } + final SampledClockModel clockModel = new SampledClockModel(sample, 2); + for (double dt = 0.02; dt < 0.98; dt += 0.02) { + final ClockOffset co = clockModel.getOffset(t0.shiftedBy(dt)); + Assertions.assertEquals(dt, co.getDate().durationFrom(t0), 1.0e-15); + Assertions.assertEquals(c.value(dt), co.getOffset(), 1.0e-15); + Assertions.assertEquals(cDot.value(dt), co.getRate(), 1.0e-15); + Assertions.assertEquals(cDotDot.value(dt), co.getAcceleration(), 1.0e-15); + } + } + + @Test + public void testCubicNoRateField() { + doTestCubicNoRate(Binary64Field.getInstance()); + } + + public > void doTestCubicNoRate(final Field field) { + final PolynomialFunction c = new PolynomialFunction(FastMath.scalb(1.0, -6), + FastMath.scalb(1.0, -7), + FastMath.scalb(1.0, -8), + FastMath.scalb(1.0, -9)); + final PolynomialFunction cDot = c.polynomialDerivative(); + final PolynomialFunction cDotDot = cDot.polynomialDerivative(); + final AbsoluteDate t0 = new AbsoluteDate(2020, 4, 1, TimeScalesFactory.getUTC()); + final FieldAbsoluteDate t0F = new FieldAbsoluteDate<>(field, t0); + final List sample = new ArrayList<>(); + for (double dt = 0; dt < 10; dt += FastMath.scalb(1, -4)) { + sample.add(new ClockOffset(t0.shiftedBy(dt), c.value(dt), Double.NaN, Double.NaN)); + } + final SampledClockModel clockModel = new SampledClockModel(sample, 4); + for (double dt = 0.02; dt < 0.98; dt += 0.02) { + final T dtF = field.getZero().newInstance(dt); + final FieldClockOffset co = clockModel.getOffset(t0F.shiftedBy(dtF)); + Assertions.assertEquals(dt, co.getDate().durationFrom(t0).getReal(), 1.0e-15); + Assertions.assertEquals(c.value(dtF).getReal(), co.getOffset().getReal(), 1.0e-15); + Assertions.assertEquals(cDot.value(dtF).getReal(), co.getRate().getReal(), 1.0e-15); + Assertions.assertEquals(cDotDot.value(dtF).getReal(), co.getAcceleration().getReal(), 1.0e-15); + } + } + + @Test + public void testCubicNoAccelerationField() { + doTestCubicNoAcceleration(Binary64Field.getInstance()); + } + + public > void doTestCubicNoAcceleration(final Field field) { + final PolynomialFunction c = new PolynomialFunction(FastMath.scalb(1.0, -6), + FastMath.scalb(1.0, -7), + FastMath.scalb(1.0, -8), + FastMath.scalb(1.0, -9)); + final PolynomialFunction cDot = c.polynomialDerivative(); + final PolynomialFunction cDotDot = cDot.polynomialDerivative(); + final AbsoluteDate t0 = new AbsoluteDate(2020, 4, 1, TimeScalesFactory.getUTC()); + final FieldAbsoluteDate t0F = new FieldAbsoluteDate<>(field, t0); + final List sample = new ArrayList<>(); + for (double dt = 0; dt < 10; dt += FastMath.scalb(1, -4)) { + sample.add(new ClockOffset(t0.shiftedBy(dt), c.value(dt), cDot.value(dt), Double.NaN)); + } + final SampledClockModel clockModel = new SampledClockModel(sample, 2); + for (double dt = 0.02; dt < 0.98; dt += 0.02) { + final T dtF = field.getZero().newInstance(dt); + final FieldClockOffset co = clockModel.getOffset(t0F.shiftedBy(dtF)); + Assertions.assertEquals(dt, co.getDate().durationFrom(t0).getReal(), 1.0e-15); + Assertions.assertEquals(c.value(dtF).getReal(), co.getOffset().getReal(), 1.0e-15); + Assertions.assertEquals(cDot.value(dtF).getReal(), co.getRate().getReal(), 1.0e-15); + Assertions.assertEquals(cDotDot.value(dtF).getReal(), co.getAcceleration().getReal(), 1.0e-15); + } + } + + @BeforeEach + public void setUp() throws Exception { + Utils.setDataRoot("regular-data"); + } + +} diff --git a/src/test/java/org/orekit/time/TimeStampedDoubleAndDerivativeHermiteInterpolatorTest.java b/src/test/java/org/orekit/time/TimeStampedDoubleAndDerivativeHermiteInterpolatorTest.java new file mode 100644 index 0000000000..4137b72be6 --- /dev/null +++ b/src/test/java/org/orekit/time/TimeStampedDoubleAndDerivativeHermiteInterpolatorTest.java @@ -0,0 +1,130 @@ +/* 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.time; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.RepeatedTest; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.DoubleAccumulator; + +class TimeStampedDoubleAndDerivativeAndDerivativeHermiteInterpolatorTest { + + @Test + @DisplayName("Test default constructor") + void testDefaultConstructor() { + // When + final TimeStampedDoubleAndDerivativeHermiteInterpolator interpolator = new TimeStampedDoubleAndDerivativeHermiteInterpolator(); + + // Then + Assertions.assertEquals(AbstractTimeInterpolator.DEFAULT_INTERPOLATION_POINTS, + interpolator.getNbInterpolationPoints()); + Assertions.assertEquals(AbstractTimeInterpolator.DEFAULT_EXTRAPOLATION_THRESHOLD_SEC, + interpolator.getExtrapolationThreshold()); + } + + @RepeatedTest(10) + @DisplayName("test interpolator in multi-threaded environment") + void testIssue1164() throws InterruptedException { + // GIVEN + // Create interpolator + final TimeInterpolator interpolator = new TimeStampedDoubleAndDerivativeHermiteInterpolator(); + + // Create sample and interpolation dates + final int sampleSize = 100; + final AbsoluteDate initialDate = new AbsoluteDate(); + final List sample = new ArrayList<>(); + final List dates = new ArrayList<>(); + for (int i = 1; i < sampleSize + 1; i++) { + sample.add(new TimeStampedDoubleAndDerivative(i * i, i / 30.0, + initialDate.shiftedBy(i * 60))); + dates.add(initialDate.shiftedBy(i * 60)); + } + + // Create multithreading environment + ExecutorService service = Executors.newFixedThreadPool(sampleSize); + + final DoubleAccumulator sum0 = new DoubleAccumulator(Double::sum, 0.0); + final DoubleAccumulator sum1 = new DoubleAccumulator(Double::sum, 0.0); + final List> tasks = new ArrayList<>(); + for (final AbsoluteDate date : dates) { + tasks.add(new ParallelTask(interpolator, sum0, sum1, sample, date)); + } + + // WHEN + service.invokeAll(tasks); + + // THEN + // Sum of 1*1 + 2*2 + 3*3 + ... + // Sum of 1 / 30 + 2 / 30 + ... + final double expectedSum0 = sampleSize * (sampleSize + 1) * (2 * sampleSize + 1) / 6.0; + final double expectedSum1 = sampleSize * (sampleSize + 1) / 60.0; + try { + // wait for proper ending + service.shutdown(); + Assertions.assertTrue(service.awaitTermination(5, TimeUnit.SECONDS)); + } catch (InterruptedException ie) { + // Restore interrupted state... + Thread.currentThread().interrupt(); + } + Assertions.assertEquals(expectedSum0, sum0.get(), 1.0e-12); + Assertions.assertEquals(expectedSum1, sum1.get(), 1.0e-12); + } + + /** Custom class for multi threading testing purpose */ + private static class ParallelTask implements Callable { + + private final TimeInterpolator interpolator; + + private final List sample; + + private final DoubleAccumulator sum0; + + private final DoubleAccumulator sum1; + + private final AbsoluteDate interpolationDate; + + private ParallelTask(final TimeInterpolator interpolator, + final DoubleAccumulator sum0, final DoubleAccumulator sum1, + final List sample, + final AbsoluteDate interpolationDate) { + // Store interpolator + this.interpolator = interpolator; + this.sum0 = sum0; + this.sum1 = sum1; + this.interpolationDate = interpolationDate; + this.sample = sample; + } + + @Override + public Integer call() { + // Add result to sums + final TimeStampedDoubleAndDerivative interpolated = interpolator.interpolate(interpolationDate, sample); + sum0.accumulate(interpolated.getValue()); + sum1.accumulate(interpolated.getDerivative()); + return 1; + } + } +} diff --git a/src/test/java/org/orekit/time/TimeStampedDoubleHermiteInterpolatorTest.java b/src/test/java/org/orekit/time/TimeStampedDoubleHermiteInterpolatorTest.java index 0cd89665fc..0069efecb8 100644 --- a/src/test/java/org/orekit/time/TimeStampedDoubleHermiteInterpolatorTest.java +++ b/src/test/java/org/orekit/time/TimeStampedDoubleHermiteInterpolatorTest.java @@ -1,3 +1,20 @@ +/* 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.time; import org.junit.jupiter.api.Assertions; diff --git a/src/test/java/org/orekit/time/UTCScaleTest.java b/src/test/java/org/orekit/time/UTCScaleTest.java index f6acc45c6c..0e78fab147 100644 --- a/src/test/java/org/orekit/time/UTCScaleTest.java +++ b/src/test/java/org/orekit/time/UTCScaleTest.java @@ -59,8 +59,8 @@ public void testNoLeap() { AbsoluteDate d1 = new AbsoluteDate(new DateComponents(1999, 12, 31), new TimeComponents(23, 59, 59), utc); - AbsoluteDate d2 = new AbsoluteDate(new DateComponents(2000, 01, 01), - new TimeComponents(00, 00, 01), + AbsoluteDate d2 = new AbsoluteDate(new DateComponents(2000, 1, 1), + new TimeComponents(0, 0, 1), utc); Assertions.assertEquals(2.0, d2.durationFrom(d1), 1.0e-10); } @@ -68,7 +68,7 @@ public void testNoLeap() { @Test public void testLeap2006() { AbsoluteDate leapDate = - new AbsoluteDate(new DateComponents(2006, 01, 01), TimeComponents.H00, utc); + new AbsoluteDate(new DateComponents(2006, 1, 1), TimeComponents.H00, utc); AbsoluteDate d1 = leapDate.shiftedBy(-1); AbsoluteDate d2 = leapDate.shiftedBy(+1); Assertions.assertEquals(2.0, d2.durationFrom(d1), 1.0e-10); @@ -76,15 +76,15 @@ public void testLeap2006() { AbsoluteDate d3 = new AbsoluteDate(new DateComponents(2005, 12, 31), new TimeComponents(23, 59, 59), utc); - AbsoluteDate d4 = new AbsoluteDate(new DateComponents(2006, 01, 01), - new TimeComponents(00, 00, 01), + AbsoluteDate d4 = new AbsoluteDate(new DateComponents(2006, 1, 1), + new TimeComponents(0, 0, 1), utc); Assertions.assertEquals(3.0, d4.durationFrom(d3), 1.0e-10); } @Test public void testDuringLeap() { - AbsoluteDate d = new AbsoluteDate(new DateComponents(1983, 06, 30), + AbsoluteDate d = new AbsoluteDate(new DateComponents(1983, 6, 30), new TimeComponents(23, 59, 59), utc); Assertions.assertEquals("1983-06-30T23:58:59.000", d.shiftedBy(-60).toString(utc)); @@ -330,9 +330,9 @@ public void testDisplayDuringLeap() { public void testMultithreading() { // generate reference offsets using a single thread - RandomGenerator random = new Well1024a(6392073424l); - List datesList = new ArrayList(); - List offsetsList = new ArrayList(); + RandomGenerator random = new Well1024a(6392073424L); + List datesList = new ArrayList<>(); + List offsetsList = new ArrayList<>(); AbsoluteDate reference = utc.getFirstKnownLeapSecond().shiftedBy(-Constants.JULIAN_YEAR); double testRange = utc.getLastKnownLeapSecond().durationFrom(reference) + Constants.JULIAN_YEAR; for (int i = 0; i < 10000; ++i) { @@ -347,16 +347,12 @@ public void testMultithreading() { for (int i = 0; i < datesList.size(); ++i) { final AbsoluteDate date = datesList.get(i); final double offset = offsetsList.get(i); - executorService.execute(new Runnable() { - public void run() { - Assertions.assertEquals(offset, utc.offsetFromTAI(date), 1.0e-12); - } - }); + executorService.execute(() -> Assertions.assertEquals(offset, utc.offsetFromTAI(date), 1.0e-12)); } try { executorService.shutdown(); - executorService.awaitTermination(3, TimeUnit.SECONDS); + Assertions.assertTrue(executorService.awaitTermination(3, TimeUnit.SECONDS)); } catch (InterruptedException ie) { Assertions.fail(ie.getLocalizedMessage()); } @@ -383,11 +379,7 @@ public void testOffsetToTAIBeforeFirstLeapSecond() { public void testEmptyOffsets() { Utils.setDataRoot("no-data"); - TimeScalesFactory.addUTCTAIOffsetsLoader(new UTCTAIOffsetsLoader() { - public List loadOffsets() { - return Collections.emptyList(); - } - }); + TimeScalesFactory.addUTCTAIOffsetsLoader(Collections::emptyList); try { TimeScalesFactory.getUTC(); @@ -428,8 +420,8 @@ public void testSerialization() throws IOException, ClassNotFoundException { ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(utc); - Assertions.assertTrue(bos.size() > 1550); - Assertions.assertTrue(bos.size() < 1650); + Assertions.assertTrue(bos.size() > 1700); + Assertions.assertTrue(bos.size() < 1800); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); diff --git a/src/test/java/org/orekit/utils/ExtendedPositionProviderTest.java b/src/test/java/org/orekit/utils/ExtendedPositionProviderTest.java new file mode 100644 index 0000000000..ef09b7b641 --- /dev/null +++ b/src/test/java/org/orekit/utils/ExtendedPositionProviderTest.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.utils; + +import org.hipparchus.CalculusFieldElement; +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.time.AbsoluteDate; +import org.orekit.time.FieldAbsoluteDate; + +class ExtendedPositionProviderTest { + + @Test + void testGetPVCoordinates() { + // GIVEN + final TestExtendedPositionProvider positionProvider = new TestExtendedPositionProvider(); + final Frame frame = Mockito.mock(Frame.class); + final AbsoluteDate date = AbsoluteDate.J2000_EPOCH; + // WHEN + final TimeStampedPVCoordinates pvCoordinates = positionProvider.getPVCoordinates(date, frame); + // THEN + final Vector3D expectedPosition = positionProvider.getPosition(date, frame); + Assertions.assertEquals(expectedPosition, pvCoordinates.getPosition()); + Assertions.assertEquals(1., pvCoordinates.getVelocity().getX()); + Assertions.assertEquals(0., pvCoordinates.getAcceleration().getNorm()); + } + + @Test + void testGetPVCoordinatesField() { + // GIVEN + final TestExtendedPositionProvider positionProvider = new TestExtendedPositionProvider(); + final Frame frame = Mockito.mock(Frame.class); + final FieldAbsoluteDate date = FieldAbsoluteDate.getJ2000Epoch(ComplexField.getInstance()); + // WHEN + final TimeStampedFieldPVCoordinates pvCoordinates = positionProvider.getPVCoordinates(date, frame); + // THEN + final FieldVector3D expectedPosition = positionProvider.getPosition(date, frame); + Assertions.assertEquals(expectedPosition, pvCoordinates.getPosition()); + Assertions.assertEquals(1., pvCoordinates.getVelocity().getX().getReal()); + Assertions.assertEquals(0., pvCoordinates.getAcceleration().getNorm().getReal()); + } + + @Test + void testToFieldPVCoordinatesProvider() { + // GIVEN + final TestExtendedPositionProvider positionProvider = new TestExtendedPositionProvider(); + final Frame frame = Mockito.mock(Frame.class); + final FieldAbsoluteDate date = FieldAbsoluteDate.getJ2000Epoch(ComplexField.getInstance()); + // WHEN + final FieldPVCoordinatesProvider fieldPVCoordinatesProvider = positionProvider + .toFieldPVCoordinatesProvider(ComplexField.getInstance()); + // THEN + final FieldVector3D expectedPosition = positionProvider.getPosition(date, frame); + final FieldVector3D actualPosition = fieldPVCoordinatesProvider.getPosition(date, frame); + Assertions.assertEquals(expectedPosition, actualPosition); + final FieldPVCoordinates expectedPV = positionProvider.getPVCoordinates(date, frame); + final FieldPVCoordinates actualPV = fieldPVCoordinatesProvider.getPVCoordinates(date, frame); + Assertions.assertEquals(expectedPV.getPosition(), actualPV.getPosition()); + Assertions.assertEquals(expectedPV.getVelocity(), actualPV.getVelocity()); + Assertions.assertEquals(expectedPV.getAcceleration(), actualPV.getAcceleration()); + } + + private static class TestExtendedPositionProvider implements ExtendedPositionProvider { + + private final AbsoluteDate referenceDate = AbsoluteDate.ARBITRARY_EPOCH; + + @Override + public Vector3D getPosition(AbsoluteDate date, Frame frame) { + final double shift = date.durationFrom(referenceDate); + return new Vector3D(1. + shift, 2., 3.); + } + + @Override + public > FieldVector3D getPosition(FieldAbsoluteDate date, Frame frame) { + final T zero = date.getField().getZero(); + final T shift = date.durationFrom(referenceDate); + return new FieldVector3D<>(zero.newInstance(1.).add(shift), zero.newInstance(2.), zero.newInstance(3.)); + } + } + +} diff --git a/src/test/java/org/orekit/utils/FieldAbsolutePVCoordinatesHermiteInterpolatorTest.java b/src/test/java/org/orekit/utils/FieldAbsolutePVCoordinatesHermiteInterpolatorTest.java index 0a35645f54..31eb93bcdf 100644 --- a/src/test/java/org/orekit/utils/FieldAbsolutePVCoordinatesHermiteInterpolatorTest.java +++ b/src/test/java/org/orekit/utils/FieldAbsolutePVCoordinatesHermiteInterpolatorTest.java @@ -1,3 +1,20 @@ +/* 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.utils; import org.hipparchus.Field; diff --git a/src/test/java/org/orekit/utils/FieldSortedListTrimmerTest.java b/src/test/java/org/orekit/utils/FieldSortedListTrimmerTest.java new file mode 100644 index 0000000000..140b8e4259 --- /dev/null +++ b/src/test/java/org/orekit/utils/FieldSortedListTrimmerTest.java @@ -0,0 +1,207 @@ +/* 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.utils; + +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.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.orekit.Utils; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.errors.TimeStampedCacheException; +import org.orekit.time.FieldAbsoluteDate; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * Unit tests for {@link FieldSortedListTrimmer}. + * + * @author Evan Ward + */ +public class FieldSortedListTrimmerTest { + + /** + * Binary64 field. + */ + private static final Field field = Binary64Field.getInstance(); + + /** + * arbitrary date + */ + private static final FieldAbsoluteDate date = FieldAbsoluteDate.getCCSDSEpoch(field); + + /** + * data provided to {@link #trimmer} + */ + private List> data; + + /** + * subject under test + */ + private FieldSortedListTrimmer trimmer; + + /** + * set Orekit data for useful debugging messages from dates. + */ + @BeforeAll + public static void setUpBefore() { + Utils.setDataRoot("regular-data"); + } + + /** + * create {@link #trimmer} and {@link #data} with neighborsSize = 3 + */ + @BeforeEach + public void setUp() { + data = Arrays.asList(date, date.shiftedBy(1), date.shiftedBy(2), + date.shiftedBy(3), date.shiftedBy(4), + date.shiftedBy(5)); + trimmer = new FieldSortedListTrimmer(3); + } + + /** + * check {@link FieldSortedListTrimmer#getNeighborsSubList(FieldAbsoluteDate, List)} + */ + @Test + public void testGetNeighborsSubList() { + // exception for neighborsSize > data.size() + try { + new FieldSortedListTrimmer(data.size() + 1).getNeighborsSubList(date, data); + Assertions.fail("Expected Exception"); + } catch (OrekitException e) { + Assertions.assertEquals(OrekitMessages.NOT_ENOUGH_DATA, e.getSpecifier()); + } + + // exception for non-positive neighborsSize + try { + new FieldSortedListTrimmer(0); + Assertions.fail("Expected Exception"); + } catch (IllegalArgumentException e) { + // expected + } + + // exception for null data + try { + new FieldSortedListTrimmer(1).getNeighborsSubList(date, null); + Assertions.fail("Expected Exception"); + } catch (NullPointerException e) { + // expected + } + + // exception for zero data + try { + new FieldSortedListTrimmer(1).getNeighborsSubList(date, Collections.emptyList()); + Assertions.fail("Expected Exception"); + } catch (OrekitException e) { + Assertions.assertEquals(OrekitMessages.NOT_ENOUGH_DATA, e.getSpecifier()); + } + } + + /** + * check {@link FieldSortedListTrimmer#getNeighborsSubList(FieldAbsoluteDate, List)} + * at a series of different dates designed to test all logic paths. + */ + @Test + public void testgetNeighborsSubList() { + // setup + int size = data.size(); + + // actions + verify + + // before fist data + try { + trimmer.getNeighborsSubList(data.get(0).shiftedBy(-1), data); + Assertions.fail("Expected Exception"); + } + catch (TimeStampedCacheException e) { + // expected + Assertions.assertEquals(OrekitMessages.UNABLE_TO_GENERATE_NEW_DATA_BEFORE, e.getSpecifier()); + } + + // on fist date + Assertions.assertArrayEquals(trimmer.getNeighborsSubList(data.get(0), data).toArray(), + data.subList(0, 3).toArray()); + // between fist and second date + Assertions.assertArrayEquals(trimmer.getNeighborsSubList(data.get(0).shiftedBy(0.5), data).toArray(), + data.subList(0, 3).toArray()); + // in the middle on a date + Assertions.assertArrayEquals(trimmer.getNeighborsSubList(data.get(2), data).toArray(), + data.subList(1, 4).toArray()); + // in the middle between dates + Assertions.assertArrayEquals(trimmer.getNeighborsSubList(data.get(2).shiftedBy(0.5), data).toArray(), + data.subList(1, 4).toArray()); + // just before last date + Assertions.assertArrayEquals(trimmer.getNeighborsSubList(data.get(size - 1).shiftedBy(-0.5), data).toArray(), + data.subList(size - 3, size).toArray()); + // on last date + Assertions.assertArrayEquals(trimmer.getNeighborsSubList(data.get(size - 1), data).toArray(), + data.subList(size - 3, size).toArray()); + + // after last date + FieldAbsoluteDate central = data.get(size - 1).shiftedBy(1); + try { + trimmer.getNeighborsSubList(central, data); + Assertions.fail("Expected Exception"); + } + catch (TimeStampedCacheException e) { + // expected + Assertions.assertEquals(OrekitMessages.UNABLE_TO_GENERATE_NEW_DATA_AFTER, e.getSpecifier()); + } + } + + /** + * check {@link FieldSortedListTrimmer#getNeighborsSize()} + */ + @Test + public void testGetNeighborsSize() { + Assertions.assertEquals(trimmer.getNeighborsSize(), 3); + } + + @Test + public void testNonLinear() { + final List> nonLinearCache = Arrays.asList(date.shiftedBy(10), + date.shiftedBy(14), + date.shiftedBy(18), + date.shiftedBy(23), + date.shiftedBy(30), + date.shiftedBy(36), + date.shiftedBy(45), + date.shiftedBy(55), + date.shiftedBy(67), + date.shiftedBy(90), + date.shiftedBy(118)); + for (double dt = 10; dt < 118; dt += 0.01) { + checkNeighbors(new FieldSortedListTrimmer(2), nonLinearCache, dt); + } + } + + private void checkNeighbors(final FieldSortedListTrimmer nonLinearTrimmer, + final List> nonLinearCache, + final double offset) { + List> s = nonLinearTrimmer.getNeighborsSubList(date.shiftedBy(offset), nonLinearCache); + Assertions.assertEquals(2, s.size()); + Assertions.assertTrue(s.get(0).durationFrom(date).getReal() <= offset); + Assertions.assertTrue(s.get(1).durationFrom(date).getReal() > offset); + } + +} diff --git a/src/test/java/org/orekit/utils/FieldifierTest.java b/src/test/java/org/orekit/utils/FieldifierTest.java index b4f99ffcfd..909726fd8c 100644 --- a/src/test/java/org/orekit/utils/FieldifierTest.java +++ b/src/test/java/org/orekit/utils/FieldifierTest.java @@ -1,3 +1,20 @@ +/* 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.utils; import org.hipparchus.Field; @@ -34,6 +51,7 @@ static void setUp() { } @Test + @Deprecated void testCircularOrbitFieldification() { // GIVEN // Create fake orbit with derivatives @@ -69,10 +87,11 @@ void testCircularOrbitFieldification() { Assertions.assertEquals(initialOrbit.getCircularEyDot(), fieldOrbit.getCircularEyDot().getReal()); Assertions.assertEquals(initialOrbit.getIDot(), fieldOrbit.getIDot().getReal()); Assertions.assertEquals(initialOrbit.getRightAscensionOfAscendingNodeDot(), fieldOrbit.getRightAscensionOfAscendingNodeDot().getReal()); - Assertions.assertEquals(initialOrbit.getAlphaMDot(), fieldOrbit.getAlphaMDot().getReal()); + Assertions.assertEquals(initialOrbit.getAlphaMDot(), fieldOrbit.getAlphaMDot().getReal(), 1e-10); } @Test + @Deprecated void testCartesianOrbitFieldification() { // GIVEN // Create fake orbit with derivatives @@ -110,6 +129,7 @@ void testCartesianOrbitFieldification() { } @Test + @Deprecated void testKeplerianOrbitFieldification() { // GIVEN // Create fake orbit with derivatives @@ -145,10 +165,11 @@ void testKeplerianOrbitFieldification() { Assertions.assertEquals(initialOrbit.getIDot(), fieldOrbit.getIDot().getReal()); Assertions.assertEquals(initialOrbit.getPerigeeArgumentDot(), fieldOrbit.getPerigeeArgumentDot().getReal()); Assertions.assertEquals(initialOrbit.getRightAscensionOfAscendingNodeDot(), fieldOrbit.getRightAscensionOfAscendingNodeDot().getReal()); - Assertions.assertEquals(initialOrbit.getMeanAnomalyDot(), fieldOrbit.getMeanAnomalyDot().getReal()); + Assertions.assertEquals(initialOrbit.getMeanAnomalyDot(), fieldOrbit.getMeanAnomalyDot().getReal(), 1e-10); } @Test + @Deprecated void testEquinoctialOrbitFieldification() { // GIVEN // Create fake orbit with derivatives diff --git a/src/test/java/org/orekit/utils/ImmutableFieldTimeStampedCacheTest.java b/src/test/java/org/orekit/utils/ImmutableFieldTimeStampedCacheTest.java index 8e62cf2ebd..5ebbf94cbd 100644 --- a/src/test/java/org/orekit/utils/ImmutableFieldTimeStampedCacheTest.java +++ b/src/test/java/org/orekit/utils/ImmutableFieldTimeStampedCacheTest.java @@ -177,11 +177,11 @@ public void testGetNeighbors() { } /** - * check {@link ImmutableFieldTimeStampedCache#getNeighborsSize()} + * check {@link ImmutableFieldTimeStampedCache#getMaxNeighborsSize()} */ @Test public void testGetNeighborsSize() { - Assertions.assertEquals(cache.getNeighborsSize(), 3); + Assertions.assertEquals(cache.getMaxNeighborsSize(), 3); } /** @@ -241,8 +241,9 @@ public void testImmutable() { /** * check {@link ImmutableFieldTimeStampedCache#emptyCache()}. */ + @Deprecated @Test - public void testEmptyCache() { + public void testEmptyCacheDeprecated() { // setup cache = ImmutableFieldTimeStampedCache.emptyCache(Binary64Field.getInstance()); @@ -269,7 +270,41 @@ public void testEmptyCache() { // expected } Assertions.assertEquals(cache.getAll().size(), 0); - Assertions.assertEquals(cache.getNeighborsSize(), 0); + Assertions.assertEquals(cache.getMaxNeighborsSize(), 0); + } + + /** + * check {@link ImmutableFieldTimeStampedCache#emptyCache()}. + */ + @Test + public void testEmptyCache() { + // setup + cache = ImmutableFieldTimeStampedCache.emptyCache(); + + // actions + verify + try { + cache.getNeighbors(date); + Assertions.fail("Expected Exception"); + } + catch (TimeStampedCacheException e) { + // expected + } + try { + cache.getEarliest(); + Assertions.fail("Expected Exception"); + } + catch (IllegalStateException e) { + // expected + } + try { + cache.getLatest(); + Assertions.fail("Expected Exception"); + } + catch (IllegalStateException e) { + // expected + } + Assertions.assertEquals(cache.getAll().size(), 0); + Assertions.assertEquals(cache.getMaxNeighborsSize(), 0); } @Test diff --git a/src/test/java/org/orekit/utils/ParameterDriversListTest.java b/src/test/java/org/orekit/utils/ParameterDriversListTest.java index 429a9bee68..b2b96b367d 100644 --- a/src/test/java/org/orekit/utils/ParameterDriversListTest.java +++ b/src/test/java/org/orekit/utils/ParameterDriversListTest.java @@ -203,7 +203,7 @@ public void testMerge() { ParameterDriver qB1 = new ParameterDriver("q", 0.0, 1.0, -1.0, +1.0); final AtomicBoolean called = new AtomicBoolean(false); qB1.addObserver(new ParameterObserver() { - /** {@inheridDoc} */ + /** {@inheritDoc} */ @Override public void valueChanged(final double previousValue, final ParameterDriver driver, final AbsoluteDate date) { called.set(true); @@ -277,4 +277,55 @@ public void valueSpanMapChanged(final TimeSpanMap previousValueSpanMap, } + @Test + public void testAddSameDriver() { + ParameterDriver p = new ParameterDriver("p", 0.0, 1.0, -1.0, +1.0); + ParameterDriver q = new ParameterDriver("q", 0.0, 1.0, -1.0, +1.0); + ParameterDriver r = new ParameterDriver("r", 0.0, 1.0, -1.0, +1.0); + ParameterDriversList list = new ParameterDriversList(); + + // first add the drivers once each + list.add(p); + list.add(q); + list.add(r); + Assertions.assertEquals(3, list.getDrivers().size()); + Assertions.assertEquals(1, list.getDrivers().get(0).getRawDrivers().size()); + Assertions.assertSame(p, list.getDrivers().get(0).getRawDrivers().get(0)); + Assertions.assertEquals(1, list.getDrivers().get(1).getRawDrivers().size()); + Assertions.assertSame(q, list.getDrivers().get(1).getRawDrivers().get(0)); + Assertions.assertEquals(1, list.getDrivers().get(2).getRawDrivers().size()); + Assertions.assertSame(r, list.getDrivers().get(2).getRawDrivers().get(0)); + + // then add the same ones several times more, this should be a no-op + list.add(p); + list.add(q); + list.add(r); + list.add(r); + list.add(r); + list.add(p); + list.add(q); + list.add(p); + list.add(r); + Assertions.assertEquals(3, list.getDrivers().size()); + Assertions.assertEquals(1, list.getDrivers().get(0).getRawDrivers().size()); + Assertions.assertSame(p, list.getDrivers().get(0).getRawDrivers().get(0)); + Assertions.assertEquals(1, list.getDrivers().get(1).getRawDrivers().size()); + Assertions.assertSame(q, list.getDrivers().get(1).getRawDrivers().get(0)); + Assertions.assertEquals(1, list.getDrivers().get(2).getRawDrivers().size()); + Assertions.assertSame(r, list.getDrivers().get(2).getRawDrivers().get(0)); + + // then add a new driver for the second parameter + ParameterDriver newQ = new ParameterDriver("q", 0.0, 1.0, -1.0, +1.0); + list.add(newQ); + Assertions.assertEquals(3, list.getDrivers().size()); + Assertions.assertEquals(1, list.getDrivers().get(0).getRawDrivers().size()); + Assertions.assertSame(p, list.getDrivers().get(0).getRawDrivers().get(0)); + Assertions.assertEquals(2, list.getDrivers().get(1).getRawDrivers().size()); + Assertions.assertSame(q, list.getDrivers().get(1).getRawDrivers().get(0)); + Assertions.assertSame(newQ, list.getDrivers().get(1).getRawDrivers().get(1)); + Assertions.assertEquals(1, list.getDrivers().get(2).getRawDrivers().size()); + Assertions.assertSame(r, list.getDrivers().get(2).getRawDrivers().get(0)); + + } + } diff --git a/src/test/java/org/orekit/utils/SortedListTrimmerTest.java b/src/test/java/org/orekit/utils/SortedListTrimmerTest.java new file mode 100644 index 0000000000..5375697c32 --- /dev/null +++ b/src/test/java/org/orekit/utils/SortedListTrimmerTest.java @@ -0,0 +1,232 @@ +/* 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.utils; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.orekit.Utils; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; +import org.orekit.errors.TimeStampedCacheException; +import org.orekit.time.AbsoluteDate; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * Unit tests for {@link SortedListTrimmer}. + * + * @author Evan Ward + */ +public class SortedListTrimmerTest { + + /** + * arbitrary date + */ + private static final AbsoluteDate date = AbsoluteDate.CCSDS_EPOCH; + + /** + * set Orekit data for useful debugging messages from dates. + */ + @BeforeAll + public static void setUpBefore() { + Utils.setDataRoot("regular-data"); + } + + /** + * data provided to {@link #trimmer} + */ + private List data; + + /** + * subject under test + */ + private SortedListTrimmer trimmer; + + /** + * create {@link #trimmer} and {@link #data} with neighborsSize = 3 + */ + @BeforeEach + public void setUp() { + data = Arrays.asList(date, date.shiftedBy(1), date.shiftedBy(2), + date.shiftedBy(3), date.shiftedBy(4), + date.shiftedBy(5)); + trimmer = new SortedListTrimmer(3); + } + + /** + * check + * {@link SortedListTrimmer#getNeighborsSubList(AbsoluteDate, List)} + */ + @Test + public void testGetNeighborsSubList() { + // exception for neighborsSize > data.size() + try { + new SortedListTrimmer(data.size() + 1).getNeighborsSubList(date, data); + Assertions.fail("Expected Exception"); + } catch (OrekitException e) { + Assertions.assertEquals(OrekitMessages.NOT_ENOUGH_DATA, e.getSpecifier()); + } + + // exception for non-positive neighborsSize + try { + new SortedListTrimmer(0); + Assertions.fail("Expected Exception"); + } catch (IllegalArgumentException e) { + // expected + } + + // exception for null data + try { + new SortedListTrimmer(1).getNeighborsSubList(date, null); + Assertions.fail("Expected Exception"); + } catch (NullPointerException e) { + // expected + } + + // exception for zero data + try { + new SortedListTrimmer(1).getNeighborsSubList(date, Collections.emptyList()); + Assertions.fail("Expected Exception"); + } catch (OrekitException e) { + Assertions.assertEquals(OrekitMessages.NOT_ENOUGH_DATA, e.getSpecifier()); + } + } + + /** + * check {@link SortedListTrimmer#getNeighborsSubList(AbsoluteDate, List)} at a + * series of different dates designed to test all logic paths. + */ + @Test + public void testGetNeighbors() { + // setup + int size = data.size(); + + // actions + verify + + // before fist data + try { + trimmer.getNeighborsSubList(data.get(0).shiftedBy(-1), data); + Assertions.fail("Expected Exception"); + } catch (TimeStampedCacheException e) { + // expected + Assertions.assertEquals(OrekitMessages.UNABLE_TO_GENERATE_NEW_DATA_BEFORE, e.getSpecifier()); + } + + // on fist date + Assertions.assertArrayEquals(trimmer.getNeighborsSubList(data.get(0), data).toArray(), + data.subList(0, 3).toArray()); + // between fist and second date + Assertions.assertArrayEquals(trimmer.getNeighborsSubList(data.get(0).shiftedBy(0.5), data).toArray(), + data.subList(0, 3).toArray()); + // in the middle on a date + Assertions.assertArrayEquals(trimmer.getNeighborsSubList(data.get(2), data).toArray(), + data.subList(1, 4).toArray()); + // in the middle between dates + Assertions.assertArrayEquals(trimmer.getNeighborsSubList(data.get(2).shiftedBy(0.5), data).toArray(), + data.subList(1, 4).toArray()); + // just before last date + Assertions.assertArrayEquals(trimmer.getNeighborsSubList(data.get(size - 1).shiftedBy(-0.5), data).toArray(), + data.subList(size - 3, size).toArray()); + // on last date + Assertions.assertArrayEquals(trimmer.getNeighborsSubList(data.get(size - 1), data).toArray(), + data.subList(size - 3, size).toArray()); + + // after last date + AbsoluteDate central = data.get(size - 1).shiftedBy(1); + try { + trimmer.getNeighborsSubList(central, data); + Assertions.fail("Expected Exception"); + } catch (TimeStampedCacheException e) { + // expected + Assertions.assertEquals(OrekitMessages.UNABLE_TO_GENERATE_NEW_DATA_AFTER, e.getSpecifier()); + } + } + + /** Check findIndex(...) returns results on a half closed interval. */ + @Test + public void testGetNeighborsSingle() { + // setup + trimmer = new SortedListTrimmer(1); + int size = data.size(); + + // actions + verify + // on fist date + Assertions.assertArrayEquals( + trimmer.getNeighborsSubList(data.get(0), data).toArray(), + data.subList(0, 1).toArray()); + // between fist and second date + Assertions.assertArrayEquals( + trimmer.getNeighborsSubList(data.get(0).shiftedBy(0.5), data).toArray(), + data.subList(0, 1).toArray()); + // in the middle on a date + Assertions.assertArrayEquals( + trimmer.getNeighborsSubList(data.get(2), data).toArray(), + data.subList(2, 3).toArray()); + // in the middle between dates + Assertions.assertArrayEquals( + trimmer.getNeighborsSubList(data.get(2).shiftedBy(0.1), data).toArray(), + data.subList(2, 3).toArray()); + // just before last date + Assertions.assertArrayEquals( + trimmer.getNeighborsSubList(data.get(size - 1).shiftedBy(-0.1), data).toArray(), + data.subList(size - 2, size - 1).toArray()); + // on last date + Assertions.assertArrayEquals( + trimmer.getNeighborsSubList(data.get(size - 1), data).toArray(), + data.subList(size - 1, size).toArray()); + } + + /** + * check {@link ImmutableTimeStampedCache#getMaxNeighborsSize()} + */ + @Test + public void testGetNeighborsSize() { + Assertions.assertEquals(trimmer.getNeighborsSize(), 3); + } + + @Test + public void testNonLinear() { + final List nonLinearCache = Arrays.asList(date.shiftedBy(10), + date.shiftedBy(14), + date.shiftedBy(18), + date.shiftedBy(23), + date.shiftedBy(30), + date.shiftedBy(36), + date.shiftedBy(45), + date.shiftedBy(55), + date.shiftedBy(67), + date.shiftedBy(90), + date.shiftedBy(118)); + for (double dt = 10; dt < 118; dt += 0.01) { + checkNeighbors(new SortedListTrimmer(2), nonLinearCache, dt); + } + } + + private void checkNeighbors(final SortedListTrimmer nonLinearTrimmer, + final List nonLinearCache, + final double offset) { + List s = nonLinearTrimmer.getNeighborsSubList(date.shiftedBy(offset), nonLinearCache); + Assertions.assertEquals(2, s.size()); + Assertions.assertTrue(s.get(0).durationFrom(date) <= offset); + Assertions.assertTrue(s.get(1).durationFrom(date) > offset); + } + +} diff --git a/src/test/java/org/orekit/utils/TimeSpanMapTest.java b/src/test/java/org/orekit/utils/TimeSpanMapTest.java index 114006b644..973e310d27 100644 --- a/src/test/java/org/orekit/utils/TimeSpanMapTest.java +++ b/src/test/java/org/orekit/utils/TimeSpanMapTest.java @@ -20,10 +20,14 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.orekit.Utils; +import org.orekit.errors.OrekitException; +import org.orekit.errors.OrekitMessages; import org.orekit.time.AbsoluteDate; import org.orekit.utils.TimeSpanMap.Span; import org.orekit.utils.TimeSpanMap.Transition; +import java.util.function.Consumer; + public class TimeSpanMapTest { @Test @@ -42,6 +46,8 @@ public void testSingleEntry() { Assertions.assertSame(single, map.get(AbsoluteDate.MODIFIED_JULIAN_EPOCH)); Assertions.assertSame(single, map.get(AbsoluteDate.PAST_INFINITY)); Assertions.assertEquals(1, map.getSpansNumber()); + Assertions.assertSame(single, map.getFirstNonNullSpan().getData()); + Assertions.assertSame(single, map.getLastNonNullSpan().getData()); } @Test @@ -668,6 +674,43 @@ public void testBetweenBothInfinity() { Assertions.assertEquals(1, map.get(AbsoluteDate.ARBITRARY_EPOCH).intValue()); } + @Test + public void testFirstNonNull() { + final TimeSpanMap map = new TimeSpanMap<>(null); + checkException(map, TimeSpanMap::getFirstNonNullSpan, OrekitMessages.NO_CACHED_ENTRIES); + for (double dt = 0; dt < 10; dt += 0.25) { + map.addValidAfter(null, AbsoluteDate.ARBITRARY_EPOCH.shiftedBy(dt), false); + } + checkException(map, TimeSpanMap::getFirstNonNullSpan, OrekitMessages.NO_CACHED_ENTRIES); + map.addValidAfter(22, map.getLastTransition().getDate().shiftedBy( 60.0), false); + map.addValidAfter(17, map.getLastTransition().getDate().shiftedBy(-20.0), false); + Assertions.assertEquals(17, map.getFirstNonNullSpan().getData()); + } + + @Test + public void testLastNonNull() { + final TimeSpanMap map = new TimeSpanMap<>(null); + checkException(map, TimeSpanMap::getLastNonNullSpan, OrekitMessages.NO_CACHED_ENTRIES); + for (double dt = 0; dt < 10; dt += 0.25) { + map.addValidBefore(null, AbsoluteDate.ARBITRARY_EPOCH.shiftedBy(-dt), false); + } + checkException(map, TimeSpanMap::getLastNonNullSpan, OrekitMessages.NO_CACHED_ENTRIES); + map.addValidBefore(22, map.getLastTransition().getDate().shiftedBy(-60.0), false); + map.addValidBefore(17, map.getLastTransition().getDate().shiftedBy( 20.0), false); + Assertions.assertEquals(17, map.getLastNonNullSpan().getData()); + } + + private void checkException(final TimeSpanMap map, + final Consumer> f, + OrekitMessages expected) { + try { + f.accept(map); + Assertions.fail("an exception should have been thrown"); + } catch (OrekitException oe) { + Assertions.assertEquals(expected, oe.getSpecifier()); + } + } + private void checkCountConsistency(final TimeSpanMap map) { final int count1 = map.getSpansNumber(); int count2 = 0; diff --git a/src/test/java/org/orekit/utils/TimeStampedAngularCoordinatesHermiteInterpolatorTest.java b/src/test/java/org/orekit/utils/TimeStampedAngularCoordinatesHermiteInterpolatorTest.java index 60be21f480..6aaa5a13a9 100644 --- a/src/test/java/org/orekit/utils/TimeStampedAngularCoordinatesHermiteInterpolatorTest.java +++ b/src/test/java/org/orekit/utils/TimeStampedAngularCoordinatesHermiteInterpolatorTest.java @@ -1,3 +1,20 @@ +/* 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.utils; import org.hamcrest.MatcherAssert; diff --git a/src/test/java/org/orekit/utils/TimeStampedFieldAngularCoordinatesHermiteInterpolatorTest.java b/src/test/java/org/orekit/utils/TimeStampedFieldAngularCoordinatesHermiteInterpolatorTest.java index 17b5b4282a..556d899361 100644 --- a/src/test/java/org/orekit/utils/TimeStampedFieldAngularCoordinatesHermiteInterpolatorTest.java +++ b/src/test/java/org/orekit/utils/TimeStampedFieldAngularCoordinatesHermiteInterpolatorTest.java @@ -1,3 +1,20 @@ +/* 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.utils; import org.hamcrest.MatcherAssert; diff --git a/src/test/java/org/orekit/utils/TimeStampedFieldPVCoordinatesHermiteInterpolatorTest.java b/src/test/java/org/orekit/utils/TimeStampedFieldPVCoordinatesHermiteInterpolatorTest.java index 1f51b7d3ba..16d8c9f3ec 100644 --- a/src/test/java/org/orekit/utils/TimeStampedFieldPVCoordinatesHermiteInterpolatorTest.java +++ b/src/test/java/org/orekit/utils/TimeStampedFieldPVCoordinatesHermiteInterpolatorTest.java @@ -1,3 +1,20 @@ +/* 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.utils; import org.hipparchus.Field; diff --git a/src/test/java/org/orekit/utils/TimeStampedPVCoordinatesHermiteInterpolatorTest.java b/src/test/java/org/orekit/utils/TimeStampedPVCoordinatesHermiteInterpolatorTest.java index 7851a9e466..3a9501b755 100644 --- a/src/test/java/org/orekit/utils/TimeStampedPVCoordinatesHermiteInterpolatorTest.java +++ b/src/test/java/org/orekit/utils/TimeStampedPVCoordinatesHermiteInterpolatorTest.java @@ -1,3 +1,20 @@ +/* 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.utils; import org.hipparchus.analysis.polynomials.PolynomialFunction; diff --git a/src/test/java/org/orekit/utils/units/UnitTest.java b/src/test/java/org/orekit/utils/units/UnitTest.java index e370fcbcd3..22e01b33dd 100644 --- a/src/test/java/org/orekit/utils/units/UnitTest.java +++ b/src/test/java/org/orekit/utils/units/UnitTest.java @@ -20,6 +20,7 @@ import java.util.Collections; import org.hipparchus.fraction.Fraction; +import org.hipparchus.util.Binary64; import org.hipparchus.util.FastMath; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -41,11 +42,15 @@ public void testTime() { Assertions.assertEquals( 3600.0, Unit.HOUR.toSI(1.0), 1.0e-10); Assertions.assertEquals( 86400.0, Unit.DAY.toSI(1.0), 1.0e-10); Assertions.assertEquals(31557600.0, Unit.YEAR.toSI(1.0), 1.0e-10); + Assertions.assertEquals(31557600.0, Unit.YEAR.toSI(Double.valueOf(1.0)), 1.0e-10); + Assertions.assertEquals(31557600.0, Unit.YEAR.toSI(new Binary64(1.0)).getReal(), 1.0e-10); Assertions.assertEquals(1.0, Unit.SECOND.fromSI( 1.0), 1.0e-10); Assertions.assertEquals(1.0, Unit.MINUTE.fromSI( 60.0), 1.0e-10); Assertions.assertEquals(1.0, Unit.HOUR.fromSI( 3600.0), 1.0e-10); Assertions.assertEquals(1.0, Unit.DAY.fromSI( 86400.0), 1.0e-10); Assertions.assertEquals(1.0, Unit.YEAR.fromSI(31557600.0), 1.0e-10); + Assertions.assertEquals(1.0, Unit.YEAR.fromSI(Double.valueOf(31557600.0)), 1.0e-10); + Assertions.assertEquals(1.0, Unit.YEAR.fromSI(new Binary64(31557600.0)).getReal(), 1.0e-10); Assertions.assertEquals(365.25, Unit.DAY.fromSI(Unit.YEAR.toSI(1.0)), 1.0e-10); } @@ -73,36 +78,36 @@ public void testEquals() { @Test public void testReference() { - checkReference(Unit.NONE, "n/a", 1.0, 0, 0, 0, 0, 0); - checkReference(Unit.ONE, "1", 1.0, 0, 0, 0, 0, 0); - checkReference(Unit.PERCENT, "%", 0.01, 0, 0, 0, 0, 0); - checkReference(Unit.SECOND, "s", 1.0, 0, 0, 1, 0, 0); - checkReference(Unit.MINUTE, "min", 60.0, 0, 0, 1, 0, 0); - checkReference(Unit.HOUR, "h", 3600.0, 0, 0, 1, 0, 0); - checkReference(Unit.DAY, "d", 86400.0, 0, 0, 1, 0, 0); - checkReference(Unit.YEAR, "a", 31557600.0, 0, 0, 1, 0, 0); - checkReference(Unit.HERTZ, "Hz", 1.0, 0, 0, -1, 0, 0); - checkReference(Unit.METRE, "m", 1.0, 0, 1, 0, 0, 0); - checkReference(Unit.KILOMETRE, "km", 1000.0, 0, 1, 0, 0, 0); - checkReference(Unit.KILOGRAM, "kg", 1.0, 1, 0, 0, 0, 0); - checkReference(Unit.GRAM, "g", 0.001, 1, 0, 0, 0, 0); - checkReference(Unit.AMPERE, "A", 1.0, 0, 0, 0, 1, 0); - checkReference(Unit.RADIAN, "rad", 1.0, 0, 0, 0, 0, 1); - checkReference(Unit.DEGREE, "°", FastMath.PI / 180.0, 0, 0, 0, 0, 1); - checkReference(Unit.ARC_MINUTE, "′", FastMath.PI / 10800.0, 0, 0, 0, 0, 1); - checkReference(Unit.ARC_SECOND, "″", FastMath.PI / 648000.0, 0, 0, 0, 0, 1); - checkReference(Unit.REVOLUTION, "rev", 2.0 * FastMath.PI, 0, 0, 0, 0, 1); - checkReference(Unit.NEWTON, "N", 1.0, 1, 1, -2, 0, 0); - checkReference(Unit.PASCAL, "Pa", 1.0, 1, -1, -2, 0, 0); - checkReference(Unit.BAR, "bar", 100000.0, 1, -1, -2, 0, 0); - checkReference(Unit.JOULE, "J", 1.0, 1, 2, -2, 0, 0); - checkReference(Unit.WATT, "W", 1.0, 1, 2, -3, 0, 0); - checkReference(Unit.COULOMB, "C", 1.0, 0, 0, 1, 1, 0); - checkReference(Unit.VOLT, "V", 1.0, 1, 2, -3, -1, 0); - checkReference(Unit.OHM, "Ω", 1.0, 1, 2, -3, -2, 0); - checkReference(Unit.TESLA, "T", 1.0, 1, 0, -2, -1, 0); + checkReference(Unit.NONE, "n/a", 1.0, 0, 0, 0, 0, 0); + checkReference(Unit.ONE, "1", 1.0, 0, 0, 0, 0, 0); + checkReference(Unit.PERCENT, "%", 0.01, 0, 0, 0, 0, 0); + checkReference(Unit.SECOND, "s", 1.0, 0, 0, 1, 0, 0); + checkReference(Unit.MINUTE, "min", 60.0, 0, 0, 1, 0, 0); + checkReference(Unit.HOUR, "h", 3600.0, 0, 0, 1, 0, 0); + checkReference(Unit.DAY, "d", 86400.0, 0, 0, 1, 0, 0); + checkReference(Unit.YEAR, "a", 31557600.0, 0, 0, 1, 0, 0); + checkReference(Unit.HERTZ, "Hz", 1.0, 0, 0, -1, 0, 0); + checkReference(Unit.METRE, "m", 1.0, 0, 1, 0, 0, 0); + checkReference(Unit.KILOMETRE, "km", 1000.0, 0, 1, 0, 0, 0); + checkReference(Unit.KILOGRAM, "kg", 1.0, 1, 0, 0, 0, 0); + checkReference(Unit.GRAM, "g", 0.001, 1, 0, 0, 0, 0); + checkReference(Unit.AMPERE, "A", 1.0, 0, 0, 0, 1, 0); + checkReference(Unit.RADIAN, "rad", 1.0, 0, 0, 0, 0, 1); + checkReference(Unit.DEGREE, "°", FastMath.PI / 180.0, 0, 0, 0, 0, 1); + checkReference(Unit.ARC_MINUTE, "′", FastMath.PI / 10800.0, 0, 0, 0, 0, 1); + checkReference(Unit.ARC_SECOND, "″", FastMath.PI / 648000.0, 0, 0, 0, 0, 1); + checkReference(Unit.REVOLUTION, "rev", 2.0 * FastMath.PI, 0, 0, 0, 0, 1); + checkReference(Unit.NEWTON, "N", 1.0, 1, 1, -2, 0, 0); + checkReference(Unit.PASCAL, "Pa", 1.0, 1, -1, -2, 0, 0); + checkReference(Unit.BAR, "bar", 100000.0, 1, -1, -2, 0, 0); + checkReference(Unit.JOULE, "J", 1.0, 1, 2, -2, 0, 0); + checkReference(Unit.WATT, "W", 1.0, 1, 2, -3, 0, 0); + checkReference(Unit.COULOMB, "C", 1.0, 0, 0, 1, 1, 0); + checkReference(Unit.VOLT, "V", 1.0, 1, 2, -3, -1, 0); + checkReference(Unit.OHM, "Ω", 1.0, 1, 2, -3, -2, 0); + checkReference(Unit.TESLA, "T", 1.0, 1, 0, -2, -1, 0); checkReference(Unit.SOLAR_FLUX_UNIT, "SFU", 1.0e-22, 1, 0, -2, 0, 0); - checkReference(Unit.TOTAL_ELECTRON_CONTENT_UNIT, "TECU", 1.0e16, 0, -2, 0, 0, 0); + checkReference(Unit.TOTAL_ELECTRON_CONTENT_UNIT, "TECU", 1.0e16, 0, -2, 0, 0, 0); } diff --git a/src/test/java/org/orekit/utils/units/UnitsConverterTest.java b/src/test/java/org/orekit/utils/units/UnitsConverterTest.java index d4feff99c2..c82dc1e012 100644 --- a/src/test/java/org/orekit/utils/units/UnitsConverterTest.java +++ b/src/test/java/org/orekit/utils/units/UnitsConverterTest.java @@ -48,8 +48,8 @@ public void testRotationRate() { @Test public void testPressure() { final double atm = 1013.25; - final Unit mbar = Unit.parse("mbar"); - final Unit hPa = Unit.parse("hPa"); + final Unit mbar = Unit.parse("mbar"); + final Unit hPa = Unit.parse("hPa"); final UnitsConverter mbar2hPa = new UnitsConverter(mbar, hPa); Assertions.assertEquals(atm, mbar2hPa.convert(atm), 1.0e-12); } diff --git a/src/test/resources/gnss/clock/issue1356.clk.gz b/src/test/resources/gnss/clock/issue1356.clk.gz new file mode 100644 index 0000000000..ab6180dd49 Binary files /dev/null and b/src/test/resources/gnss/clock/issue1356.clk.gz differ diff --git a/src/test/resources/gnss/clock/part-1.clk b/src/test/resources/gnss/clock/part-1.clk new file mode 100644 index 0000000000..f40f545d42 --- /dev/null +++ b/src/test/resources/gnss/clock/part-1.clk @@ -0,0 +1,128 @@ + 3.00 C M RINEX VERSION / TYPE +MADOCA 0.9.3 20200903 141939 UTC PGM / RUN BY / DATE +MADOCA FINAL COMMENT + GPS TIME SYSTEM ID + P1C1_RINEX_20200901.DCB SYS / DCBS APPLIED + igs14_20200901.atx SYS / PCVS APPLIED + 2 AR AS # / TYPES OF DATA + ANALYSIS CENTER + 1 2020 9 1 0 0 0.000000 2020 9 1 0 4 30.000000# OF CLK REF + 5 IGS14 # OF SOLN STA / TRF +CHPI 41609M003 4164613909 -4162456992 -2445028632SOLN STA NAME / NUM +GLPS 42005M002 -33800869 -6377516528 -82154223SOLN STA NAME / NUM +KITG 12334M004 1944879627 4556783828 4004205962SOLN STA NAME / NUM +NOVM 12367M002 452260696 3635877614 5203453343SOLN STA NAME / NUM +OWMG 50253M004 -4584394280 -290931596 -4410047891SOLN STA NAME / NUM + 7 # OF SOLN SATS +G17 G27 E11 R01 R17 R23 J01 PRN LIST + END OF HEADER +AS G17 2020 09 01 00 00 0.000000 2 3.191505186622e-04 1.003862579940e-10 +AS G27 2020 09 01 00 00 0.000000 2 -3.850020774386e-04 1.006773039867e-10 +AS R01 2020 09 01 00 00 0.000000 2 6.669178048750e-05 1.003834197019e-10 +AS R17 2020 09 01 00 00 0.000000 2 3.541404523254e-04 1.003946896151e-10 +AS R23 2020 09 01 00 00 0.000000 2 3.288151578612e-04 1.005228780693e-10 +AS J01 2020 09 01 00 00 0.000000 2 -1.835373032761e-04 1.004452051682e-10 +AR CHPI 2020 09 01 00 00 0.000000 2 -3.015808769579e-04 1.008894715227e-10 +AR GLPS 2020 09 01 00 00 0.000000 2 -1.947994477078e-07 1.080539352122e-10 +AR KITG 2020 09 01 00 00 0.000000 2 -1.578514524589e-05 1.006404209213e-10 +AR NOVM 2020 09 01 00 00 0.000000 2 -4.536669503332e-04 1.005038095177e-10 +AR OWMG 2020 09 01 00 00 0.000000 2 -2.082798191909e-07 1.005851477318e-10 +AS G17 2020 09 01 00 00 30.000000 2 3.191506572706e-04 1.003864852144e-10 +AS G27 2020 09 01 00 00 30.000000 2 -3.850023654619e-04 1.006759799479e-10 +AS R01 2020 09 01 00 00 30.000000 2 6.669181951441e-05 1.003838189155e-10 +AS R17 2020 09 01 00 00 30.000000 2 3.541405139190e-04 1.003965046603e-10 +AS R23 2020 09 01 00 00 30.000000 2 3.288152732106e-04 1.005201172064e-10 +AS J01 2020 09 01 00 00 30.000000 2 -1.835371927809e-04 1.004449222210e-10 +AR CHPI 2020 09 01 00 00 30.000000 2 -3.015787263035e-04 1.008865275570e-10 +AR GLPS 2020 09 01 00 00 30.000000 2 -1.945515831468e-07 1.080607056645e-10 +AR KITG 2020 09 01 00 00 30.000000 2 -1.578553280056e-05 1.006410313871e-10 +AR NOVM 2020 09 01 00 00 30.000000 2 -4.536669698725e-04 1.005166843456e-10 +AR OWMG 2020 09 01 00 00 30.000000 2 -2.085428609750e-07 1.005845732989e-10 +AS G17 2020 09 01 00 01 0.000000 2 3.191509294453e-04 1.003872356559e-10 +AS G27 2020 09 01 00 01 0.000000 2 -3.850026641417e-04 1.006761991299e-10 +AS R01 2020 09 01 00 01 0.000000 2 6.669182415752e-05 1.003840755554e-10 +AS R17 2020 09 01 00 01 0.000000 2 3.541406410097e-04 1.003954802923e-10 +AS R23 2020 09 01 00 01 0.000000 2 3.288152901942e-04 1.005207945478e-10 +AS J01 2020 09 01 00 01 0.000000 2 -1.835370705076e-04 1.004433565331e-10 +AR CHPI 2020 09 01 00 01 0.000000 2 -3.015784164361e-04 1.008855843231e-10 +AR GLPS 2020 09 01 00 01 0.000000 2 -1.948315487077e-07 1.080689203396e-10 +AR KITG 2020 09 01 00 01 0.000000 2 -1.578421535522e-05 1.006419054727e-10 +AR NOVM 2020 09 01 00 01 0.000000 2 -4.536669863118e-04 1.005347177866e-10 +AR OWMG 2020 09 01 00 01 0.000000 2 -2.085912381818e-07 1.005847330064e-10 +AS G17 2020 09 01 00 01 30.000000 2 3.191511951835e-04 1.003866111770e-10 +AS G27 2020 09 01 00 01 30.000000 2 -3.850029359844e-04 1.006780566546e-10 +AS R01 2020 09 01 00 01 30.000000 2 6.669187598323e-05 1.003859692788e-10 +AS R17 2020 09 01 00 01 30.000000 2 3.541407487588e-04 1.003958573199e-10 +AS R23 2020 09 01 00 01 30.000000 2 3.288153356816e-04 1.005219468827e-10 +AS J01 2020 09 01 00 01 30.000000 2 -1.835369458100e-04 1.004448909494e-10 +AR CHPI 2020 09 01 00 01 30.000000 2 -3.015795857690e-04 1.008853760227e-10 +AR GLPS 2020 09 01 00 01 30.000000 2 -1.962633750854e-07 1.080786644120e-10 +AR KITG 2020 09 01 00 01 30.000000 2 -1.578384973213e-05 1.006421844255e-10 +AR NOVM 2020 09 01 00 01 30.000000 2 -4.536669434023e-04 1.005053872410e-10 +AR OWMG 2020 09 01 00 01 30.000000 2 -2.086628303335e-07 1.005863050610e-10 +AS G17 2020 09 01 00 02 0.000000 2 3.191513814991e-04 1.003872659416e-10 +AS G27 2020 09 01 00 02 0.000000 2 -3.850032210803e-04 1.006775573200e-10 +AS R01 2020 09 01 00 02 0.000000 2 6.669189876244e-05 1.003864119371e-10 +AS R17 2020 09 01 00 02 0.000000 2 3.541408348269e-04 1.003964880111e-10 +AS R23 2020 09 01 00 02 0.000000 2 3.288155083650e-04 1.005225182204e-10 +AS J01 2020 09 01 00 02 0.000000 2 -1.835368564524e-04 1.004452796742e-10 +AR CHPI 2020 09 01 00 02 0.000000 2 -3.015793320299e-04 1.008853104027e-10 +AR GLPS 2020 09 01 00 02 0.000000 2 -1.954810131642e-07 1.080862130584e-10 +AR KITG 2020 09 01 00 02 0.000000 2 -1.578653675451e-05 1.006427877952e-10 +AR NOVM 2020 09 01 00 02 0.000000 2 -4.536669488393e-04 1.005059305192e-10 +AR OWMG 2020 09 01 00 02 0.000000 2 -2.084258305917e-07 1.005863786645e-10 +AS G17 2020 09 01 00 02 30.000000 2 3.191514883248e-04 1.003878917517e-10 +AS G27 2020 09 01 00 02 30.000000 2 -3.850034911723e-04 1.006771492399e-10 +AS R01 2020 09 01 00 02 30.000000 2 6.669186561016e-05 1.003844330647e-10 +AS R17 2020 09 01 00 02 30.000000 2 3.541409223188e-04 1.003970440979e-10 +AS R23 2020 09 01 00 02 30.000000 2 3.288156386651e-04 1.005130924864e-10 +AS J01 2020 09 01 00 02 30.000000 2 -1.835367443881e-04 1.004452593979e-10 +AR CHPI 2020 09 01 00 02 30.000000 2 -3.015776574865e-04 1.008843581598e-10 +AR GLPS 2020 09 01 00 02 30.000000 2 -1.954371200009e-07 1.080938437636e-10 +AR KITG 2020 09 01 00 02 30.000000 2 -1.578745491624e-05 1.006434275609e-10 +AR NOVM 2020 09 01 00 02 30.000000 2 -4.536669599779e-04 1.005065355282e-10 +AR OWMG 2020 09 01 00 02 30.000000 2 -2.083614107830e-07 1.005858143764e-10 +AS G17 2020 09 01 00 03 0.000000 2 3.191515444542e-04 1.003902455424e-10 +AS G27 2020 09 01 00 03 0.000000 2 -3.850037427945e-04 1.006768577188e-10 +AS R01 2020 09 01 00 03 0.000000 2 6.669182236858e-05 1.003849128406e-10 +AS R17 2020 09 01 00 03 0.000000 2 3.541410555110e-04 1.003982449369e-10 +AS R23 2020 09 01 00 03 0.000000 2 3.288156188429e-04 1.005140965095e-10 +AS J01 2020 09 01 00 03 0.000000 2 -1.835366371341e-04 1.004456043477e-10 +AR CHPI 2020 09 01 00 03 0.000000 2 -3.015807985907e-04 1.008845048179e-10 +AR GLPS 2020 09 01 00 03 0.000000 2 -1.954118795587e-07 1.081015769162e-10 +AR KITG 2020 09 01 00 03 0.000000 2 -1.578542258061e-05 1.006440761278e-10 +AR NOVM 2020 09 01 00 03 0.000000 2 -4.536669376609e-04 1.005072029387e-10 +AR OWMG 2020 09 01 00 03 0.000000 2 -2.081847124133e-07 1.005861690322e-10 +AS G17 2020 09 01 00 03 30.000000 2 3.191515350023e-04 1.003890585047e-10 +AS G27 2020 09 01 00 03 30.000000 2 -3.850040183163e-04 1.006768353366e-10 +AS R01 2020 09 01 00 03 30.000000 2 6.669185766615e-05 1.003869689710e-10 +AS R17 2020 09 01 00 03 30.000000 2 3.541411916741e-04 1.003987574628e-10 +AS R23 2020 09 01 00 03 30.000000 2 3.288156817260e-04 1.005148774907e-10 +AS J01 2020 09 01 00 03 30.000000 2 -1.835365354885e-04 1.004458510755e-10 +AR CHPI 2020 09 01 00 03 30.000000 2 -3.015783036195e-04 1.008844908865e-10 +AR GLPS 2020 09 01 00 03 30.000000 2 -1.949245222321e-07 1.081095514095e-10 +AR KITG 2020 09 01 00 03 30.000000 2 -1.578731205007e-05 1.006448072888e-10 +AR NOVM 2020 09 01 00 03 30.000000 2 -4.536669441427e-04 1.004834068988e-10 +AR OWMG 2020 09 01 00 03 30.000000 2 -2.083503771772e-07 1.005860579667e-10 +AS G17 2020 09 01 00 04 0.000000 2 3.191516547544e-04 1.003907265470e-10 +AS G27 2020 09 01 00 04 0.000000 2 -3.850042830678e-04 1.006651964658e-10 +AS R01 2020 09 01 00 04 0.000000 2 6.669188182029e-05 1.003858103394e-10 +AS R17 2020 09 01 00 04 0.000000 2 3.541412699240e-04 1.003990459860e-10 +AS R23 2020 09 01 00 04 0.000000 2 3.288157300087e-04 1.005160580182e-10 +AS J01 2020 09 01 00 04 0.000000 2 -1.835364206033e-04 1.004465683232e-10 +AR CHPI 2020 09 01 00 04 0.000000 2 -3.015807552333e-04 1.008835943287e-10 +AR GLPS 2020 09 01 00 04 0.000000 2 -1.948306571457e-07 1.009672296473e-10 +AR KITG 2020 09 01 00 04 0.000000 2 -1.578828506791e-05 1.006455569893e-10 +AR NOVM 2020 09 01 00 04 0.000000 2 -4.536669399125e-04 1.004841253215e-10 +AR OWMG 2020 09 01 00 04 0.000000 2 -2.084444349852e-07 1.005857070486e-10 +AS G17 2020 09 01 00 04 30.000000 2 3.191517431504e-04 1.003931878873e-10 +AS G27 2020 09 01 00 04 30.000000 2 -3.850045565075e-04 1.006649594864e-10 +AS R01 2020 09 01 00 04 30.000000 2 6.669197509194e-05 1.003860899082e-10 +AS R17 2020 09 01 00 04 30.000000 2 3.541413893047e-04 1.004002120943e-10 +AS R23 2020 09 01 00 04 30.000000 2 3.288157208984e-04 1.005169146807e-10 +AS J01 2020 09 01 00 04 30.000000 2 -1.835363165839e-04 1.004469517571e-10 +AR CHPI 2020 09 01 00 04 30.000000 2 -3.015804311253e-04 1.008838095198e-10 +AR GLPS 2020 09 01 00 04 30.000000 2 -1.949423356807e-07 1.009675730547e-10 +AR KITG 2020 09 01 00 04 30.000000 2 -1.578814549636e-05 1.006463022613e-10 +AR NOVM 2020 09 01 00 04 30.000000 2 -4.536669513131e-04 1.004848595297e-10 +AR OWMG 2020 09 01 00 04 30.000000 2 -2.085365587999e-07 1.005862195333e-10 diff --git a/src/test/resources/gnss/clock/part-2.clk b/src/test/resources/gnss/clock/part-2.clk new file mode 100644 index 0000000000..58d74c52de --- /dev/null +++ b/src/test/resources/gnss/clock/part-2.clk @@ -0,0 +1,117 @@ + 3.00 C M RINEX VERSION / TYPE +MADOCA 0.9.3 20200903 141939 UTC PGM / RUN BY / DATE +MADOCA FINAL COMMENT + GPS TIME SYSTEM ID + P1C1_RINEX_20200901.DCB SYS / DCBS APPLIED + igs14_20200901.atx SYS / PCVS APPLIED + 2 AR AS # / TYPES OF DATA + ANALYSIS CENTER + 1 2020 9 1 0 5 0.000000 2020 9 1 0 9 30.000000# OF CLK REF + 4 IGS14 # OF SOLN STA / TRF +CHPI 41609M003 4164613909 -4162456992 -2445028632SOLN STA NAME / NUM +GLPS 42005M002 -33800869 -6377516528 -82154223SOLN STA NAME / NUM +KITG 12334M004 1944879627 4556783828 4004205962SOLN STA NAME / NUM +OWMG 50253M004 -4584394280 -290931596 -4410047891SOLN STA NAME / NUM + 6 # OF SOLN SATS +G17 G27 E11 R01 R17 R23 J01 PRN LIST + END OF HEADER +AS G17 2020 09 01 00 05 0.000000 2 3.191519112614e-04 1.003921431862e-10 +AS G27 2020 09 01 00 05 0.000000 2 -3.850048308604e-04 1.006640446406e-10 +AS R01 2020 09 01 00 05 0.000000 2 6.669198448404e-05 1.003865962517e-10 +AS R17 2020 09 01 00 05 0.000000 2 3.541414869793e-04 1.004004093479e-10 +AS R23 2020 09 01 00 05 0.000000 2 3.288158512702e-04 1.005177661685e-10 +AS J01 2020 09 01 00 05 0.000000 2 -1.835362120476e-04 1.004469961450e-10 +AR CHPI 2020 09 01 00 05 0.000000 2 -3.015781312145e-04 1.008840159514e-10 +AR GLPS 2020 09 01 00 05 0.000000 2 -1.943783870023e-07 1.009676341523e-10 +AR KITG 2020 09 01 00 05 0.000000 2 -1.578638350414e-05 1.006470705062e-10 +AR OWMG 2020 09 01 00 05 0.000000 2 -2.087779005611e-07 1.005857730040e-10 +AS G17 2020 09 01 00 05 30.000000 2 3.191521089030e-04 1.003917122169e-10 +AS G27 2020 09 01 00 05 30.000000 2 -3.850051152779e-04 1.006623584777e-10 +AS R01 2020 09 01 00 05 30.000000 2 6.669204097039e-05 1.003856624085e-10 +AS R17 2020 09 01 00 05 30.000000 2 3.541415501531e-04 1.004015729708e-10 +AS R23 2020 09 01 00 05 30.000000 2 3.288159058209e-04 1.005187076307e-10 +AS J01 2020 09 01 00 05 30.000000 2 -1.835361092098e-04 1.004523631401e-10 +AR CHPI 2020 09 01 00 05 30.000000 2 -3.015805295838e-04 1.008842871326e-10 +AR GLPS 2020 09 01 00 05 30.000000 2 -1.942933619448e-07 1.009677351165e-10 +AR KITG 2020 09 01 00 05 30.000000 2 -1.578581060523e-05 1.006477799832e-10 +AR OWMG 2020 09 01 00 05 30.000000 2 -2.089082883158e-07 1.005835022226e-10 +AS G17 2020 09 01 00 06 0.000000 2 3.191522375670e-04 1.003922889161e-10 +AS G27 2020 09 01 00 06 0.000000 2 -3.850053915542e-04 1.006630732097e-10 +AS R01 2020 09 01 00 06 0.000000 2 6.669213065971e-05 1.003847991920e-10 +AS R17 2020 09 01 00 06 0.000000 2 3.541416587480e-04 1.004021634465e-10 +AS R23 2020 09 01 00 06 0.000000 2 3.288160311766e-04 1.005197727999e-10 +AS J01 2020 09 01 00 06 0.000000 2 -1.835359908964e-04 1.004479947444e-10 +AR CHPI 2020 09 01 00 06 0.000000 2 -3.015814580897e-04 1.008840295722e-10 +AR GLPS 2020 09 01 00 06 0.000000 2 -1.945528912151e-07 1.009673123702e-10 +AR KITG 2020 09 01 00 06 0.000000 2 -1.578613159216e-05 1.006484627574e-10 +AR OWMG 2020 09 01 00 06 0.000000 2 -2.089804446277e-07 1.005841183993e-10 +AS G17 2020 09 01 00 06 30.000000 2 3.191523845193e-04 1.003930802073e-10 +AS G27 2020 09 01 00 06 30.000000 2 -3.850056631629e-04 1.006672521627e-10 +AS R01 2020 09 01 00 06 30.000000 2 6.669215856150e-05 1.003853763472e-10 +AS R17 2020 09 01 00 06 30.000000 2 3.541418034138e-04 1.004023864951e-10 +AS R23 2020 09 01 00 06 30.000000 2 3.288160673255e-04 1.005212570450e-10 +AS J01 2020 09 01 00 06 30.000000 2 -1.835358832961e-04 1.004463949476e-10 +AR CHPI 2020 09 01 00 06 30.000000 2 -3.015786103922e-04 1.008844692481e-10 +AR GLPS 2020 09 01 00 06 30.000000 2 -1.942958126837e-07 1.009681288144e-10 +AR KITG 2020 09 01 00 06 30.000000 2 -1.578508940110e-05 1.006493584158e-10 +AR OWMG 2020 09 01 00 06 30.000000 2 -2.089168472941e-07 1.005862996362e-10 +AS G17 2020 09 01 00 07 0.000000 2 3.191528134646e-04 1.003939217943e-10 +AS G27 2020 09 01 00 07 0.000000 2 -3.850059450407e-04 1.006662963676e-10 +AS R01 2020 09 01 00 07 0.000000 2 6.669231807706e-05 1.003859720066e-10 +AS R17 2020 09 01 00 07 0.000000 2 3.541419046618e-04 1.004032001691e-10 +AS R23 2020 09 01 00 07 0.000000 2 3.288162365636e-04 1.005220679570e-10 +AS J01 2020 09 01 00 07 0.000000 2 -1.835357776760e-04 1.004504791343e-10 +AR CHPI 2020 09 01 00 07 0.000000 2 -3.015817084265e-04 1.008848761208e-10 +AR GLPS 2020 09 01 00 07 0.000000 2 -1.938189398839e-07 1.009681451912e-10 +AR KITG 2020 09 01 00 07 0.000000 2 -1.578668049920e-05 1.006502526110e-10 +AR OWMG 2020 09 01 00 07 0.000000 2 -2.089326649365e-07 1.005859525094e-10 +AS G17 2020 09 01 00 07 30.000000 2 3.191530518344e-04 1.003947476766e-10 +AS G27 2020 09 01 00 07 30.000000 2 -3.850062023397e-04 1.006648944975e-10 +AS R01 2020 09 01 00 07 30.000000 2 6.669235806726e-05 1.003865628609e-10 +AS R17 2020 09 01 00 07 30.000000 2 3.541419474591e-04 1.004040203481e-10 +AS R23 2020 09 01 00 07 30.000000 2 3.288162767690e-04 1.005230486809e-10 +AS J01 2020 09 01 00 07 30.000000 2 -1.835356757367e-04 1.004509919374e-10 +AR CHPI 2020 09 01 00 07 30.000000 2 -3.015794549257e-04 1.008847403336e-10 +AR GLPS 2020 09 01 00 07 30.000000 2 -1.939153867129e-07 1.009683214195e-10 +AR KITG 2020 09 01 00 07 30.000000 2 -1.578724683975e-05 1.006511646410e-10 +AR OWMG 2020 09 01 00 07 30.000000 2 -2.089671372967e-07 1.005856274793e-10 +AS G17 2020 09 01 00 08 0.000000 2 3.191532844925e-04 1.003956196758e-10 +AS G27 2020 09 01 00 08 0.000000 2 -3.850064819331e-04 1.006660568656e-10 +AS R01 2020 09 01 00 08 0.000000 2 6.669233624579e-05 1.003872085166e-10 +AS R17 2020 09 01 00 08 0.000000 2 3.541420685626e-04 1.004048619634e-10 +AS R23 2020 09 01 00 08 0.000000 2 3.288163678004e-04 1.005244944983e-10 +AS J01 2020 09 01 00 08 0.000000 2 -1.835355681476e-04 1.004524050743e-10 +AR CHPI 2020 09 01 00 08 0.000000 2 -3.015806800824e-04 1.008853562004e-10 +AR GLPS 2020 09 01 00 08 0.000000 2 -1.932545189490e-07 1.009687654232e-10 +AR KITG 2020 09 01 00 08 0.000000 2 -1.578823230527e-05 1.006521616363e-10 +AR OWMG 2020 09 01 00 08 0.000000 2 -2.088877808030e-07 1.005870685151e-10 +AS G17 2020 09 01 00 08 30.000000 2 3.191534097427e-04 1.003523459899e-10 +AS G27 2020 09 01 00 08 30.000000 2 -3.850067726050e-04 1.006219929608e-10 +AS R01 2020 09 01 00 08 30.000000 2 6.669224535277e-05 1.003454858570e-10 +AS R17 2020 09 01 00 08 30.000000 2 3.541421415617e-04 1.003618294448e-10 +AS R23 2020 09 01 00 08 30.000000 2 3.288163748695e-04 1.004813286152e-10 +AS J01 2020 09 01 00 08 30.000000 2 -1.835354729550e-04 1.004076304287e-10 +AR CHPI 2020 09 01 00 08 30.000000 2 -3.015808030073e-04 1.008424809443e-10 +AR GLPS 2020 09 01 00 08 30.000000 2 -1.930708147111e-07 1.009253806073e-10 +AR KITG 2020 09 01 00 08 30.000000 2 -1.578543862933e-05 1.006034596000e-10 +AR OWMG 2020 09 01 00 08 30.000000 2 -2.090212612949e-07 1.005421196166e-10 +AS G17 2020 09 01 00 09 0.000000 2 3.191535559419e-04 1.003512061177e-10 +AS G27 2020 09 01 00 09 0.000000 2 -3.850070293018e-04 1.006211447727e-10 +AS R01 2020 09 01 00 09 0.000000 2 6.669231005386e-05 1.003456203248e-10 +AS R17 2020 09 01 00 09 0.000000 2 3.541421854910e-04 1.003621255693e-10 +AS R23 2020 09 01 00 09 0.000000 2 3.288164087105e-04 1.004820068742e-10 +AS J01 2020 09 01 00 09 0.000000 2 -1.835353570133e-04 1.004033866815e-10 +AR CHPI 2020 09 01 00 09 0.000000 2 -3.015805306390e-04 1.008425436854e-10 +AR GLPS 2020 09 01 00 09 0.000000 2 -1.934740856069e-07 1.009252928960e-10 +AR KITG 2020 09 01 00 09 0.000000 2 -1.578647602315e-05 1.006038442150e-10 +AR OWMG 2020 09 01 00 09 0.000000 2 -2.089268131242e-07 1.005385252022e-10 +AS G17 2020 09 01 00 09 30.000000 2 3.191537036075e-04 1.003535228896e-10 +AS G27 2020 09 01 00 09 30.000000 2 -3.850073094370e-04 1.006205809339e-10 +AS R01 2020 09 01 00 09 30.000000 2 6.669234625633e-05 1.003458133690e-10 +AS R17 2020 09 01 00 09 30.000000 2 3.541423105803e-04 1.003624868761e-10 +AS R23 2020 09 01 00 09 30.000000 2 3.288163997233e-04 1.004825723467e-10 +AS J01 2020 09 01 00 09 30.000000 2 -1.835352539978e-04 1.004075316465e-10 +AR CHPI 2020 09 01 00 09 30.000000 2 -3.015797354555e-04 1.008429392441e-10 +AR GLPS 2020 09 01 00 09 30.000000 2 -1.932527618547e-07 1.009252018659e-10 +AR KITG 2020 09 01 00 09 30.000000 2 -1.578637217069e-05 1.006042395320e-10 +AR OWMG 2020 09 01 00 09 30.000000 2 -2.089142309883e-07 1.005380550580e-10 diff --git a/src/test/resources/gnss/clock/part-3.clk b/src/test/resources/gnss/clock/part-3.clk new file mode 100644 index 0000000000..abbeacc0cf --- /dev/null +++ b/src/test/resources/gnss/clock/part-3.clk @@ -0,0 +1,118 @@ + 3.00 C M RINEX VERSION / TYPE +MADOCA 0.9.3 20200903 141939 UTC PGM / RUN BY / DATE +MADOCA FINAL COMMENT + GPS TIME SYSTEM ID + P1C1_RINEX_20200901.DCB SYS / DCBS APPLIED + igs14_20200901.atx SYS / PCVS APPLIED + 2 AR AS # / TYPES OF DATA + ANALYSIS CENTER + 1 2020 9 1 0 10 0.000000 2020 9 1 0 14 30.000000# OF CLK REF + 5 IGS14 # OF SOLN STA / TRF +CHPI 41609M003 4164613909 -4162456992 -2445028632SOLN STA NAME / NUM +GLPS 42005M002 -33800869 -6377516528 -82154223SOLN STA NAME / NUM +KITG 12334M004 1944879627 4556783828 4004205962SOLN STA NAME / NUM +NOVM 12367M002 452260696 3635877614 5203453343SOLN STA NAME / NUM +OWMG 50253M004 -4584394280 -290931596 -4410047891SOLN STA NAME / NUM + 5 # OF SOLN SATS +G17 G27 E11 R17 R23 J01 PRN LIST + END OF HEADER +AS G17 2020 09 01 00 10 0.000000 2 3.191539476989e-04 1.003519203611e-10 +AS G27 2020 09 01 00 10 0.000000 2 -3.850075828601e-04 1.006204915751e-10 +AS R17 2020 09 01 00 10 0.000000 2 3.541423796558e-04 1.003627791465e-10 +AS R23 2020 09 01 00 10 0.000000 2 3.288165788277e-04 1.004833161376e-10 +AS J01 2020 09 01 00 10 0.000000 2 -1.835351517958e-04 1.004035331153e-10 +AR CHPI 2020 09 01 00 10 0.000000 2 -3.015807467052e-04 1.008428640818e-10 +AR GLPS 2020 09 01 00 10 0.000000 2 -1.932728089481e-07 1.009247549595e-10 +AR KITG 2020 09 01 00 10 0.000000 2 -1.578837106566e-05 1.006046522012e-10 +AR NOVM 2020 09 01 00 10 0.000000 2 -4.536669712885e-04 1.004448185346e-10 +AR OWMG 2020 09 01 00 10 0.000000 2 -2.091625199853e-07 1.005384874360e-10 +AS G17 2020 09 01 00 10 30.000000 2 3.191541140895e-04 1.003525878403e-10 +AS G27 2020 09 01 00 10 30.000000 2 -3.850078665449e-04 1.006222000355e-10 +AS R17 2020 09 01 00 10 30.000000 2 3.541425150532e-04 1.003632134040e-10 +AS R23 2020 09 01 00 10 30.000000 2 3.288166417673e-04 1.004728880616e-10 +AS J01 2020 09 01 00 10 30.000000 2 -1.835350512986e-04 1.004078711848e-10 +AR CHPI 2020 09 01 00 10 30.000000 2 -3.015787869716e-04 1.008418338547e-10 +AR GLPS 2020 09 01 00 10 30.000000 2 -1.936087154966e-07 1.005796171724e-10 +AR KITG 2020 09 01 00 10 30.000000 2 -1.578560346945e-05 1.006051473804e-10 +AR NOVM 2020 09 01 00 10 30.000000 2 -4.536669712722e-04 1.004453612877e-10 +AR OWMG 2020 09 01 00 10 30.000000 2 -2.093224793532e-07 1.005381465183e-10 +AS G17 2020 09 01 00 11 0.000000 2 3.191542569160e-04 1.003530295845e-10 +AS G27 2020 09 01 00 11 0.000000 2 -3.850081278082e-04 1.006193014799e-10 +AS R17 2020 09 01 00 11 0.000000 2 3.541425870097e-04 1.003638309564e-10 +AS R23 2020 09 01 00 11 0.000000 2 3.288167422088e-04 1.004734379064e-10 +AS J01 2020 09 01 00 11 0.000000 2 -1.835349388158e-04 1.004039789827e-10 +AR CHPI 2020 09 01 00 11 0.000000 2 -3.015798356443e-04 1.008420560072e-10 +AR GLPS 2020 09 01 00 11 0.000000 2 -1.931615617620e-07 1.005799241332e-10 +AR KITG 2020 09 01 00 11 0.000000 2 -1.578802569697e-05 1.006056863397e-10 +AR NOVM 2020 09 01 00 11 0.000000 2 -4.536669492214e-04 1.004458547053e-10 +AR OWMG 2020 09 01 00 11 0.000000 2 -2.094982739814e-07 1.005370940477e-10 +AS G17 2020 09 01 00 11 30.000000 2 3.191544105051e-04 1.003531347379e-10 +AS G27 2020 09 01 00 11 30.000000 2 -3.850084034000e-04 1.006186949196e-10 +AS R17 2020 09 01 00 11 30.000000 2 3.541427420011e-04 1.003648624780e-10 +AS R23 2020 09 01 00 11 30.000000 2 3.288168378314e-04 1.004739041984e-10 +AS J01 2020 09 01 00 11 30.000000 2 -1.835348275825e-04 1.004083901126e-10 +AR CHPI 2020 09 01 00 11 30.000000 2 -3.015804247221e-04 1.008421422499e-10 +AR GLPS 2020 09 01 00 11 30.000000 2 -1.932643043190e-07 1.005800904234e-10 +AR KITG 2020 09 01 00 11 30.000000 2 -1.578740267855e-05 1.006061361245e-10 +AR NOVM 2020 09 01 00 11 30.000000 2 -4.536669467852e-04 1.004449613356e-10 +AR OWMG 2020 09 01 00 11 30.000000 2 -2.097470314856e-07 1.005370176143e-10 +AS G17 2020 09 01 00 12 0.000000 2 3.191546204244e-04 1.003536854480e-10 +AS G27 2020 09 01 00 12 0.000000 2 -3.850086741758e-04 1.006195093840e-10 +AS R17 2020 09 01 00 12 0.000000 2 3.541428000929e-04 1.003645339994e-10 +AS R23 2020 09 01 00 12 0.000000 2 3.288169716011e-04 1.004749717382e-10 +AS J01 2020 09 01 00 12 0.000000 2 -1.835347230006e-04 1.004050885967e-10 +AR CHPI 2020 09 01 00 12 0.000000 2 -3.015809036880e-04 1.008424595678e-10 +AR GLPS 2020 09 01 00 12 0.000000 2 -1.933659114925e-07 1.005806070837e-10 +AR KITG 2020 09 01 00 12 0.000000 2 -1.578551241720e-05 1.006066980157e-10 +AR NOVM 2020 09 01 00 12 0.000000 2 -4.536669538634e-04 1.004454738359e-10 +AR OWMG 2020 09 01 00 12 0.000000 2 -2.098534196354e-07 1.005385150304e-10 +AS G17 2020 09 01 00 12 30.000000 2 3.191547027807e-04 1.003540088014e-10 +AS G27 2020 09 01 00 12 30.000000 2 -3.850089544124e-04 1.006180433427e-10 +AS R17 2020 09 01 00 12 30.000000 2 3.541428338787e-04 1.003648518970e-10 +AS R23 2020 09 01 00 12 30.000000 2 3.288170735487e-04 1.004754502924e-10 +AS J01 2020 09 01 00 12 30.000000 2 -1.835346574539e-04 1.004051094267e-10 +AR CHPI 2020 09 01 00 12 30.000000 2 -3.015800593604e-04 1.008425793176e-10 +AR GLPS 2020 09 01 00 12 30.000000 2 -1.935132076188e-07 1.005809194800e-10 +AR KITG 2020 09 01 00 12 30.000000 2 -1.578649179742e-05 1.006071989382e-10 +AR NOVM 2020 09 01 00 12 30.000000 2 -4.536669412829e-04 1.004458284202e-10 +AR OWMG 2020 09 01 00 12 30.000000 2 -2.098123785883e-07 1.005357517619e-10 +AS G17 2020 09 01 00 13 0.000000 2 3.191548046052e-04 1.003559464138e-10 +AS G27 2020 09 01 00 13 0.000000 2 -3.850092210317e-04 1.006180444002e-10 +AS R17 2020 09 01 00 13 0.000000 2 3.541429092273e-04 1.003653774834e-10 +AS R23 2020 09 01 00 13 0.000000 2 3.288172047684e-04 1.004763825952e-10 +AS J01 2020 09 01 00 13 0.000000 2 -1.835345502416e-04 1.004053060811e-10 +AR CHPI 2020 09 01 00 13 0.000000 2 -3.015811820236e-04 1.008428343168e-10 +AR GLPS 2020 09 01 00 13 0.000000 2 -1.934493750037e-07 1.005813637151e-10 +AR KITG 2020 09 01 00 13 0.000000 2 -1.578578489752e-05 1.005991864180e-10 +AR NOVM 2020 09 01 00 13 0.000000 2 -4.536669309938e-04 1.004464693598e-10 +AR OWMG 2020 09 01 00 13 0.000000 2 -2.097878200832e-07 1.005357132244e-10 +AS G17 2020 09 01 00 13 30.000000 2 3.191549891936e-04 1.003548928540e-10 +AS G27 2020 09 01 00 13 30.000000 2 -3.850094963049e-04 1.006070199184e-10 +AS R17 2020 09 01 00 13 30.000000 2 3.541430072312e-04 1.003656974280e-10 +AS R23 2020 09 01 00 13 30.000000 2 3.288172361588e-04 1.004784720541e-10 +AS J01 2020 09 01 00 13 30.000000 2 -1.835344304954e-04 1.004056115242e-10 +AR CHPI 2020 09 01 00 13 30.000000 2 -3.015815536223e-04 1.008429539484e-10 +AR GLPS 2020 09 01 00 13 30.000000 2 -1.930518299970e-07 1.005816462227e-10 +AR KITG 2020 09 01 00 13 30.000000 2 -1.578582312144e-05 1.005993150501e-10 +AR NOVM 2020 09 01 00 13 30.000000 2 -4.536669166955e-04 1.004467482242e-10 +AR OWMG 2020 09 01 00 13 30.000000 2 -2.097065438710e-07 1.005333882545e-10 +AS G17 2020 09 01 00 14 0.000000 2 3.191551130335e-04 1.003553721029e-10 +AS G27 2020 09 01 00 14 0.000000 2 -3.850097677296e-04 1.006061693789e-10 +AS R17 2020 09 01 00 14 0.000000 2 3.541430855844e-04 1.003659394530e-10 +AS R23 2020 09 01 00 14 0.000000 2 3.288173102712e-04 1.004791256377e-10 +AS J01 2020 09 01 00 14 0.000000 2 -1.835343334595e-04 1.004055266441e-10 +AR CHPI 2020 09 01 00 14 0.000000 2 -3.015804753248e-04 1.008444295329e-10 +AR GLPS 2020 09 01 00 14 0.000000 2 -1.931810934866e-07 1.005826282539e-10 +AR KITG 2020 09 01 00 14 0.000000 2 -1.578872359770e-05 1.005995653906e-10 +AR NOVM 2020 09 01 00 14 0.000000 2 -4.536669270215e-04 1.004472247174e-10 +AR OWMG 2020 09 01 00 14 0.000000 2 -2.097129135653e-07 1.005330425148e-10 +AS G17 2020 09 01 00 14 30.000000 2 3.191552633074e-04 1.003558514568e-10 +AS G27 2020 09 01 00 14 30.000000 2 -3.850100398045e-04 1.006060533917e-10 +AS R17 2020 09 01 00 14 30.000000 2 3.541431780091e-04 1.003667258346e-10 +AS R23 2020 09 01 00 14 30.000000 2 3.288173426393e-04 1.004799597387e-10 +AS J01 2020 09 01 00 14 30.000000 2 -1.835342315546e-04 1.004062769841e-10 +AR CHPI 2020 09 01 00 14 30.000000 2 -3.015796370902e-04 1.008450180514e-10 +AR GLPS 2020 09 01 00 14 30.000000 2 -1.930810083130e-07 1.005831230834e-10 +AR KITG 2020 09 01 00 14 30.000000 2 -1.578676149093e-05 1.005999285954e-10 +AR NOVM 2020 09 01 00 14 30.000000 2 -4.536669229983e-04 1.004465720934e-10 +AR OWMG 2020 09 01 00 14 30.000000 2 -2.098821556072e-07 1.005332071595e-10 diff --git a/src/test/resources/gpt-grid/README b/src/test/resources/gpt-grid/README new file mode 100644 index 0000000000..3e0fb6dc4c --- /dev/null +++ b/src/test/resources/gpt-grid/README @@ -0,0 +1,2 @@ +The files in this folder are not original GPT grid files +they have been edited for test purposes and should not be used except for tests diff --git a/src/test/resources/gpt-grid/corrupted-bad-data-gpt3_15.grd b/src/test/resources/gpt-grid/corrupted-bad-data-gpt3_15.grd new file mode 100644 index 0000000000..7a55f500fd --- /dev/null +++ b/src/test/resources/gpt-grid/corrupted-bad-data-gpt3_15.grd @@ -0,0 +1,9 @@ +% lat lon p:a0 A1 B1 A2 B2 T:a0 A1 B1 A2 B2 Q:a0 A1 B1 A2 B2 dT:a0 A1 B1 A2 B2 undu Hs a_h:a0 A1 B1 A2 B2 a_w:a0 A1 B1 A2 B2 lambda:a0 A1 B1 A2 B2 Tm:a0 A1 B1 A2 B2 Gn_h:a0 A1 B1 A2 B2 Ge_h:a0 A1 B1 A2 B2 Gn_w:a0 A1 B1 A2 B2 Ge_w:a0 A1 B1 A2 B2 + 82.5 7.5 101260 -239 308 -169 -108 261.7 -10.6 -5.2 2.7 0.3 1.88 -1.45 -0.64 0.49 0.28 -0.0 3.9 1.4 -1.0 1.4 29.80 -0.00 1.18699 -0.03410 -0.01032 0.00476 0.00373 0.56673 -0.00107 0.00343 -0.00174 0.00244 1.9560 -0.4181 -0.1116 0.1128 -0.1728 257.0 -8.4 -4.0 2.4 1.2 -13.52 0.98 2.64 -0.11 0.76 -7.81 0.19 -0.26 1.93 0.69 -1.82 0.03 0.45 0.11 -0.11 0.62 -0.04 -0.38 0.13 0.20 + 82.5 22.5 101209 -249 315 -141 -97 262.5 -9.8 -5.5 2.7 0.2 1.95 -1.39 -0.70 0.47 0.28 -0.8 3.2 1.9 -1.3 1.6 25.99 -0.00 1.18640 -0.03410 -0.01049 0.00484 0.00368 0.56361 -0.00524 0.00420 -0.00069 0.00276 2.0569 -0.2638 -0.1576 0.1266 -0.2021 257.2 -8.0 -4.1 2.3 1.2 -10.48 1.55 3.24 -0.60 0.81 -8.03 0.79 -0.19 1.99 0.79 -1.78 0.17 0.42 0.05 -0.12 -0.17 -0.05 -0.22 -0.07 0.34 + 82.5 37.5 101198 -203 328 -118 -95 262.5 -9.8 -5.7 2.5 0.3 1.93 -1.38 -0.72 0.45 0.29 -1.3 2.9 2.1 -1.0 1.6 20.77 -0.00 1.18676 -0.03459 -0.01070 0.00495 0.00381 0.56340 -0.00476 0.00381 -0.00118 0.00305 2.0776 -0.2508 -0.1828 0.0898 -0.2222 257.1 -8.1 -4.2 2.3 1.3 -8.35 2.17 3.72 -1.31 0.73 -9.47 1.07 0.05 2.02 1.28 -1.67 0.41 0.41 -0.31 -0.31 -0.54 -0.24 -0.19 0.14 0.47 + 82.5 52.5 101202 -129 343 -93 -88 262.4 -9.8 -5.7 2.3 0.4 1.91 -1.38 -0.72 0.42 0.31 -1.9 2.3 1.8 -0.9 1.6 17.04 -0.00 1.18547 -0.03525 -0.01080 0.00505 0.00398 0.56358 -0.00458 0.00485 0.00101 0.00295 2.1030 -0.2041 -0.1650 0.0329 -0.2203 256.8 -8.3 -4.3 2.2 1.3 -4.39 2.67 4.09 -2.08 0.21 -10.21 1.42 0.51 1.54 1.87 -1.35 0.57 0.38 -0.29 -0.53 -0.50 -0.78 0.03 0.25 0.34 + 82.5 67.5 dummy -27 368 -70 -72 261.6 -10.7 -6.2 2.1 0.3 1.83 -1.44 -0.76 0.41 0.32 -1.1 3.4 2.6 -1.0 2.0 12.16 -0.00 1.18547 -0.03609 -0.01088 0.00503 0.00415 0.56726 -0.00298 0.00804 0.00152 0.00583 1.9777 -0.3149 -0.2799 0.0275 -0.2594 256.3 -8.7 -4.4 2.1 1.3 1.40 3.21 4.49 -2.85 -0.75 -4.79 2.32 1.20 0.53 1.81 -1.03 0.57 0.40 -0.20 -0.60 -0.42 -0.75 0.10 -0.08 0.14 + 82.5 82.5 101270 82 392 -57 -55 261.2 -11.3 -6.3 1.8 0.3 1.79 -1.50 -0.77 0.40 0.34 -0.8 4.0 2.6 -0.4 1.9 8.58 -0.00 1.18427 -0.03677 -0.01081 0.00495 0.00426 0.56972 -0.00031 0.00976 0.00277 0.00603 1.9214 -0.3769 -0.3040 -0.0205 -0.2414 255.9 -9.2 -4.6 1.9 1.3 2.35 3.13 4.48 -3.38 -1.66 -4.00 3.29 1.85 -0.57 0.76 -1.16 1.17 0.59 -0.31 -0.65 -0.32 -0.62 0.31 0.04 -0.31 + 82.5 97.5 101329 185 420 -61 -40 260.7 -11.9 -6.3 1.6 0.1 1.76 -1.55 -0.76 0.40 0.33 -0.3 5.0 2.8 -0.1 1.9 6.42 -0.00 1.18371 -0.03715 -0.01067 0.00488 0.00417 0.57154 0.00402 0.01183 0.00251 0.00641 1.8637 -0.4648 -0.3231 -0.0552 -0.2292 255.6 -9.6 -4.6 1.8 1.2 1.05 2.61 4.05 -3.60 -1.86 -0.05 4.01 2.34 -1.68 -0.89 -0.81 0.97 0.46 -0.16 -0.29 -0.31 -0.23 0.22 0.09 -0.56 + 82.5 112.5 101393 273 445 -81 -37 260.4 -12.4 -6.4 1.6 0.0 1.74 -1.59 -0.76 0.40 0.32 -0.1 5.2 3.3 -0.2 2.0 6.03 -0.00 1.18403 -0.03719 -0.01043 0.00484 0.00397 0.57321 0.00477 0.01246 0.00438 0.00583 1.8270 -0.5105 -0.3435 -0.0747 -0.2249 255.5 -9.8 -4.6 1.7 1.0 1.93 1.92 3.52 -3.20 -1.71 3.58 4.49 2.43 -2.77 -2.00 -0.53 0.75 0.24 -0.25 -0.11 -0.08 -0.30 0.15 0.07 -0.33 diff --git a/src/test/resources/gpt-grid/corrupted-incomplete-header.grd b/src/test/resources/gpt-grid/corrupted-incomplete-header.grd new file mode 100644 index 0000000000..5a5eb55067 --- /dev/null +++ b/src/test/resources/gpt-grid/corrupted-incomplete-header.grd @@ -0,0 +1,4 @@ +% lat lon p:a0 A1 B1 A2 B2 T:a0 A1 B1 A2 B2 ##:a0 A1 B1 A2 B2 dT:a0 A1 B1 A2 B2 undu Hs h:a0 A1 B1 A2 B2 w:a0 A1 B1 A2 B2 + 82.5 7.5 101260 -239 308 -169 -108 261.7 -10.6 -5.2 2.7 0.3 1.88 -1.45 -0.64 0.49 0.28 -0.0 3.9 1.4 -1.0 1.4 29.80 -0.00 1.1924 -0.0322 -0.0125 0.0052 0.0034 0.5710 -0.0023 0.0030 -0.0018 0.0029 + 82.5 22.5 101209 -249 315 -141 -97 262.5 -9.8 -5.5 2.7 0.2 1.95 -1.39 -0.70 0.47 0.28 -0.8 3.2 1.9 -1.3 1.6 25.99 -0.00 1.1923 -0.0321 -0.0126 0.0052 0.0034 0.5672 -0.0062 0.0034 -0.0009 0.0032 + 82.5 37.5 101198 -203 328 -118 -95 262.5 -9.8 -5.7 2.5 0.3 1.93 -1.38 -0.72 0.45 0.29 -1.3 2.9 2.1 -1.0 1.6 20.77 -0.00 1.1918 -0.0324 -0.0128 0.0052 0.0035 0.5668 -0.0063 0.0035 -0.0008 0.0032 diff --git a/src/test/resources/gpt-grid/corrupted-irregular-grid-gpt3_15.grd b/src/test/resources/gpt-grid/corrupted-irregular-grid-gpt3_15.grd new file mode 100644 index 0000000000..97197a89c9 --- /dev/null +++ b/src/test/resources/gpt-grid/corrupted-irregular-grid-gpt3_15.grd @@ -0,0 +1,5 @@ +% lat lon p:a0 A1 B1 A2 B2 T:a0 A1 B1 A2 B2 Q:a0 A1 B1 A2 B2 dT:a0 A1 B1 A2 B2 undu Hs a_h:a0 A1 B1 A2 B2 a_w:a0 A1 B1 A2 B2 lambda:a0 A1 B1 A2 B2 Tm:a0 A1 B1 A2 B2 Gn_h:a0 A1 B1 A2 B2 Ge_h:a0 A1 B1 A2 B2 Gn_w:a0 A1 B1 A2 B2 Ge_w:a0 A1 B1 A2 B2 + 37.5 67.5 94606 790 43 -97 -166 292.7 -12.3 -3.6 -0.9 0.2 6.25 -2.57 0.29 0.07 -0.17 -6.0 2.5 0.1 1.1 -0.0 -40.60 594.09 1.24690 -0.02756 -0.00966 0.00009 0.00195 0.53822 -0.00003 0.00012 0.00190 -0.00318 2.7011 0.2488 0.1934 -0.0404 -0.0308 279.8 -10.1 -3.1 -0.6 0.3 -21.98 -3.23 -1.12 -0.89 -1.33 -2.38 3.47 2.16 -0.06 0.23 13.29 -24.20 -7.62 6.05 3.47 10.61 -12.35 -2.86 4.68 2.73 + 37.5 82.5 87775 665 -89 -57 -143 289.6 -13.9 -2.7 -2.3 0.1 2.63 -1.53 -0.50 0.28 0.28 -5.0 2.3 -0.1 0.4 0.1 -53.38 1221.77 1.24144 -0.02860 -0.00882 -0.00018 0.00259 0.57927 -0.02498 -0.00457 0.00473 0.00818 1.5222 0.2078 0.0075 -0.0469 -0.1070 272.9 -8.7 -2.5 -0.8 0.0 -29.50 -4.95 -0.74 -1.27 -0.47 -1.46 -1.93 -0.98 0.21 0.95 -10.95 11.12 6.40 -0.68 -8.36 -1.53 -0.47 -0.83 0.63 1.12 + 22.5 67.4999 100875 756 103 -67 -33 299.1 -2.7 -1.1 -0.2 -1.1 15.97 -4.65 -0.68 -1.27 -0.76 -5.7 1.9 1.4 -2.8 -0.4 -50.06 0.00 1.27561 -0.01025 -0.00306 -0.00058 -0.00114 0.56123 -0.04885 -0.01917 0.01792 0.02637 5.8978 1.5981 1.6607 -1.5429 -0.8005 289.7 -2.0 0.0 -1.3 -2.1 -2.22 -8.26 -6.18 -0.25 -0.27 -2.78 0.13 -0.14 0.28 -0.24 -9.15 9.58 9.54 -0.95 0.55 9.69 -12.21 -10.31 0.39 3.72 + 22.5 82.5 95697 718 17 -15 -30 299.7 -4.5 2.6 -2.4 -2.0 11.86 -4.85 -3.71 0.35 1.42 -5.5 1.3 0.1 0.2 0.0 -60.13 449.47 1.27241 -0.00973 -0.00162 -0.00084 -0.00191 0.57379 -0.06771 -0.01486 0.00308 0.01558 2.7599 0.8824 -0.2252 0.1690 -0.1666 286.9 -1.4 0.7 -1.1 -1.3 -7.62 -9.13 -6.75 -0.22 0.60 -0.20 0.03 -0.87 0.05 0.34 -8.18 -1.21 1.38 6.05 6.88 3.28 -5.19 -3.98 -2.83 -0.72 diff --git a/src/test/resources/gpt-grid/corrupted-missing-data-fields-gpt3_15.grd b/src/test/resources/gpt-grid/corrupted-missing-data-fields-gpt3_15.grd new file mode 100644 index 0000000000..7431165a5c --- /dev/null +++ b/src/test/resources/gpt-grid/corrupted-missing-data-fields-gpt3_15.grd @@ -0,0 +1,5 @@ +% lat lon p:a0 A1 B1 A2 B2 T:a0 A1 B1 A2 B2 Q:a0 A1 B1 A2 B2 dT:a0 A1 B1 A2 B2 undu Hs a_h:a0 A1 B1 A2 B2 a_w:a0 A1 B1 A2 B2 lambda:a0 A1 B1 A2 B2 Tm:a0 A1 B1 A2 B2 Gn_h:a0 A1 B1 A2 B2 Ge_h:a0 A1 B1 A2 B2 Gn_w:a0 A1 B1 A2 B2 Ge_w:a0 A1 B1 A2 B2 + 82.5 7.5 101260 -239 308 -169 -108 261.7 -10.6 -5.2 2.7 0.3 1.88 -1.45 -0.64 0.49 0.28 -0.0 3.9 1.4 -1.0 1.4 29.80 -0.00 1.18699 -0.03410 -0.01032 0.00476 0.00373 0.56673 -0.00107 0.00343 -0.00174 0.00244 1.9560 -0.4181 -0.1116 0.1128 -0.1728 257.0 -8.4 -4.0 2.4 1.2 -13.52 0.98 2.64 -0.11 0.76 -7.81 0.19 -0.26 1.93 0.69 -1.82 0.03 0.45 0.11 -0.11 0.62 -0.04 -0.38 0.13 0.20 + 82.5 22.5 101209 -249 315 -141 -97 262.5 -9.8 -5.5 2.7 0.2 1.95 -1.39 -0.70 0.47 0.28 -0.8 3.2 1.9 -1.3 1.6 25.99 -0.00 1.18640 -0.03410 -0.01049 0.00484 0.00368 0.56361 -0.00524 0.00420 -0.00069 0.00276 2.0569 -0.2638 -0.1576 0.1266 -0.2021 257.2 -8.0 -4.1 2.3 1.2 -10.48 1.55 3.24 -0.60 0.81 -8.03 0.79 -0.19 1.99 0.79 -1.78 0.17 0.42 0.05 -0.12 -0.17 -0.05 -0.22 -0.07 0.34 + 82.5 37.5 101198 -203 328 -118 -95 262.5 -9.8 -5.7 2.5 0.3 1.93 -1.38 -0.72 0.45 0.29 -1.3 2.9 2.1 -1.0 1.6 20.77 -0.00 1.18676 -0.03459 -0.01070 + 82.5 52.5 101202 -129 343 -93 -88 262.4 -9.8 -5.7 2.3 0.4 1.91 -1.38 -0.72 0.42 0.31 -1.9 2.3 1.8 -0.9 1.6 17.04 -0.00 1.18547 -0.03525 -0.01080 0.00505 0.00398 0.56358 -0.00458 0.00485 0.00101 0.00295 2.1030 -0.2041 -0.1650 0.0329 -0.2203 256.8 -8.3 -4.3 2.2 1.3 -4.39 2.67 4.09 -2.08 0.21 -10.21 1.42 0.51 1.54 1.87 -1.35 0.57 0.38 -0.29 -0.53 -0.50 -0.78 0.03 0.25 0.34 diff --git a/src/test/resources/gpt-grid/corrupted-missing-seasonal-columns-gpt3_15.grd b/src/test/resources/gpt-grid/corrupted-missing-seasonal-columns-gpt3_15.grd new file mode 100644 index 0000000000..f5959d49d1 --- /dev/null +++ b/src/test/resources/gpt-grid/corrupted-missing-seasonal-columns-gpt3_15.grd @@ -0,0 +1,5 @@ +% lat lon p:a0 A1 B1 A2 B2 T:a0 A1 B1 A2 B2 Q:a0 A1 B1 dT:a0 A1 B1 A2 B2 undu Hs a_h:a0 A1 B1 A2 B2 a_w:a0 A1 B1 A2 B2 lambda:a0 A1 B1 A2 B2 Tm:a0 A1 B1 A2 B2 Gn_h:a0 A1 B1 A2 B2 Ge_h:a0 A1 B1 A2 B2 Gn_w:a0 A1 B1 A2 B2 Ge_w:a0 A1 B1 A2 B2 + 82.5 7.5 101260 -239 308 -169 -108 261.7 -10.6 -5.2 2.7 0.3 1.88 -1.45 -0.64 -0.0 3.9 1.4 -1.0 1.4 29.80 -0.00 1.18699 -0.03410 -0.01032 0.00476 0.00373 0.56673 -0.00107 0.00343 -0.00174 0.00244 1.9560 -0.4181 -0.1116 0.1128 -0.1728 257.0 -8.4 -4.0 2.4 1.2 -13.52 0.98 2.64 -0.11 0.76 -7.81 0.19 -0.26 1.93 0.69 -1.82 0.03 0.45 0.11 -0.11 0.62 -0.04 -0.38 0.13 0.20 + 82.5 22.5 101209 -249 315 -141 -97 262.5 -9.8 -5.5 2.7 0.2 1.95 -1.39 -0.70 -0.8 3.2 1.9 -1.3 1.6 25.99 -0.00 1.18640 -0.03410 -0.01049 0.00484 0.00368 0.56361 -0.00524 0.00420 -0.00069 0.00276 2.0569 -0.2638 -0.1576 0.1266 -0.2021 257.2 -8.0 -4.1 2.3 1.2 -10.48 1.55 3.24 -0.60 0.81 -8.03 0.79 -0.19 1.99 0.79 -1.78 0.17 0.42 0.05 -0.12 -0.17 -0.05 -0.22 -0.07 0.34 + 82.5 37.5 101198 -203 328 -118 -95 262.5 -9.8 -5.7 2.5 0.3 1.93 -1.38 -0.72 -1.3 2.9 2.1 -1.0 1.6 20.77 -0.00 1.18676 -0.03459 -0.01070 0.00495 0.00381 0.56340 -0.00476 0.00381 -0.00118 0.00305 2.0776 -0.2508 -0.1828 0.0898 -0.2222 257.1 -8.1 -4.2 2.3 1.3 -8.35 2.17 3.72 -1.31 0.73 -9.47 1.07 0.05 2.02 1.28 -1.67 0.41 0.41 -0.31 -0.31 -0.54 -0.24 -0.19 0.14 0.47 + 82.5 52.5 101202 -129 343 -93 -88 262.4 -9.8 -5.7 2.3 0.4 1.91 -1.38 -0.72 -1.9 2.3 1.8 -0.9 1.6 17.04 -0.00 1.18547 -0.03525 -0.01080 0.00505 0.00398 0.56358 -0.00458 0.00485 0.00101 0.00295 2.1030 -0.2041 -0.1650 0.0329 -0.2203 256.8 -8.3 -4.3 2.2 1.3 -4.39 2.67 4.09 -2.08 0.21 -10.21 1.42 0.51 1.54 1.87 -1.35 0.57 0.38 -0.29 -0.53 -0.50 -0.78 0.03 0.25 0.34 diff --git a/src/test/resources/gpt2-grid/gpt2_15.grd b/src/test/resources/gpt-grid/gpt2_15.grd similarity index 99% rename from src/test/resources/gpt2-grid/gpt2_15.grd rename to src/test/resources/gpt-grid/gpt2_15.grd index 96d691e553..4178750ad0 100644 --- a/src/test/resources/gpt2-grid/gpt2_15.grd +++ b/src/test/resources/gpt-grid/gpt2_15.grd @@ -1,4 +1,3 @@ -% This is not an original GPT2 grid, it has been edited for test purposes and should not be used except for test % lat lon p:a0 A1 B1 A2 B2 T:a0 A1 B1 A2 B2 Q:a0 A1 B1 A2 B2 dT:a0 A1 B1 A2 B2 undu Hs h:a0 A1 B1 A2 B2 w:a0 A1 B1 A2 B2 82.5 7.5 101260 -239 308 -169 -108 261.7 -10.6 -5.2 2.7 0.3 1.88 -1.45 -0.64 0.49 0.28 -0.0 3.9 1.4 -1.0 1.4 29.80 -0.00 1.1924 -0.0322 -0.0125 0.0052 0.0034 0.5710 -0.0023 0.0030 -0.0018 0.0029 82.5 22.5 101209 -249 315 -141 -97 262.5 -9.8 -5.5 2.7 0.2 1.95 -1.39 -0.70 0.47 0.28 -0.8 3.2 1.9 -1.3 1.6 25.99 -0.00 1.1923 -0.0321 -0.0126 0.0052 0.0034 0.5672 -0.0062 0.0034 -0.0009 0.0032 diff --git a/src/test/resources/gpt-grid/gpt2_15w.grd b/src/test/resources/gpt-grid/gpt2_15w.grd new file mode 100644 index 0000000000..ef137799d5 --- /dev/null +++ b/src/test/resources/gpt-grid/gpt2_15w.grd @@ -0,0 +1,289 @@ +% lat lon p:a0 A1 B1 A2 B2 T:a0 A1 B1 A2 B2 Q:a0 A1 B1 A2 B2 dT:a0 A1 B1 A2 B2 undu Hs h:a0 A1 B1 A2 B2 w:a0 A1 B1 A2 B2 lambda:a0 A1 B1 A2 B2 Tm:a0 A1 B1 A2 B2 + 82.5 7.5 101260 -239 308 -169 -108 261.7 -10.6 -5.2 2.7 0.3 1.88 -1.45 -0.64 0.49 0.28 -0.0 3.9 1.4 -1.0 1.4 29.80 -0.00 1.1924 -0.0322 -0.0125 0.0052 0.0034 0.5710 -0.0023 0.0030 -0.0018 0.0029 1.9560 -0.4181 -0.1116 0.1128 -0.1728 257.0 -8.4 -4.0 2.4 1.2 + 82.5 22.5 101209 -249 315 -141 -97 262.5 -9.8 -5.5 2.7 0.2 1.95 -1.39 -0.70 0.47 0.28 -0.8 3.2 1.9 -1.3 1.6 25.99 -0.00 1.1923 -0.0321 -0.0126 0.0052 0.0034 0.5672 -0.0062 0.0034 -0.0009 0.0032 2.0569 -0.2638 -0.1576 0.1266 -0.2021 257.2 -8.0 -4.1 2.3 1.2 + 82.5 37.5 101198 -203 328 -118 -95 262.5 -9.8 -5.7 2.5 0.3 1.93 -1.38 -0.72 0.45 0.29 -1.3 2.9 2.1 -1.0 1.6 20.77 -0.00 1.1918 -0.0324 -0.0128 0.0052 0.0035 0.5668 -0.0063 0.0035 -0.0008 0.0032 2.0776 -0.2508 -0.1828 0.0898 -0.2222 257.1 -8.1 -4.2 2.3 1.3 + 82.5 52.5 101202 -129 343 -93 -88 262.4 -9.8 -5.7 2.3 0.4 1.91 -1.38 -0.72 0.42 0.31 -1.9 2.3 1.8 -0.9 1.6 17.04 -0.00 1.1912 -0.0329 -0.0129 0.0052 0.0037 0.5673 -0.0060 0.0043 0.0008 0.0033 2.1030 -0.2041 -0.1650 0.0329 -0.2203 256.8 -8.3 -4.3 2.2 1.3 + 82.5 67.5 101230 -27 368 -70 -72 261.6 -10.7 -6.2 2.1 0.3 1.83 -1.44 -0.76 0.41 0.32 -1.1 3.4 2.6 -1.0 2.0 12.16 -0.00 1.1905 -0.0336 -0.0130 0.0051 0.0038 0.5703 -0.0041 0.0074 0.0018 0.0051 1.9777 -0.3149 -0.2799 0.0275 -0.2594 256.3 -8.7 -4.4 2.1 1.3 + 82.5 82.5 101270 82 392 -57 -55 261.2 -11.3 -6.3 1.8 0.3 1.79 -1.50 -0.77 0.40 0.34 -0.8 4.0 2.6 -0.4 1.9 8.58 -0.00 1.1899 -0.0342 -0.0130 0.0050 0.0038 0.5729 -0.0014 0.0095 0.0025 0.0059 1.9214 -0.3769 -0.3040 -0.0205 -0.2414 255.9 -9.2 -4.6 1.9 1.3 + 82.5 97.5 101329 185 420 -61 -40 260.7 -11.9 -6.3 1.6 0.1 1.76 -1.55 -0.76 0.40 0.33 -0.3 5.0 2.8 -0.1 1.9 6.42 -0.00 1.1894 -0.0347 -0.0130 0.0049 0.0036 0.5748 0.0016 0.0112 0.0028 0.0065 1.8637 -0.4648 -0.3231 -0.0552 -0.2292 255.6 -9.6 -4.6 1.8 1.2 + 82.5 112.5 101393 273 445 -81 -37 260.4 -12.4 -6.4 1.6 0.0 1.74 -1.59 -0.76 0.40 0.32 -0.1 5.2 3.3 -0.2 2.0 6.03 -0.00 1.1893 -0.0349 -0.0129 0.0048 0.0034 0.5763 0.0037 0.0119 0.0039 0.0064 1.8270 -0.5105 -0.3435 -0.0747 -0.2249 255.5 -9.8 -4.6 1.7 1.0 + 82.5 127.5 101461 336 460 -116 -48 260.5 -12.4 -6.3 1.5 0.1 1.73 -1.60 -0.75 0.40 0.31 -0.3 5.1 3.1 -0.2 1.8 4.83 -0.00 1.1894 -0.0349 -0.0127 0.0047 0.0032 0.5772 0.0053 0.0110 0.0037 0.0058 1.8286 -0.5075 -0.3288 -0.0616 -0.2261 255.4 -10.0 -4.6 1.7 0.9 + 82.5 142.5 101531 376 471 -149 -65 260.4 -12.6 -6.2 1.4 0.0 1.73 -1.62 -0.74 0.39 0.29 -0.1 5.4 3.1 0.1 1.7 7.02 0.00 1.1898 -0.0347 -0.0126 0.0047 0.0030 0.5787 0.0074 0.0101 0.0035 0.0056 1.7865 -0.5557 -0.3242 -0.0658 -0.2326 255.5 -10.1 -4.5 1.6 0.8 + 82.5 157.5 101599 393 477 -182 -84 260.3 -12.7 -6.2 1.5 -0.0 1.72 -1.63 -0.73 0.40 0.29 0.2 5.5 3.1 0.0 1.7 3.81 0.00 1.1902 -0.0346 -0.0124 0.0047 0.0030 0.5812 0.0081 0.0100 0.0037 0.0056 1.7415 -0.5856 -0.3322 -0.0694 -0.2513 255.5 -10.2 -4.5 1.7 0.8 + 82.5 172.5 101664 387 480 -208 -107 260.2 -12.7 -6.2 1.6 0.0 1.71 -1.63 -0.73 0.41 0.29 0.5 5.3 3.1 -0.1 1.6 4.40 0.00 1.1906 -0.0344 -0.0123 0.0048 0.0030 0.5831 0.0088 0.0106 0.0040 0.0052 1.7352 -0.5597 -0.3433 -0.0664 -0.2479 255.4 -10.3 -4.5 1.7 0.8 + 82.5 -172.5 101719 361 471 -220 -134 260.0 -12.9 -6.2 1.7 0.1 1.69 -1.64 -0.72 0.43 0.30 1.0 5.4 3.1 0.0 1.4 5.02 0.00 1.1907 -0.0344 -0.0122 0.0049 0.0030 0.5840 0.0095 0.0111 0.0037 0.0047 1.7124 -0.5970 -0.3451 -0.0599 -0.2331 255.4 -10.4 -4.5 1.8 0.8 + 82.5 -157.5 101751 314 444 -226 -158 259.6 -13.1 -6.2 1.9 0.0 1.66 -1.65 -0.70 0.45 0.32 1.5 5.8 2.7 -0.1 1.4 5.29 0.00 1.1905 -0.0345 -0.0122 0.0050 0.0029 0.5844 0.0100 0.0113 0.0034 0.0040 1.6769 -0.6479 -0.3504 -0.0326 -0.2207 255.2 -10.6 -4.5 1.9 0.8 + 82.5 -142.5 101749 254 409 -227 -176 259.1 -13.4 -6.3 2.0 0.0 1.62 -1.65 -0.69 0.47 0.33 1.9 5.8 2.6 -0.2 1.5 4.69 0.00 1.1902 -0.0346 -0.0123 0.0050 0.0029 0.5851 0.0110 0.0102 0.0027 0.0033 1.6346 -0.7046 -0.3518 -0.0008 -0.2237 255.0 -10.7 -4.5 2.0 0.9 + 82.5 -127.5 101709 193 382 -225 -183 258.9 -13.6 -6.4 2.1 0.1 1.61 -1.65 -0.70 0.48 0.34 1.9 5.7 2.4 0.0 1.3 5.35 0.00 1.1897 -0.0347 -0.0125 0.0050 0.0029 0.5849 0.0117 0.0092 0.0018 0.0028 1.6240 -0.7259 -0.3550 0.0225 -0.2151 254.8 -10.7 -4.6 2.1 0.9 + 82.5 -112.5 101652 136 368 -223 -178 258.4 -14.0 -6.7 2.3 0.1 1.59 -1.67 -0.71 0.49 0.36 2.3 6.1 2.4 -0.1 1.3 6.59 0.00 1.1892 -0.0349 -0.0128 0.0050 0.0030 0.5847 0.0130 0.0107 0.0003 0.0026 1.6063 -0.7934 -0.3832 0.0487 -0.2023 254.5 -10.8 -4.7 2.1 0.9 + 82.5 -97.5 101600 99 361 -232 -166 257.8 -14.5 -6.9 2.6 0.3 1.56 -1.69 -0.71 0.54 0.39 4.0 6.3 1.8 -0.4 1.3 11.67 -0.00 1.1888 -0.0353 -0.0131 0.0051 0.0031 0.5861 0.0148 0.0122 -0.0010 0.0036 1.5500 -0.8873 -0.3773 0.0966 -0.1847 254.4 -11.0 -4.9 2.3 1.0 + 82.5 -82.5 99050 -81 258 -220 -167 261.1 -13.2 -6.2 2.6 1.0 1.34 -1.58 -0.65 0.61 0.49 -4.5 0.6 -0.0 0.4 0.3 17.64 188.83 1.1891 -0.0355 -0.0130 0.0052 0.0033 0.5868 0.0208 0.0126 -0.0033 0.0012 1.2302 -0.9783 -0.3967 0.2244 0.0093 253.8 -11.2 -4.8 2.5 1.3 + 82.5 -67.5 92840 -376 108 -149 -128 258.9 -13.0 -6.0 2.8 1.2 1.43 -1.52 -0.65 0.58 0.47 0.6 2.6 1.6 -0.4 0.3 20.20 681.12 1.1898 -0.0352 -0.0126 0.0052 0.0036 0.5490 0.0095 0.0071 -0.0012 0.0009 1.9037 -0.8414 -0.3820 0.1871 0.0180 252.9 -10.5 -4.4 2.5 1.4 + 82.5 -52.5 100915 4 312 -252 -156 259.8 -14.5 -6.4 3.4 0.9 1.38 -1.60 -0.56 0.63 0.39 1.1 2.9 0.4 -1.2 0.7 20.43 48.17 1.1896 -0.0356 -0.0132 0.0055 0.0035 0.5980 0.0257 0.0115 -0.0028 0.0041 1.2764 -0.9954 -0.2442 0.2199 -0.1263 254.5 -11.3 -4.7 2.8 1.3 + 82.5 -37.5 90881 -488 62 -120 -103 260.1 -12.2 -5.0 3.0 1.4 1.26 -1.31 -0.50 0.51 0.35 -2.4 2.4 1.1 0.1 0.1 29.76 844.30 1.1910 -0.0345 -0.0122 0.0053 0.0037 0.5483 0.0121 0.0052 -0.0031 0.0012 1.6863 -0.8212 -0.2243 0.1605 -0.0381 252.8 -9.9 -4.0 2.7 1.4 + 82.5 -22.5 101250 -34 332 -257 -148 260.7 -13.3 -5.8 3.4 1.0 1.45 -1.62 -0.54 0.66 0.36 -4.1 0.4 0.7 -0.8 0.3 28.54 19.42 1.1908 -0.0345 -0.0129 0.0056 0.0036 0.5972 0.0218 0.0091 -0.0055 0.0025 1.2989 -1.0064 -0.2328 0.2887 -0.1259 255.3 -10.4 -4.4 2.9 1.4 + 82.5 -7.5 101382 -142 323 -212 -129 260.2 -12.1 -5.3 2.7 0.5 1.72 -1.56 -0.60 0.52 0.32 1.2 3.4 0.9 -0.5 1.5 29.74 -0.00 1.1917 -0.0332 -0.0127 0.0054 0.0035 0.5806 0.0074 0.0052 -0.0033 0.0029 1.7377 -0.6742 -0.1482 0.1247 -0.1428 256.3 -9.3 -4.2 2.6 1.3 + 67.5 7.5 100811 -511 83 -19 -49 279.3 -3.4 -2.5 0.2 0.8 4.84 -1.37 -1.03 0.17 0.48 -7.7 -1.6 -0.3 0.5 0.1 42.63 0.00 1.2163 -0.0233 -0.0107 0.0016 0.0024 0.5424 -0.0260 -0.0133 0.0031 0.0034 3.3427 0.3733 0.1801 -0.0878 -0.0187 268.3 -4.2 -2.5 0.6 0.9 + 67.5 22.5 97486 -333 54 -41 -22 275.0 -10.7 -3.7 1.2 0.5 3.88 -2.39 -1.28 0.47 0.65 -3.8 4.0 0.2 0.5 0.3 27.29 287.37 1.2139 -0.0278 -0.0121 0.0025 0.0021 0.5404 -0.0120 -0.0081 0.0023 0.0028 2.6473 -0.1842 -0.0657 -0.1127 -0.0108 265.6 -7.5 -3.5 1.2 0.7 + 67.5 37.5 98191 -322 75 -1 -9 273.8 -9.8 -4.4 1.1 0.4 3.74 -2.18 -1.38 0.40 0.60 -4.8 2.7 0.7 0.4 0.7 16.14 232.51 1.2117 -0.0294 -0.0129 0.0030 0.0020 0.5443 -0.0165 -0.0058 0.0044 0.0027 2.7153 0.0253 -0.1338 -0.1359 -0.0306 264.6 -7.7 -4.0 1.3 0.8 + 67.5 52.5 100337 -98 156 15 6 271.9 -11.8 -5.9 0.8 0.2 3.50 -2.46 -1.51 0.41 0.65 -3.1 4.1 1.7 0.8 1.1 2.81 61.95 1.2091 -0.0315 -0.0136 0.0035 0.0021 0.5578 -0.0111 -0.0021 0.0067 0.0048 2.3952 -0.3040 -0.2552 -0.1804 -0.1204 263.7 -8.9 -4.6 1.3 0.8 + 67.5 67.5 99392 68 181 40 21 268.4 -14.2 -6.9 0.9 0.9 3.02 -2.67 -1.40 0.56 0.71 -1.6 6.4 2.3 1.4 0.8 -6.67 142.69 1.2057 -0.0337 -0.0142 0.0041 0.0025 0.5616 0.0006 0.0030 0.0068 0.0020 2.0627 -0.7500 -0.3815 -0.2323 -0.0811 261.4 -10.3 -5.1 1.7 1.2 + 67.5 82.5 100480 411 194 89 30 266.7 -17.7 -7.3 0.9 1.4 2.97 -2.93 -1.36 0.66 0.80 0.2 7.8 2.8 2.0 0.4 -15.39 59.97 1.2032 -0.0366 -0.0146 0.0046 0.0031 0.5671 0.0089 0.0015 0.0099 0.0018 2.0476 -0.9638 -0.3150 -0.2972 -0.0866 260.6 -12.2 -5.3 1.8 1.5 + 67.5 97.5 92001 85 -10 120 67 264.1 -17.9 -6.3 1.5 1.2 2.60 -2.92 -1.13 0.81 0.77 -1.5 7.9 1.6 2.3 0.7 -17.15 763.19 1.2004 -0.0393 -0.0147 0.0055 0.0036 0.5364 0.0131 -0.0014 0.0081 0.0049 2.1032 -1.1293 -0.2044 -0.3511 -0.1533 256.8 -13.4 -5.2 2.3 1.4 + 67.5 112.5 96927 410 83 81 75 264.7 -20.9 -5.9 0.8 0.9 2.60 -2.99 -1.16 0.80 0.84 -0.5 9.4 1.0 3.7 1.1 -14.17 358.33 1.1999 -0.0413 -0.0145 0.0059 0.0038 0.5612 0.0136 0.0004 0.0137 0.0054 1.8429 -1.1076 -0.2433 -0.4032 -0.0866 257.7 -14.6 -5.2 2.2 1.3 + 67.5 127.5 88256 -162 -101 71 75 262.9 -18.3 -5.1 2.0 1.2 2.36 -2.79 -0.96 0.89 0.77 -0.8 8.2 0.8 1.7 0.5 -7.23 1084.25 1.2002 -0.0411 -0.0137 0.0063 0.0041 0.5212 0.0073 -0.0026 0.0093 0.0048 2.2926 -1.0869 -0.1408 -0.3353 -0.0581 255.2 -14.3 -4.7 2.4 1.3 + 67.5 142.5 97004 438 124 -19 52 265.3 -20.6 -5.9 1.5 0.5 2.52 -3.14 -1.04 0.96 0.78 -1.7 6.3 0.7 1.2 0.2 2.48 358.01 1.2020 -0.0407 -0.0135 0.0056 0.0033 0.5726 0.0241 0.0017 0.0109 0.0071 1.5608 -1.2083 -0.2722 -0.2502 -0.1000 258.0 -15.3 -4.8 2.1 0.9 + 67.5 157.5 98639 507 207 -17 56 265.2 -21.5 -6.2 0.2 -0.2 2.52 -2.89 -1.09 0.66 0.68 1.9 12.1 1.9 4.9 1.7 6.34 226.33 1.2042 -0.0376 -0.0129 0.0048 0.0032 0.5751 0.0172 0.0046 0.0109 0.0086 1.6082 -1.2193 -0.2922 -0.4250 -0.1653 259.1 -14.0 -4.9 1.8 0.8 + 67.5 172.5 95382 64 176 -40 20 265.7 -16.4 -6.2 1.6 0.6 2.64 -2.77 -1.14 0.72 0.63 -0.8 8.2 2.6 1.9 2.0 6.74 486.17 1.2060 -0.0329 -0.0119 0.0049 0.0041 0.5533 0.0103 0.0086 0.0048 0.0095 2.0645 -0.9526 -0.3695 -0.2593 -0.2280 258.9 -11.8 -4.7 2.0 1.2 + 67.5 -172.5 101325 42 390 -17 50 267.6 -12.0 -8.1 0.5 -0.6 2.75 -2.11 -1.32 0.35 0.44 -2.6 -0.8 3.0 2.3 1.8 2.05 -0.00 1.2077 -0.0298 -0.0118 0.0046 0.0037 0.5729 -0.0013 0.0129 0.0078 0.0118 2.2146 -0.2531 -0.5207 -0.2315 -0.3237 261.2 -10.1 -5.0 1.5 0.8 + 67.5 -157.5 95027 -324 197 15 76 269.1 -14.6 -4.7 2.1 0.7 2.99 -2.86 -1.08 0.84 0.61 -2.7 4.9 0.9 0.4 0.3 5.63 496.39 1.2106 -0.0285 -0.0101 0.0052 0.0035 0.5449 0.0057 0.0020 0.0041 0.0046 2.2660 -0.9312 -0.2473 -0.0343 -0.0407 261.4 -10.3 -3.6 2.1 0.9 + 67.5 -142.5 95374 -155 228 -11 88 268.8 -17.2 -4.7 2.0 0.2 2.89 -3.00 -1.06 0.82 0.59 -1.8 6.5 0.7 1.7 0.6 7.47 481.73 1.2114 -0.0293 -0.0105 0.0053 0.0028 0.5538 0.0169 0.0025 0.0050 0.0051 1.9303 -1.1646 -0.2621 -0.1311 -0.0585 261.6 -11.3 -3.8 2.3 0.6 + 67.5 -127.5 97957 9 318 -33 59 267.2 -17.3 -6.8 1.9 0.2 2.67 -2.68 -1.18 0.62 0.60 0.5 8.9 2.6 1.9 1.4 -5.79 284.22 1.2097 -0.0299 -0.0120 0.0048 0.0024 0.5685 0.0176 0.0063 0.0049 0.0069 1.8190 -1.0873 -0.4150 -0.1564 -0.1594 261.1 -11.3 -4.7 2.0 0.5 + 67.5 -112.5 99347 37 348 -71 -33 266.0 -14.7 -7.3 1.4 1.3 2.36 -2.34 -1.21 0.49 0.69 -4.0 1.0 1.1 0.7 -0.1 -24.21 180.21 1.2054 -0.0310 -0.0133 0.0038 0.0028 0.5752 0.0169 0.0097 0.0049 0.0048 1.7645 -0.8780 -0.4501 -0.0910 -0.1150 259.2 -11.4 -5.4 1.5 1.0 + 67.5 -97.5 101033 104 409 -145 -83 262.4 -17.7 -9.4 0.9 0.4 2.21 -2.33 -1.29 0.50 0.75 0.1 7.6 3.1 2.3 2.1 -34.87 37.36 1.2014 -0.0329 -0.0147 0.0026 0.0026 0.5845 0.0171 0.0147 0.0014 0.0063 1.6360 -1.0662 -0.6359 -0.1070 -0.1875 257.8 -12.0 -6.1 1.2 1.1 + 67.5 -82.5 98675 -156 286 -178 -78 263.4 -14.7 -8.3 0.9 0.3 2.15 -2.06 -1.19 0.41 0.54 -0.5 4.4 2.2 1.4 1.9 -26.50 195.61 1.2005 -0.0331 -0.0147 0.0019 0.0022 0.5707 0.0087 0.0128 0.0013 0.0073 1.9100 -0.7079 -0.5631 -0.1232 -0.2620 257.2 -11.5 -6.1 0.9 0.7 + 67.5 -67.5 91791 -528 42 -152 -90 263.4 -12.2 -7.0 1.3 0.4 2.09 -1.89 -1.05 0.45 0.52 -0.3 3.1 3.3 0.0 0.9 0.69 749.61 1.2019 -0.0319 -0.0132 0.0017 0.0018 0.5426 0.0025 0.0095 -0.0029 0.0043 2.2808 -0.5192 -0.5834 -0.0068 -0.1806 256.1 -10.3 -5.1 1.1 0.6 + 67.5 -52.5 97432 -491 103 -147 -121 270.6 -9.5 -5.1 2.0 0.1 2.63 -1.97 -0.87 0.52 0.35 -4.2 2.0 0.4 -0.2 0.6 28.08 276.78 1.2069 -0.0286 -0.0117 0.0024 0.0014 0.5574 -0.0027 0.0028 -0.0019 0.0032 2.5307 -0.3694 -0.1182 -0.0393 -0.0870 260.8 -8.4 -4.1 1.5 0.4 + 67.5 -37.5 72078 -984 -199 48 -56 259.3 -7.8 -2.9 1.6 0.8 1.35 -0.85 -0.32 0.25 0.20 -3.5 1.2 -0.1 -0.6 -0.1 53.57 2643.97 1.2071 -0.0275 -0.0102 0.0017 0.0019 0.4652 -0.0060 -0.0074 -0.0008 -0.0031 2.7624 -0.2256 0.1363 0.1596 0.0928 250.6 -6.9 -2.7 1.2 0.8 + 67.5 -22.5 100916 -512 142 -83 -148 274.0 -3.6 -2.9 0.6 0.7 3.67 -1.15 -0.76 0.27 0.37 -4.6 -3.6 -0.2 1.8 0.9 61.84 -0.02 1.2131 -0.0228 -0.0097 0.0021 0.0022 0.5616 -0.0176 -0.0082 -0.0016 0.0009 2.7593 0.2175 0.0923 0.0730 -0.0465 265.0 -4.9 -2.5 1.2 0.9 + 67.5 -7.5 100825 -532 124 -10 -81 276.7 -3.0 -2.8 0.2 0.9 4.29 -1.16 -0.93 0.15 0.42 -6.6 -1.7 0.4 0.6 -0.2 54.98 -0.00 1.2152 -0.0219 -0.0100 0.0014 0.0024 0.5482 -0.0215 -0.0108 0.0012 0.0017 3.0721 0.3201 0.1295 -0.0151 -0.0102 266.9 -4.0 -2.4 0.7 0.9 + 52.5 7.5 101113 -10 -13 24 -7 283.7 -7.7 -2.8 -0.2 0.3 6.32 -2.39 -1.42 0.20 0.38 -5.0 1.0 -0.2 0.3 -0.1 42.83 35.28 1.2358 -0.0195 -0.0104 0.0006 0.0011 0.5568 -0.0135 -0.0121 0.0031 0.0013 2.9968 0.0633 0.0775 -0.0744 0.0212 273.4 -5.7 -2.8 0.0 0.3 + 52.5 22.5 99832 60 -78 -21 -20 282.1 -10.9 -3.2 -0.0 0.0 5.81 -3.14 -1.45 0.50 0.40 -5.0 1.7 -0.2 -0.3 0.2 29.59 143.43 1.2336 -0.0237 -0.0114 0.0012 0.0005 0.5564 -0.0094 -0.0103 0.0045 0.0022 2.7986 -0.1531 0.0203 -0.1189 -0.0107 271.9 -8.0 -3.3 0.4 0.0 + 52.5 37.5 99397 209 -93 -59 -46 280.8 -13.6 -3.9 0.0 -0.3 5.35 -3.45 -1.40 0.60 0.41 -4.8 1.8 0.4 0.0 0.7 12.60 184.72 1.2320 -0.0268 -0.0123 0.0015 0.0008 0.5622 -0.0049 -0.0109 0.0056 0.0029 2.5426 -0.2959 0.0105 -0.1211 -0.0843 270.7 -9.7 -3.6 0.5 0.1 + 52.5 52.5 99970 505 -61 -77 -57 279.8 -16.3 -4.5 -0.4 -0.4 4.72 -3.39 -1.15 0.56 0.37 -3.5 4.0 1.0 0.3 0.9 -4.50 148.66 1.2316 -0.0289 -0.0120 0.0010 0.0006 0.5698 0.0012 -0.0082 0.0032 0.0017 2.4144 -0.3959 0.0036 -0.1907 -0.0966 270.3 -10.9 -3.7 0.5 0.1 + 52.5 67.5 98245 635 11 -104 -70 278.3 -16.4 -4.2 -0.7 -0.7 4.21 -3.28 -0.98 0.58 0.32 -2.0 5.3 1.0 1.3 0.8 -25.30 298.82 1.2297 -0.0295 -0.0110 0.0004 -0.0001 0.5646 0.0074 -0.0073 0.0045 0.0035 2.3337 -0.4560 -0.0254 -0.2471 -0.1308 268.9 -11.3 -3.3 0.2 -0.3 + 52.5 82.5 99564 958 114 -106 -52 278.4 -16.6 -4.3 -0.9 -0.9 4.27 -3.63 -1.19 0.78 0.51 -3.2 3.4 0.6 1.8 0.9 -40.58 200.18 1.2276 -0.0313 -0.0110 0.0006 -0.0001 0.5703 0.0091 -0.0027 0.0064 0.0009 2.3523 -0.5135 -0.0920 -0.2439 -0.0598 268.3 -12.3 -3.7 0.1 -0.2 + 52.5 97.5 84614 242 -132 -72 -35 271.5 -15.6 -4.0 -0.0 0.3 3.30 -3.19 -1.06 0.77 0.61 -5.3 2.6 -0.1 1.6 0.2 -37.65 1516.58 1.2219 -0.0356 -0.0124 0.0028 0.0019 0.5107 0.0020 -0.0050 0.0094 0.0018 2.6159 -0.4412 -0.0503 -0.2585 0.0256 261.0 -13.2 -4.1 0.7 0.5 + 52.5 112.5 89324 365 -122 -35 43 272.5 -18.3 -4.4 -0.2 0.5 3.45 -3.52 -1.34 0.94 0.86 -4.5 3.7 -0.0 2.5 0.4 -23.29 1044.99 1.2198 -0.0397 -0.0135 0.0033 0.0028 0.5285 -0.0087 -0.0042 0.0094 0.0029 2.5010 -0.3025 -0.1153 -0.2500 -0.0399 261.9 -14.9 -4.6 0.8 0.9 + 52.5 127.5 97719 667 -69 24 64 274.0 -21.0 -4.0 -1.5 0.5 3.93 -4.42 -1.55 1.12 1.10 -3.0 4.5 -0.2 1.4 1.0 7.96 293.46 1.2180 -0.0422 -0.0138 0.0025 0.0025 0.5650 -0.0013 -0.0061 0.0149 0.0025 2.1988 -0.5900 -0.0493 -0.3360 0.0276 263.7 -16.9 -4.6 0.2 0.8 + 52.5 142.5 99640 158 35 -4 10 273.4 -15.4 -6.6 -1.4 0.3 3.88 -3.43 -1.80 0.41 0.92 -4.3 0.3 0.7 1.9 0.1 18.53 116.21 1.2181 -0.0384 -0.0153 0.0019 0.0022 0.5626 -0.0230 -0.0029 0.0106 0.0055 2.4942 0.0569 -0.1532 -0.3128 -0.0381 264.1 -14.0 -5.8 0.0 0.4 + 52.5 157.5 95112 -576 -198 -97 -93 273.7 -9.1 -5.2 0.5 1.0 3.78 -2.28 -1.53 0.28 0.67 -5.1 -1.6 -0.3 1.4 0.1 24.73 469.56 1.2210 -0.0312 -0.0142 0.0023 0.0029 0.5385 -0.0374 -0.0054 0.0028 0.0031 3.0705 0.6096 -0.0873 -0.1696 -0.0408 264.2 -9.5 -5.1 0.9 1.0 + 52.5 172.5 100648 -677 -142 -140 -131 277.0 -3.5 -3.6 -0.5 0.8 4.31 -1.47 -1.28 -0.04 0.53 -7.5 -2.1 -0.6 0.7 0.8 1.96 6.97 1.2240 -0.0241 -0.0121 0.0017 0.0035 0.5555 -0.0436 -0.0092 0.0038 0.0042 3.0003 0.7569 0.0679 -0.1898 -0.0687 267.2 -5.8 -3.9 0.5 1.2 + 52.5 -172.5 100726 -782 -85 -169 -66 277.6 -3.1 -3.1 -0.1 0.8 4.54 -1.34 -1.11 0.12 0.51 -7.4 -1.8 -0.4 0.8 0.7 7.31 0.00 1.2258 -0.0202 -0.0102 0.0021 0.0043 0.5584 -0.0380 -0.0077 0.0030 0.0067 2.9653 0.6523 0.0749 -0.1401 -0.1441 267.7 -4.6 -3.3 0.8 1.3 + 52.5 -157.5 100773 -761 13 -94 86 278.8 -3.1 -3.4 -0.0 1.1 4.96 -1.39 -1.24 0.15 0.61 -7.2 -1.4 -0.0 0.8 0.4 11.07 0.00 1.2282 -0.0177 -0.0098 0.0021 0.0046 0.5607 -0.0306 -0.0083 0.0040 0.0078 2.9722 0.4765 0.0820 -0.1152 -0.1058 268.7 -4.1 -3.2 0.6 1.4 + 52.5 -142.5 101014 -663 -15 -7 171 280.3 -2.4 -3.3 -0.1 1.0 5.46 -1.22 -1.27 0.11 0.57 -7.2 -1.0 -0.0 0.5 0.3 -1.16 0.00 1.2312 -0.0148 -0.0099 0.0019 0.0044 0.5640 -0.0218 -0.0115 0.0036 0.0078 2.9725 0.3417 0.1301 -0.1222 -0.0938 270.0 -3.3 -3.0 0.5 1.3 + 52.5 -127.5 93713 -352 -69 6 103 278.8 -6.1 -2.8 0.5 1.2 4.70 -1.85 -1.16 0.21 0.54 -5.8 0.5 -0.3 0.3 0.2 -12.45 658.38 1.2326 -0.0169 -0.0105 0.0024 0.0042 0.5446 -0.0028 -0.0089 0.0007 0.0030 2.7708 -0.1771 0.0239 -0.0948 0.0145 268.1 -5.3 -3.1 0.8 1.4 + 52.5 -112.5 92579 -64 -25 -6 86 279.1 -11.7 -4.1 0.2 0.5 4.27 -2.90 -1.18 0.65 0.66 -2.4 4.3 -0.2 1.4 -0.0 -20.16 753.56 1.2336 -0.0230 -0.0115 0.0023 0.0029 0.5488 0.0017 -0.0070 0.0027 0.0001 2.3906 -0.3461 -0.0851 -0.0669 0.0319 268.3 -8.7 -3.7 0.8 1.0 + 52.5 -97.5 98802 206 173 -50 64 276.1 -16.3 -6.0 -0.9 -0.5 4.43 -3.80 -1.84 0.53 0.84 -3.5 1.9 1.0 0.8 0.3 -32.73 222.14 1.2282 -0.0292 -0.0133 0.0009 0.0012 0.5755 0.0104 0.0024 0.0067 0.0047 2.2461 -0.6878 -0.3379 -0.2108 -0.0865 267.2 -12.1 -5.1 0.2 0.2 + 52.5 -82.5 101196 176 253 -124 25 274.2 -15.3 -7.1 -0.9 -1.0 3.77 -3.05 -1.90 0.31 0.68 -4.4 -0.4 0.6 0.6 0.4 -43.62 20.24 1.2237 -0.0310 -0.0145 -0.0001 -0.0002 0.5914 0.0069 0.0098 0.0032 0.0067 2.0112 -0.4160 -0.3877 -0.1284 -0.1402 265.4 -12.4 -6.0 -0.1 -0.4 + 52.5 -67.5 93782 -314 -75 -166 -94 271.9 -14.6 -6.7 -0.2 -0.9 3.63 -2.91 -1.60 0.38 0.53 -5.0 2.8 1.2 1.1 0.6 -21.65 618.60 1.2229 -0.0290 -0.0144 -0.0002 -0.0005 0.5567 0.0060 0.0028 0.0000 0.0033 2.2718 -0.5043 -0.2521 -0.1484 -0.1093 263.0 -11.3 -5.6 0.2 -0.4 + 52.5 -52.5 100978 -417 -88 -171 -202 275.6 -5.3 -4.8 -0.3 0.5 4.14 -1.68 -1.42 0.07 0.52 -2.8 -6.8 -1.1 1.9 0.3 17.12 0.00 1.2274 -0.0233 -0.0135 0.0003 0.0006 0.5787 -0.0150 -0.0081 -0.0018 -0.0027 2.3411 0.3391 0.0266 -0.0887 0.0860 267.5 -7.5 -4.6 0.4 0.4 + 52.5 -37.5 100982 -490 -174 -59 -139 281.0 -2.7 -2.7 -0.2 0.4 5.63 -1.31 -1.14 0.06 0.35 -7.4 -0.9 -0.3 0.4 0.3 52.75 0.00 1.2328 -0.0173 -0.0110 0.0006 0.0017 0.5651 -0.0208 -0.0129 0.0033 0.0025 2.9291 0.2414 0.1176 -0.1067 -0.0463 270.8 -3.9 -3.0 0.1 0.6 + 52.5 -22.5 101206 -244 -48 22 -8 283.8 -2.0 -2.1 0.0 0.5 6.53 -1.14 -1.03 0.08 0.32 -8.2 -0.3 -0.2 0.0 0.2 61.33 0.00 1.2362 -0.0141 -0.0095 0.0007 0.0021 0.5606 -0.0187 -0.0123 0.0025 0.0031 3.1140 0.2588 0.1195 -0.0744 -0.0514 273.0 -2.5 -2.2 0.1 0.6 + 52.5 -7.5 99974 -97 6 37 44 283.4 -4.3 -2.4 0.1 0.4 6.51 -1.57 -1.22 0.09 0.31 -6.7 0.8 -0.1 0.2 -0.1 56.87 120.83 1.2366 -0.0154 -0.0097 0.0005 0.0018 0.5517 -0.0162 -0.0132 0.0011 0.0026 3.2371 0.2402 0.1315 -0.0566 0.0171 273.1 -3.5 -2.4 0.1 0.4 + 37.5 7.5 101620 177 17 66 44 292.5 -5.6 -3.9 -0.0 0.5 9.28 -2.90 -2.02 -0.08 0.50 -4.8 -3.3 0.1 0.7 -0.1 42.97 0.00 1.2578 -0.0186 -0.0102 0.0013 0.0019 0.5632 -0.0209 -0.0114 -0.0021 -0.0023 3.5358 -0.2021 0.0601 0.0992 0.1434 281.4 -6.0 -3.5 0.4 0.8 + 37.5 22.5 95757 146 -84 -14 -82 289.3 -7.8 -4.0 0.5 0.5 7.68 -2.00 -1.79 0.01 0.16 -6.0 -0.2 0.2 -0.2 -0.2 31.66 495.02 1.2550 -0.0195 -0.0110 0.0012 0.0014 0.5316 -0.0057 -0.0032 -0.0057 -0.0075 3.6210 0.1142 0.0123 0.0046 0.0753 278.3 -7.0 -3.9 0.7 0.7 + 37.5 37.5 88824 335 -69 -39 -145 287.6 -10.3 -4.9 -0.6 0.5 5.59 -1.99 -0.95 -0.14 0.30 -6.4 0.4 -0.1 0.5 0.4 31.30 1114.27 1.2560 -0.0239 -0.0133 0.0011 0.0030 0.5040 0.0083 0.0009 -0.0025 -0.0076 3.3330 -0.1241 -0.0507 -0.0235 0.1685 276.4 -9.5 -4.9 0.2 1.2 + 37.5 52.5 101535 648 87 -42 -152 291.4 -6.9 -4.7 -0.5 0.4 8.95 -3.76 -2.88 0.34 0.65 -4.4 -1.4 1.3 -0.1 -0.3 -10.95 -0.00 1.2595 -0.0235 -0.0112 -0.0001 0.0017 0.5797 -0.0241 0.0032 -0.0018 0.0016 3.3654 0.1658 -0.3835 0.1726 0.0648 280.8 -7.5 -4.6 -0.3 0.4 + 37.5 67.5 94606 790 43 -97 -166 292.7 -12.3 -3.6 -0.9 0.2 6.25 -2.57 0.29 0.07 -0.17 -6.0 2.5 0.1 1.1 -0.0 -40.60 594.09 1.2605 -0.0257 -0.0095 -0.0003 0.0013 0.5467 -0.0017 0.0001 0.0022 -0.0027 2.7011 0.2488 0.1934 -0.0404 -0.0308 279.8 -10.1 -3.1 -0.6 0.3 + 37.5 82.5 87775 665 -89 -57 -143 289.6 -13.9 -2.7 -2.3 0.1 2.63 -1.53 -0.50 0.28 0.28 -5.0 2.3 -0.1 0.4 0.1 -53.38 1221.77 1.2610 -0.0278 -0.0090 -0.0006 0.0019 0.6026 -0.0327 -0.0057 0.0036 0.0094 1.5222 0.2078 0.0075 -0.0469 -0.1070 272.9 -8.7 -2.5 -0.8 0.0 + 37.5 97.5 60519 -401 -314 -107 -66 269.5 -10.6 -3.2 -0.5 1.0 2.27 -1.98 -0.74 0.42 0.63 -7.4 0.7 -0.2 0.4 -0.0 -48.57 4252.68 1.2508 -0.0285 -0.0112 0.0011 0.0040 0.4396 -0.0174 -0.0091 0.0021 0.0009 2.9269 0.2251 0.0874 -0.0031 -0.0193 257.2 -9.5 -3.3 -0.1 1.3 + 37.5 112.5 88423 544 -161 -113 -44 283.3 -13.0 -2.8 -1.0 -0.1 5.46 -4.58 -2.17 0.60 1.38 -5.1 0.8 -0.1 -0.1 -0.2 -17.76 1171.24 1.2513 -0.0287 -0.0105 -0.0003 0.0021 0.5435 -0.0211 -0.0035 0.0065 0.0095 2.7688 0.1102 -0.1470 -0.0698 -0.1236 271.3 -10.7 -3.5 -0.6 0.2 + 37.5 127.5 98175 690 -31 -82 -34 284.2 -12.5 -4.8 -1.3 0.1 6.71 -5.31 -2.72 0.66 1.45 -5.7 0.4 -0.0 -0.0 -0.3 24.85 288.58 1.2487 -0.0284 -0.0132 -0.0011 0.0019 0.5685 -0.0313 -0.0071 0.0057 0.0182 3.0422 0.4054 -0.0339 -0.1322 -0.1985 273.6 -10.9 -4.9 -0.8 0.0 + 37.5 142.5 101393 319 -16 -156 -77 287.4 -6.7 -5.6 -0.6 0.2 8.21 -4.61 -3.18 0.22 1.08 -7.0 -3.4 0.0 0.5 -0.1 27.42 0.00 1.2510 -0.0249 -0.0154 -0.0013 0.0005 0.5830 -0.0498 -0.0191 -0.0048 0.0067 2.9204 0.4536 0.1511 0.0091 -0.0820 276.0 -7.9 -5.5 -0.2 0.1 + 37.5 157.5 101425 -120 -147 -223 -169 288.1 -5.0 -5.7 -0.9 0.4 8.79 -3.45 -3.24 -0.14 0.96 -8.5 -1.6 -0.2 0.4 0.0 1.14 0.00 1.2534 -0.0203 -0.0151 -0.0022 -0.0007 0.5882 -0.0417 -0.0165 -0.0061 -0.0052 2.8980 0.3737 0.1011 0.0477 0.0772 276.6 -5.9 -5.6 -0.6 0.3 + 37.5 172.5 101572 -396 -181 -269 -172 288.5 -3.8 -4.8 -0.6 0.7 8.94 -2.85 -2.79 -0.05 0.86 -8.1 -1.6 -0.0 0.6 0.1 -12.72 0.00 1.2548 -0.0169 -0.0126 -0.0019 -0.0002 0.5897 -0.0353 -0.0102 -0.0033 -0.0044 2.9057 0.2913 0.0303 0.0108 0.0583 277.1 -4.7 -4.8 -0.5 0.5 + 37.5 -172.5 101756 -534 -104 -257 -80 288.4 -3.4 -4.3 -0.4 1.2 8.87 -2.53 -2.46 0.04 1.05 -7.8 -1.2 0.2 0.5 0.1 -12.29 0.00 1.2549 -0.0150 -0.0103 -0.0009 0.0011 0.5887 -0.0273 -0.0070 -0.0017 -0.0037 2.9418 0.2148 -0.0189 0.0075 0.0855 277.3 -4.2 -4.0 -0.3 1.0 + 37.5 -157.5 101958 -529 -47 -174 5 288.3 -2.9 -4.2 -0.4 1.0 8.85 -2.03 -2.24 -0.00 0.75 -7.7 -0.8 0.4 0.5 -0.1 -15.18 0.00 1.2554 -0.0128 -0.0095 -0.0003 0.0010 0.5883 -0.0215 -0.0052 0.0001 -0.0077 2.9442 0.1984 -0.0131 -0.0183 0.1503 277.5 -3.5 -3.7 -0.1 1.0 + 37.5 -142.5 102172 -371 -24 -99 -17 288.5 -1.8 -3.6 -0.2 0.6 8.64 -1.33 -1.79 0.09 0.44 -8.2 -0.5 0.3 0.4 -0.1 -30.28 0.00 1.2561 -0.0107 -0.0091 0.0003 0.0007 0.5800 -0.0139 0.0012 0.0038 -0.0078 3.1148 0.1451 -0.0558 -0.0471 0.1291 278.0 -2.6 -3.5 -0.1 0.7 + 37.5 -127.5 101969 35 93 4 -67 286.5 -1.0 -2.7 -0.1 0.3 7.87 -0.67 -1.41 0.02 0.32 -6.9 0.4 0.4 0.0 -0.0 -38.40 0.00 1.2576 -0.0123 -0.0101 0.0013 0.0017 0.5660 -0.0039 0.0048 0.0044 -0.0118 3.7470 0.0314 -0.2267 -0.1514 0.2123 278.6 -3.0 -3.6 0.3 1.1 + 37.5 -112.5 78277 -145 -238 71 80 283.2 -11.0 -4.3 0.4 1.1 3.62 -1.14 -0.93 -0.03 0.75 -6.6 2.7 -0.1 1.1 0.0 -20.68 2190.97 1.2569 -0.0202 -0.0117 0.0023 0.0027 0.4966 -0.0166 -0.0091 0.0182 -0.0007 2.8510 0.5458 0.2250 -0.3009 0.0687 270.4 -7.3 -3.9 0.0 1.1 + 37.5 -97.5 96791 222 -86 59 87 288.6 -12.1 -4.0 -0.7 0.2 7.37 -5.00 -1.69 0.52 0.55 -4.6 2.6 0.0 1.2 0.0 -28.70 410.88 1.2590 -0.0212 -0.0103 0.0004 0.0009 0.5684 -0.0023 -0.0077 0.0096 0.0090 2.5358 -0.2562 -0.0097 -0.1095 -0.1489 278.4 -9.4 -3.5 -0.3 0.0 + 37.5 -82.5 97220 77 -93 20 18 287.5 -10.5 -3.7 -1.0 -0.3 7.69 -4.40 -1.88 0.42 0.64 -5.6 0.1 -0.4 0.1 -0.1 -32.48 388.87 1.2559 -0.0198 -0.0106 -0.0004 0.0002 0.5659 -0.0037 -0.0035 0.0104 0.0040 2.6680 -0.1291 -0.1031 -0.1048 -0.0384 276.9 -8.6 -3.9 -0.7 -0.2 + 37.5 -67.5 101660 -45 -79 -5 -15 291.6 -5.7 -4.3 0.0 0.1 9.96 -3.65 -2.42 0.53 0.57 -9.4 -0.7 0.0 0.1 -0.2 -39.75 0.00 1.2572 -0.0162 -0.0114 0.0003 0.0002 0.5777 -0.0212 -0.0106 0.0061 0.0056 3.0858 0.0777 0.0801 -0.0245 -0.0412 279.5 -5.7 -4.1 -0.1 -0.2 + 37.5 -52.5 101805 -269 -140 74 -16 292.2 -3.4 -3.9 0.1 0.7 10.52 -2.73 -2.33 0.44 0.68 -9.0 -0.7 0.1 0.4 -0.1 -9.77 0.00 1.2585 -0.0130 -0.0108 0.0006 0.0009 0.5768 -0.0225 -0.0108 0.0057 0.0028 3.1000 0.0883 0.0646 -0.0489 -0.0265 280.4 -3.7 -3.8 0.1 0.4 + 37.5 -37.5 102034 -276 -50 131 21 292.0 -2.4 -3.3 0.1 0.8 10.31 -1.85 -2.02 0.19 0.58 -8.7 -0.3 0.2 0.2 -0.1 38.00 0.00 1.2589 -0.0108 -0.0095 0.0009 0.0015 0.5716 -0.0160 -0.0092 0.0027 0.0001 3.2050 0.0944 0.0789 -0.0152 0.0209 280.7 -2.7 -3.2 0.2 0.7 + 37.5 -22.5 102106 -116 12 152 49 290.9 -1.9 -2.8 -0.0 0.7 9.57 -1.37 -1.58 0.09 0.50 -8.8 -0.1 0.2 0.2 -0.0 50.71 0.00 1.2576 -0.0106 -0.0087 0.0012 0.0018 0.5615 -0.0127 -0.0079 0.0021 -0.0023 3.3600 0.1172 0.1256 -0.0303 0.0581 280.3 -2.4 -2.8 0.3 0.8 + 37.5 -7.5 99636 216 34 117 36 290.7 -5.7 -3.0 -0.1 0.9 8.22 -1.48 -1.33 -0.22 0.29 -5.0 -1.1 -0.6 0.9 0.6 54.00 178.43 1.2585 -0.0159 -0.0094 0.0017 0.0030 0.5469 -0.0068 -0.0109 -0.0011 -0.0020 3.7640 0.0160 0.0991 0.1017 0.1601 280.6 -5.5 -2.9 0.5 1.2 + 22.5 7.5 89835 197 -64 39 20 299.3 -8.2 -1.8 -1.5 -0.5 3.46 -0.82 -0.97 -0.09 0.13 -7.1 0.9 0.2 0.3 -0.0 32.55 1043.35 1.2810 -0.0115 -0.0034 -0.0006 0.0001 0.5939 -0.0111 0.0128 -0.0014 -0.0056 1.8023 0.4700 -0.0340 0.1802 0.1110 281.2 -4.6 -2.0 -0.5 0.1 + 22.5 22.5 95718 391 -1 55 -20 298.9 -8.2 -1.6 -1.4 -0.3 4.20 -0.74 -1.33 -0.04 0.14 -0.7 1.2 0.3 -0.4 -0.3 15.27 495.56 1.2814 -0.0114 -0.0036 -0.0004 0.0003 0.5778 -0.0019 0.0295 -0.0073 -0.0055 2.2514 0.4778 -0.2424 0.1980 0.0498 284.7 -5.5 -2.8 -0.4 0.2 + 22.5 37.5 100866 574 147 15 -53 300.9 -4.0 -2.9 0.1 -0.0 14.62 -2.80 -2.55 -1.07 -0.04 -0.9 -7.8 0.2 -0.4 -0.8 6.47 0.00 1.2850 -0.0121 -0.0048 -0.0001 0.0009 0.5744 -0.0542 -0.0167 0.0016 0.0084 5.4703 -0.7690 0.3538 -0.4423 -0.2145 290.3 -2.4 -1.8 -0.6 -0.6 + 22.5 52.5 99886 985 187 -61 -136 303.7 -9.7 -2.5 -1.3 -0.5 7.48 -0.50 -1.69 0.04 0.32 -2.7 0.6 0.1 0.5 0.3 -33.13 82.37 1.2864 -0.0147 -0.0043 -0.0002 0.0006 0.5857 -0.0402 0.0188 0.0041 0.0168 2.8707 0.5211 -0.3299 0.0588 -0.1427 289.6 -4.7 -2.7 -0.9 -0.8 + 22.5 67.5 100875 756 103 -67 -33 299.1 -2.7 -1.1 -0.2 -1.1 15.97 -4.65 -0.68 -1.27 -0.76 -5.7 1.9 1.4 -2.8 -0.4 -50.06 0.00 1.2827 -0.0081 -0.0020 -0.0010 -0.0017 0.5622 -0.0496 -0.0197 0.0157 0.0249 5.8978 1.5981 1.6607 -1.5429 -0.8005 289.7 -2.0 0.0 -1.3 -2.1 + 22.5 82.5 95697 718 17 -15 -30 299.7 -4.5 2.6 -2.4 -2.0 11.86 -4.85 -3.71 0.35 1.42 -5.5 1.3 0.1 0.2 0.0 -60.13 449.47 1.2827 -0.0079 -0.0006 -0.0011 -0.0023 0.5789 -0.0687 -0.0150 0.0009 0.0148 2.7599 0.8824 -0.2252 0.1690 -0.1666 286.9 -1.4 0.7 -1.1 -1.3 + 22.5 97.5 89345 387 -17 -29 -58 294.4 -1.9 1.0 -1.9 -0.1 12.65 -4.16 -2.66 0.28 -0.53 -6.1 -1.7 -1.1 0.2 -0.1 -42.81 1056.91 1.2806 -0.0068 -0.0023 -0.0007 -0.0004 0.5548 -0.0733 -0.0300 -0.0048 0.0021 3.0166 0.9867 0.1923 0.3551 -0.0535 283.3 -0.2 0.5 -0.7 -0.3 + 22.5 112.5 99313 739 109 -3 -75 295.2 -6.0 -2.4 -1.1 -0.4 12.97 -5.77 -0.98 -0.38 0.41 -5.9 0.6 0.5 0.2 0.3 -8.41 167.74 1.2784 -0.0076 -0.0035 -0.0006 -0.0005 0.6079 -0.0429 -0.0106 -0.0031 -0.0062 2.2958 -0.0634 -0.0220 0.0031 0.0389 284.7 -3.1 -1.1 -0.6 0.1 + 22.5 127.5 101264 517 237 51 -17 298.3 -3.2 -2.1 -0.2 -0.4 15.18 -3.66 -1.77 -0.28 -0.28 -9.0 -0.3 0.2 -0.2 -0.1 32.14 -0.00 1.2789 -0.0057 -0.0033 -0.0003 -0.0006 0.6071 -0.0346 -0.0149 -0.0011 -0.0060 2.7823 0.1670 0.1289 -0.0165 0.0264 286.5 -2.1 -1.3 -0.3 -0.2 + 22.5 142.5 101326 264 217 36 -0 299.0 -2.2 -2.0 -0.0 -0.8 15.38 -2.65 -2.12 -0.36 -0.83 -9.3 -0.1 -0.0 -0.1 -0.1 45.92 -0.00 1.2793 -0.0037 -0.0031 0.0001 -0.0009 0.5979 -0.0311 -0.0196 0.0009 -0.0015 2.9041 0.2368 0.1813 -0.0665 -0.0565 287.3 -1.1 -1.3 -0.2 -0.8 + 22.5 157.5 101451 111 143 -13 -19 299.0 -1.6 -1.7 0.0 -0.6 15.12 -1.90 -1.87 -0.18 -0.46 -9.3 -0.1 -0.0 -0.1 -0.0 20.60 -0.00 1.2788 -0.0023 -0.0028 0.0003 -0.0010 0.5834 -0.0359 -0.0216 0.0027 0.0070 3.1345 0.3697 0.2302 -0.0785 -0.1735 287.7 -0.1 -1.0 -0.1 -1.0 + 22.5 172.5 101564 11 109 -27 -7 298.5 -1.3 -1.6 0.0 -0.1 14.48 -1.60 -1.58 -0.08 -0.10 -9.4 -0.1 0.0 -0.0 0.0 2.38 -0.00 1.2773 -0.0018 -0.0028 0.0006 -0.0005 0.5695 -0.0311 -0.0219 0.0014 0.0045 3.3658 0.3924 0.2778 -0.0257 -0.1189 287.5 0.0 -0.8 0.0 -0.4 + 22.5 -172.5 101651 -55 106 -25 13 297.8 -1.3 -1.6 0.1 -0.1 13.86 -1.27 -1.36 -0.02 -0.15 -9.4 -0.1 0.0 -0.0 -0.0 5.64 -0.00 1.2755 -0.0022 -0.0031 0.0007 -0.0001 0.5611 -0.0176 -0.0163 -0.0016 -0.0044 3.4687 0.2615 0.2091 0.0376 0.0538 287.0 -0.5 -1.0 0.2 0.0 + 22.5 -157.5 101704 -54 97 -12 -5 297.2 -0.9 -1.5 0.0 -0.2 13.62 -0.92 -1.25 -0.01 -0.21 -9.2 -0.0 0.0 -0.0 0.0 4.02 -0.00 1.2743 -0.0032 -0.0039 0.0006 0.0000 0.5504 -0.0024 -0.0061 -0.0015 -0.0040 3.6614 0.0249 0.0462 0.0561 0.0395 286.9 -1.0 -1.4 0.2 0.0 + 22.5 -142.5 101870 34 128 1 -50 294.7 -0.5 -1.3 -0.1 0.0 11.55 -0.58 -1.03 0.07 0.11 -9.4 0.0 -0.0 0.0 0.0 -19.90 -0.00 1.2733 -0.0049 -0.0047 0.0006 0.0005 0.5425 -0.0013 -0.0015 0.0006 -0.0019 3.7861 0.0207 0.0377 0.0683 -0.0180 285.3 -1.1 -1.6 0.1 0.2 + 22.5 -127.5 101798 179 182 20 -57 292.2 -0.3 -1.8 -0.2 0.2 10.23 -0.89 -1.39 0.11 0.36 -9.3 -0.4 -0.2 0.2 0.2 -42.21 -0.00 1.2742 -0.0065 -0.0050 0.0002 0.0002 0.5527 -0.0130 -0.0050 0.0006 0.0076 3.9102 0.3323 0.4106 -0.0200 -0.2976 284.2 -1.6 -2.1 0.2 0.2 + 22.5 -112.5 101368 233 160 76 -9 294.0 -0.3 -3.4 -0.5 0.9 12.64 -2.04 -2.27 -0.59 0.62 -4.0 -3.6 2.4 0.4 -0.8 -38.71 -0.00 1.2787 -0.0069 -0.0037 -0.0005 -0.0008 0.5929 -0.0338 -0.0017 0.0019 0.0164 4.7716 0.7761 1.5686 -0.6252 -0.6926 285.6 -1.6 -2.6 -0.2 -0.5 + 22.5 -97.5 101424 314 -9 130 56 297.8 -3.9 -1.7 -0.6 -0.3 14.77 -3.53 -1.04 -0.38 -0.08 -7.7 0.4 0.5 -0.2 0.0 -18.40 3.32 1.2777 -0.0058 -0.0021 -0.0011 -0.0005 0.5903 -0.0233 -0.0184 -0.0017 0.0110 3.1576 0.0739 0.3661 -0.0738 -0.1712 287.1 -2.3 -0.2 -0.6 -0.8 + 22.5 -82.5 101332 142 123 126 61 299.1 -2.5 -1.5 -0.3 -0.0 14.92 -2.60 -1.69 -0.13 0.20 -8.1 -0.1 0.2 -0.0 0.0 -19.52 19.27 1.2778 -0.0036 -0.0027 -0.0004 -0.0001 0.5829 -0.0357 -0.0280 0.0026 0.0049 3.3107 0.4195 0.4491 -0.0439 -0.0065 287.4 -0.8 -0.4 -0.4 -0.2 + 22.5 -67.5 101669 1 139 134 90 299.0 -1.4 -1.7 0.0 0.0 14.92 -1.66 -1.62 -0.05 -0.04 -9.4 -0.2 0.0 0.0 -0.1 -50.80 -0.00 1.2765 -0.0029 -0.0031 0.0000 0.0002 0.5755 -0.0305 -0.0214 -0.0037 -0.0075 3.2996 0.3081 0.2718 0.0534 0.1230 287.5 -0.2 -0.9 0.2 0.3 + 22.5 -52.5 101808 -77 149 128 83 298.1 -1.0 -1.7 -0.0 0.0 13.95 -1.31 -1.72 -0.21 0.07 -9.4 -0.1 -0.0 -0.0 -0.0 -35.60 -0.00 1.2750 -0.0034 -0.0034 -0.0000 0.0006 0.5639 -0.0207 -0.0223 -0.0097 -0.0095 3.4020 0.2360 0.2787 0.1380 0.1536 287.1 -0.5 -1.0 0.4 0.6 + 22.5 -37.5 101875 -69 145 82 56 296.7 -0.7 -1.8 -0.2 -0.1 12.74 -0.98 -1.99 -0.21 0.02 -9.5 -0.1 -0.1 -0.0 0.0 0.50 -0.00 1.2740 -0.0052 -0.0041 0.0000 0.0009 0.5546 -0.0149 -0.0283 -0.0026 -0.0064 3.5803 0.0900 0.3795 0.0321 0.1059 286.4 -0.9 -1.1 0.0 0.5 + 22.5 -22.5 101714 74 99 67 -7 294.8 -0.5 -2.0 -0.3 -0.2 12.37 -1.31 -2.22 -0.49 0.38 -8.1 -0.4 -0.3 -0.4 0.5 24.23 -0.00 1.2757 -0.0079 -0.0047 0.0002 0.0009 0.5615 -0.0299 -0.0373 0.0016 0.0035 4.5408 0.3767 0.7883 -0.0416 0.1131 286.0 -1.4 -1.1 -0.1 0.3 + 22.5 -7.5 96968 404 55 73 -26 302.4 -8.7 -3.1 -1.3 0.8 4.62 -1.08 -1.42 -0.21 0.43 -6.3 0.1 0.0 1.1 0.3 30.61 373.24 1.2824 -0.0127 -0.0047 -0.0004 0.0010 0.6140 -0.0240 -0.0098 0.0015 -0.0016 1.9035 0.4024 0.1526 0.0996 0.0798 285.0 -4.7 -1.8 -0.6 0.5 + 7.5 7.5 98675 -141 -129 62 40 299.8 2.1 1.8 -0.0 -0.3 15.40 -2.45 -0.32 -1.60 -0.83 -5.8 1.4 -0.6 0.6 -0.2 22.93 207.44 1.2834 0.0023 0.0018 0.0002 -0.0004 0.6131 -0.0348 -0.0123 -0.0074 0.0046 2.5823 0.4111 0.0851 0.0450 -0.1169 287.4 2.1 1.6 -0.1 -0.6 + 7.5 22.5 92650 -106 -104 68 15 298.4 2.5 2.0 -0.3 -0.0 12.67 -4.36 -1.09 -1.66 -1.05 -4.5 1.7 -0.1 0.6 -0.6 -2.89 751.24 1.2848 0.0022 0.0021 -0.0003 -0.0001 0.5912 -0.0247 -0.0082 -0.0032 0.0065 2.3918 -0.0925 -0.0112 -0.1407 -0.1619 285.3 1.4 1.3 -0.4 -0.6 + 7.5 37.5 81555 -55 -36 31 11 291.4 1.8 1.1 -0.5 -0.3 10.90 -1.84 0.00 -0.41 -0.08 -5.6 0.0 0.0 -0.0 -0.1 -9.16 1865.08 1.2824 0.0005 0.0010 -0.0006 -0.0005 0.5229 -0.0269 -0.0007 -0.0045 0.0036 2.7385 0.1990 0.0087 -0.0056 -0.1069 280.1 1.5 0.7 -0.2 -0.3 + 7.5 52.5 101081 179 -6 16 49 299.7 -0.7 0.6 -0.4 -0.9 16.02 -1.01 -0.02 -0.48 -0.80 -9.3 -0.1 -0.3 -0.0 0.0 -52.34 0.00 1.2830 -0.0014 0.0006 -0.0005 -0.0002 0.5772 -0.0257 -0.0235 0.0106 -0.0116 3.4813 0.3710 0.4807 -0.2845 0.2603 289.1 0.1 1.3 -0.9 -0.1 + 7.5 67.5 101027 88 -13 -2 61 300.6 -0.4 0.4 -0.1 -0.2 16.65 -0.58 0.17 -0.10 -0.46 -9.4 -0.1 -0.1 0.1 -0.0 -86.05 -0.00 1.2825 -0.0005 0.0008 -0.0004 -0.0003 0.6058 -0.0230 -0.0268 0.0047 -0.0188 2.9865 0.3236 0.4588 -0.1066 0.3149 288.3 0.6 1.6 -0.4 0.5 + 7.5 82.5 100897 205 35 1 52 300.9 -1.4 -0.0 -0.2 -0.2 17.11 -0.62 0.21 -0.29 -0.62 -8.9 -0.1 -0.2 0.0 -0.1 -94.84 -0.00 1.2831 -0.0016 0.0005 -0.0003 -0.0001 0.6293 -0.0170 -0.0231 0.0007 -0.0136 2.7224 0.2156 0.4148 -0.0708 0.2475 287.7 -0.2 1.2 -0.3 0.2 + 7.5 97.5 100919 99 -10 5 34 301.2 -0.4 0.5 -0.1 -0.0 17.21 -0.58 0.01 -0.19 -0.40 -9.2 0.1 -0.1 -0.1 -0.1 -28.31 -0.00 1.2830 -0.0008 0.0007 -0.0001 -0.0001 0.6427 -0.0206 -0.0184 -0.0022 -0.0112 2.5166 0.1803 0.2426 0.0297 0.1834 287.1 0.5 1.1 -0.1 0.2 + 7.5 112.5 100907 79 32 -4 42 300.8 -0.9 -0.0 -0.2 -0.3 17.49 -0.27 -0.07 -0.02 -0.33 -9.2 0.1 -0.0 0.0 -0.0 28.13 -0.00 1.2828 -0.0011 0.0004 -0.0004 -0.0003 0.6428 -0.0116 -0.0195 0.0080 -0.0059 2.4859 0.0980 0.2685 -0.1191 0.1149 287.1 0.1 0.9 -0.5 -0.1 + 7.5 127.5 100890 18 46 -12 40 300.6 -0.3 -0.3 -0.1 -0.2 17.60 -0.14 -0.18 0.02 -0.32 -9.0 0.0 -0.0 0.0 -0.0 57.90 -0.00 1.2827 -0.0005 0.0001 -0.0003 -0.0004 0.6429 -0.0130 -0.0144 0.0084 -0.0021 2.4194 0.1210 0.1692 -0.1074 0.0379 287.2 0.4 0.5 -0.4 -0.3 + 7.5 142.5 100880 -32 12 -15 36 300.7 0.1 0.0 -0.0 -0.2 17.46 -0.17 -0.15 0.00 -0.38 -9.2 -0.0 -0.0 -0.0 -0.0 62.45 -0.00 1.2829 0.0001 0.0004 -0.0001 -0.0002 0.6404 -0.0191 -0.0161 0.0026 -0.0056 2.4496 0.2310 0.2068 -0.0322 0.0850 287.3 0.9 0.7 -0.1 -0.1 + 7.5 157.5 100888 -49 10 -23 20 300.6 0.0 0.0 0.0 -0.1 17.47 -0.29 -0.23 -0.02 -0.39 -9.2 -0.1 -0.1 -0.0 -0.0 45.72 -0.00 1.2830 0.0002 0.0005 0.0001 0.0000 0.6334 -0.0220 -0.0190 -0.0018 -0.0113 2.5124 0.2559 0.2287 0.0288 0.1391 287.6 0.9 0.8 0.1 0.2 + 7.5 172.5 100922 -53 9 -25 15 300.5 -0.1 0.0 -0.0 -0.1 17.38 -0.32 -0.25 -0.05 -0.33 -9.2 -0.0 -0.1 -0.0 -0.0 24.07 -0.00 1.2828 0.0000 0.0004 0.0002 0.0000 0.6265 -0.0208 -0.0205 -0.0035 -0.0108 2.5791 0.2146 0.2560 0.0395 0.1130 287.8 0.8 0.8 0.2 0.3 + 7.5 -172.5 100959 -58 9 -24 10 300.0 -0.1 -0.0 -0.1 -0.1 16.98 -0.26 -0.18 0.01 -0.26 -9.3 -0.1 -0.0 0.0 -0.0 11.21 -0.00 1.2821 -0.0002 0.0001 0.0002 -0.0001 0.6266 -0.0173 -0.0167 -0.0066 -0.0108 2.5096 0.1792 0.2318 0.0577 0.1052 287.4 0.6 0.6 0.3 0.3 + 7.5 -157.5 101003 -48 9 -14 9 299.4 -0.1 -0.0 -0.2 -0.1 16.70 -0.17 -0.10 -0.01 -0.28 -9.2 -0.1 -0.1 0.1 -0.0 12.93 -0.00 1.2813 -0.0007 -0.0001 0.0001 -0.0002 0.6256 -0.0103 -0.0129 -0.0047 -0.0075 2.4404 0.1273 0.2099 0.0303 0.0588 287.2 0.2 0.4 0.2 0.1 + 7.5 -142.5 101054 -27 3 -11 12 298.8 -0.2 -0.0 -0.2 -0.0 16.47 -0.06 0.13 0.11 -0.32 -9.0 -0.0 -0.1 0.1 -0.1 -3.96 -0.00 1.2806 -0.0012 -0.0003 -0.0003 -0.0002 0.6265 -0.0088 -0.0088 -0.0013 -0.0056 2.3651 0.1239 0.1876 -0.0052 0.0547 286.8 0.1 0.3 0.0 0.0 + 7.5 -127.5 101085 -14 -21 -3 22 298.8 -0.4 0.2 -0.3 0.0 16.37 0.05 0.55 0.11 -0.41 -8.8 0.2 0.0 0.2 -0.3 -27.71 -0.00 1.2805 -0.0014 -0.0002 -0.0005 -0.0002 0.6266 -0.0121 -0.0063 0.0015 -0.0056 2.3709 0.1428 0.1779 -0.0307 0.0806 286.8 0.2 0.5 -0.2 -0.1 + 7.5 -112.5 101100 -11 -46 10 24 298.9 -0.2 0.7 -0.2 0.0 16.39 0.07 0.62 0.01 -0.35 -8.9 0.1 -0.1 0.2 -0.2 -30.61 0.00 1.2807 -0.0011 0.0003 -0.0006 -0.0001 0.6250 -0.0221 -0.0108 0.0024 -0.0054 2.4369 0.2547 0.2424 -0.0519 0.1237 286.8 0.8 0.9 -0.3 -0.1 + 7.5 -97.5 101088 -7 -44 23 23 299.2 0.1 0.9 -0.2 -0.1 16.73 -0.23 0.16 -0.08 -0.29 -8.9 -0.1 -0.4 0.1 -0.1 -7.98 -0.00 1.2811 -0.0003 0.0010 -0.0005 -0.0001 0.6227 -0.0297 -0.0207 0.0012 -0.0070 2.5606 0.3948 0.3970 -0.0623 0.1470 287.1 1.1 1.3 -0.2 0.1 + 7.5 -82.5 101007 -30 -34 16 7 299.9 0.2 1.1 -0.1 0.4 17.57 -0.46 -0.36 0.03 -0.38 -8.0 0.0 -0.2 0.1 -0.1 12.73 -0.00 1.2817 0.0002 0.0013 -0.0002 0.0004 0.6360 -0.0257 -0.0210 -0.0026 -0.0139 2.4870 0.3303 0.2886 0.0395 0.2030 287.2 1.0 1.3 0.0 0.6 + 7.5 -67.5 100166 -123 -26 71 55 300.3 1.2 1.5 -0.7 0.2 15.99 -1.32 -1.52 -0.08 -0.99 -6.0 -0.8 -1.1 -0.2 -0.4 -10.78 75.31 1.2827 0.0008 0.0012 -0.0007 -0.0000 0.6152 -0.0262 -0.0096 -0.0030 -0.0127 2.4761 0.1525 -0.0792 0.0310 0.0240 288.0 1.3 0.6 -0.3 0.3 + 7.5 -52.5 101195 -85 39 33 65 299.9 -0.1 -0.4 -0.3 -0.2 16.23 -0.31 -0.48 0.04 -0.52 -9.3 -0.1 0.0 0.1 -0.1 -38.54 -0.00 1.2814 -0.0003 0.0000 -0.0006 -0.0003 0.6123 -0.0157 -0.0186 0.0034 -0.0114 2.7261 0.2133 0.2316 -0.1102 0.1279 287.5 0.4 0.5 -0.2 0.1 + 7.5 -37.5 101244 -81 3 28 56 299.0 -0.0 -0.3 -0.0 -0.2 15.91 -0.42 -0.75 0.15 -0.56 -9.2 -0.3 -0.3 0.1 -0.1 -10.51 -0.00 1.2808 -0.0005 0.0008 -0.0006 -0.0003 0.6227 -0.0086 -0.0277 0.0032 -0.0054 2.5842 0.1613 0.4124 -0.0769 0.0680 286.6 0.1 1.0 -0.2 -0.1 + 7.5 -22.5 101223 -99 -39 41 46 298.8 0.3 -0.5 0.4 -0.4 15.95 -0.38 -0.69 -0.02 -0.56 -9.1 -0.3 -0.2 -0.1 0.0 16.63 -0.00 1.2814 0.0001 0.0016 -0.0007 -0.0001 0.6313 -0.0132 -0.0121 0.0014 -0.0002 2.6218 0.4080 0.3973 -0.0244 0.1219 286.5 0.5 0.6 -0.3 -0.4 + 7.5 -7.5 96717 -124 -99 48 27 297.9 1.7 1.4 0.1 -0.1 15.24 -1.60 -0.08 -1.32 -0.91 -5.4 0.6 -0.1 0.1 -0.0 32.13 392.57 1.2823 0.0012 0.0014 0.0002 -0.0002 0.6003 -0.0187 -0.0020 -0.0062 0.0027 2.6961 0.3455 0.0231 0.1115 -0.1184 286.2 1.2 1.0 -0.1 -0.4 + -7.5 7.5 101223 -197 -171 69 49 297.3 1.7 3.0 -0.4 -0.4 14.64 1.30 1.80 -0.44 -0.55 -9.5 -0.1 -0.2 -0.1 0.1 8.51 -0.00 1.2821 -0.0001 0.0013 -0.0002 -0.0004 0.6046 0.0470 0.0085 -0.0174 -0.0016 2.7967 -0.6320 0.1672 0.3669 -0.0577 287.2 -1.0 0.8 0.2 -0.3 + -7.5 22.5 91286 -72 -8 56 -2 296.1 -2.2 -0.2 0.1 0.2 13.55 3.76 0.42 -1.99 -0.33 -3.9 -0.7 0.1 0.0 0.0 -5.61 887.66 1.2834 -0.0019 0.0002 -0.0000 -0.0000 0.5792 0.0385 0.0115 -0.0060 -0.0025 2.6347 -0.0816 -0.0848 -0.1483 0.0123 284.3 -1.7 -0.5 -0.2 0.1 + -7.5 37.5 96586 -296 -117 43 21 297.0 1.1 0.1 -0.2 -0.3 13.44 2.31 1.63 -0.51 -0.31 -6.8 0.4 0.5 -0.1 0.1 -22.74 420.39 1.2813 0.0010 0.0006 -0.0004 -0.0004 0.5718 0.0317 0.0307 -0.0033 -0.0033 2.7123 -0.1672 -0.2282 -0.0236 -0.0011 285.9 0.4 -0.4 -0.2 -0.2 + -7.5 52.5 101170 -131 -136 30 50 299.4 0.8 1.1 -0.3 -0.4 16.25 0.84 1.02 -0.25 -0.41 -9.3 0.0 -0.0 0.0 0.0 -37.07 -0.00 1.2813 0.0001 0.0009 -0.0004 -0.0006 0.6002 0.0309 0.0181 0.0009 -0.0032 2.8997 -0.3865 -0.1999 -0.0322 0.0138 287.8 -0.5 0.3 -0.3 -0.3 + -7.5 67.5 101077 -63 -81 1 49 299.6 0.3 0.7 -0.2 -0.3 16.63 0.61 0.70 -0.15 -0.33 -9.3 0.1 -0.0 0.0 -0.0 -62.99 -0.00 1.2814 -0.0004 0.0007 -0.0003 -0.0006 0.6220 0.0193 0.0076 -0.0011 0.0018 2.6190 -0.2478 -0.0593 0.0088 -0.0477 287.2 -0.4 0.4 -0.1 -0.4 + -7.5 82.5 101027 -34 -69 3 30 299.8 0.2 0.5 -0.2 -0.2 16.82 0.19 0.53 -0.12 -0.14 -9.2 -0.0 -0.0 0.0 0.0 -80.37 -0.00 1.2816 -0.0003 0.0006 -0.0003 -0.0006 0.6301 0.0067 0.0045 0.0010 0.0037 2.5359 -0.0855 -0.0392 -0.0343 -0.0731 287.0 -0.1 0.4 -0.2 -0.4 + -7.5 97.5 100989 -31 -86 -22 27 300.1 -0.2 0.5 0.1 -0.1 16.77 0.18 0.72 -0.03 -0.28 -9.3 0.1 0.0 -0.0 -0.0 -31.68 -0.00 1.2821 -0.0006 0.0004 -0.0001 -0.0004 0.6288 0.0176 0.0168 0.0018 -0.0014 2.5696 -0.2325 -0.2094 -0.0552 0.0104 287.2 -0.7 -0.1 -0.1 -0.2 + -7.5 112.5 97556 -79 -55 -9 31 298.0 -0.1 -0.1 -0.3 -0.5 15.86 1.14 1.04 -0.01 -0.49 -6.8 1.0 0.7 0.1 -0.0 29.21 302.99 1.2827 -0.0007 -0.0001 -0.0003 -0.0005 0.6046 0.0343 0.0287 0.0060 -0.0018 2.7613 -0.3957 -0.3337 -0.0713 0.0279 286.3 -1.1 -0.8 -0.5 -0.4 + -7.5 127.5 100964 -186 -88 -15 36 300.4 0.9 0.5 -0.1 -0.6 16.53 1.46 0.75 -0.16 -0.47 -9.4 0.1 0.1 0.0 0.0 47.00 -0.00 1.2830 0.0001 -0.0001 -0.0004 -0.0007 0.6213 0.0259 0.0272 0.0076 0.0057 2.7706 -0.2513 -0.4870 -0.2222 -0.0777 287.6 -0.2 -0.6 -0.4 -0.8 + -7.5 142.5 100395 -162 -78 -17 32 298.3 0.9 0.2 -0.1 -0.3 17.25 0.67 0.70 0.05 -0.44 -6.3 0.4 0.5 0.1 -0.1 75.56 49.12 1.2818 0.0007 0.0004 -0.0002 -0.0005 0.6463 0.0133 0.0158 0.0003 0.0057 2.2946 -0.0451 -0.1187 -0.0263 -0.1018 286.3 0.1 -0.2 0.0 -0.6 + -7.5 157.5 100642 -120 -57 -31 13 300.3 0.3 0.2 0.0 -0.2 17.60 0.14 0.22 0.07 -0.10 -9.3 -0.0 -0.0 0.0 0.0 66.68 18.59 1.2824 0.0003 0.0005 0.0000 -0.0003 0.6534 0.0054 0.0079 0.0003 0.0035 2.2463 -0.0424 -0.0734 0.0016 -0.0339 286.6 0.1 -0.1 0.0 -0.3 + -7.5 172.5 100867 -106 -43 -39 6 300.7 0.2 0.0 -0.0 -0.2 17.50 0.06 0.32 0.04 -0.06 -9.3 -0.0 0.0 -0.0 0.0 39.63 -0.00 1.2828 0.0001 0.0002 0.0001 -0.0002 0.6395 0.0133 0.0126 0.0004 0.0010 2.4276 -0.1257 -0.1296 -0.0071 -0.0036 287.4 -0.5 -0.3 0.0 -0.2 + -7.5 -172.5 100902 -90 -46 -33 7 300.8 0.0 0.0 -0.0 -0.2 17.16 -0.01 0.54 -0.00 -0.08 -9.3 -0.0 0.1 -0.0 0.0 20.68 -0.00 1.2829 -0.0001 -0.0001 0.0002 -0.0002 0.6154 0.0211 0.0246 0.0021 0.0002 2.7374 -0.1951 -0.2645 -0.0219 0.0180 288.3 -0.9 -0.8 -0.1 -0.2 + -7.5 -157.5 100969 -70 -54 -22 14 300.5 0.0 0.2 0.0 -0.2 16.53 -0.18 0.76 -0.15 -0.10 -9.4 -0.0 0.1 -0.0 0.0 10.66 -0.00 1.2827 -0.0004 -0.0002 0.0002 -0.0004 0.5827 0.0212 0.0341 0.0010 -0.0002 3.2103 -0.2052 -0.4164 0.0023 0.0201 289.3 -1.0 -1.1 -0.1 -0.2 + -7.5 -142.5 101061 -61 -71 -10 18 300.0 0.0 0.5 -0.0 -0.2 15.86 -0.06 0.96 -0.22 -0.09 -9.5 0.0 0.1 -0.0 0.0 1.98 0.00 1.2822 -0.0008 -0.0001 0.0000 -0.0005 0.5503 0.0137 0.0339 -0.0041 0.0018 3.7347 -0.1166 -0.4621 0.0752 -0.0535 290.0 -0.7 -0.8 0.0 -0.3 + -7.5 -127.5 101169 -47 -95 4 20 299.0 -0.1 0.8 -0.1 -0.2 14.89 -0.06 1.21 -0.24 -0.12 -9.5 0.0 0.1 -0.0 0.0 -10.80 -0.00 1.2817 -0.0010 0.0001 -0.0003 -0.0006 0.5333 0.0089 0.0342 -0.0096 0.0036 3.9676 -0.0382 -0.5261 0.1519 -0.0741 289.7 -0.5 -0.4 0.2 -0.4 + -7.5 -112.5 101253 -38 -126 23 15 297.9 -0.4 1.2 -0.2 -0.2 13.90 -0.15 1.50 -0.44 -0.08 -9.5 -0.0 0.1 -0.0 0.0 -11.58 -0.00 1.2814 -0.0008 0.0004 -0.0003 -0.0004 0.5313 0.0100 0.0349 -0.0125 0.0049 3.9328 -0.1140 -0.5898 0.1705 -0.1056 288.9 -0.6 0.0 0.2 -0.3 + -7.5 -97.5 101283 -55 -167 32 -1 296.6 -0.3 2.4 -0.2 -0.0 13.24 0.01 2.12 -0.53 0.15 -9.6 0.0 0.2 -0.1 0.0 -12.64 -0.00 1.2814 -0.0005 0.0005 -0.0001 -0.0004 0.5493 0.0116 0.0311 -0.0115 0.0072 3.6021 -0.1981 -0.5674 0.1424 -0.1561 287.7 -0.5 0.8 0.2 -0.2 + -7.5 -82.5 101252 -103 -171 27 -18 293.7 1.8 3.6 -0.2 0.5 12.50 1.25 2.54 -0.31 0.34 -8.9 0.1 0.0 -0.2 -0.1 -0.12 0.00 1.2820 -0.0003 0.0008 -0.0002 -0.0003 0.6195 0.0131 0.0088 -0.0091 -0.0004 2.5011 -0.0617 0.0263 0.1017 -0.0390 285.5 -0.1 1.1 0.1 0.2 + -7.5 -67.5 100058 -178 17 46 37 298.0 0.2 -0.6 -0.4 0.2 17.16 0.86 0.26 -0.24 -0.20 -3.8 -1.2 -0.8 0.8 0.6 18.88 87.67 1.2816 -0.0000 -0.0004 -0.0003 -0.0001 0.6340 0.0268 0.0173 -0.0048 -0.0065 2.4574 -0.2470 -0.2149 0.0541 0.0927 286.7 -0.8 -0.9 -0.1 0.2 + -7.5 -52.5 97741 -114 32 38 27 298.2 -1.6 -1.3 0.2 0.7 15.69 1.91 0.98 -0.78 -1.16 -3.9 -2.1 -0.2 1.0 0.2 -19.35 295.12 1.2823 -0.0014 -0.0009 0.0000 0.0003 0.6072 0.0360 0.0227 -0.0062 -0.0052 2.5669 -0.2412 -0.1822 0.0205 -0.0468 286.7 -1.7 -1.3 0.0 0.2 + -7.5 -37.5 94916 -185 -74 35 62 297.5 1.5 0.5 -0.3 -0.5 12.54 0.02 1.25 -0.04 -0.12 -8.4 0.1 0.3 -0.1 -0.2 -6.75 564.23 1.2823 0.0004 0.0002 -0.0006 -0.0007 0.5504 0.0129 0.0367 0.0019 -0.0054 3.0133 -0.2199 -0.4840 -0.0525 0.1131 286.3 0.2 -0.9 -0.3 -0.2 + -7.5 -22.5 101327 -138 -147 39 52 298.7 -0.1 1.4 -0.1 -0.1 14.86 0.37 1.24 -0.22 -0.22 -9.5 0.0 0.0 -0.0 0.0 0.21 -0.00 1.2813 -0.0002 0.0010 -0.0005 -0.0004 0.5495 0.0107 0.0211 -0.0042 -0.0029 3.5950 -0.0868 -0.2647 0.0122 0.0085 289.1 -0.1 0.4 0.0 -0.1 + -7.5 -7.5 101353 -166 -165 57 37 297.4 0.1 2.2 -0.2 0.0 14.13 0.74 1.47 -0.45 -0.23 -9.5 0.1 -0.0 -0.1 -0.0 15.71 -0.00 1.2815 0.0001 0.0011 -0.0004 -0.0003 0.5619 0.0314 0.0126 -0.0094 0.0005 3.3838 -0.4673 -0.1091 0.1407 0.0116 288.1 -0.5 0.9 0.0 -0.3 + -22.5 7.5 101723 -258 -186 44 21 291.7 1.5 2.4 0.1 0.2 10.54 0.82 1.59 -0.07 -0.01 -8.1 -1.2 -0.2 0.3 -0.4 24.73 -0.00 1.2762 0.0047 0.0026 -0.0003 0.0005 0.5253 0.0328 0.0044 -0.0137 -0.0012 5.3035 -1.0572 -0.5046 0.4116 -0.1899 285.7 0.8 1.8 0.2 0.1 + -22.5 22.5 89798 -407 3 56 18 296.3 4.3 -0.4 -1.6 -0.4 8.00 3.74 1.85 0.26 0.09 -4.5 -2.7 -0.8 0.9 0.3 21.15 1055.42 1.2791 0.0065 0.0004 -0.0013 0.0005 0.5339 0.0506 0.0025 -0.0166 -0.0015 2.6589 -0.4999 0.0627 0.3142 0.0609 283.8 1.6 0.0 -0.3 0.0 + -22.5 37.5 101593 -511 -195 28 -13 297.8 2.3 1.6 0.1 0.0 14.14 2.61 0.91 -0.35 0.23 -9.1 -0.0 -0.6 -0.3 0.1 0.40 -0.00 1.2763 0.0052 0.0015 -0.0005 0.0006 0.5461 0.0467 0.0145 -0.0012 -0.0065 4.0991 -0.7727 -0.5970 -0.2232 0.2251 288.1 1.0 0.5 -0.3 0.4 + -22.5 52.5 101732 -458 -330 8 -10 297.4 2.0 1.8 -0.1 -0.1 13.59 2.37 1.90 -0.18 0.14 -9.3 0.2 0.1 -0.0 0.0 -3.96 -0.00 1.2748 0.0044 0.0029 -0.0001 0.0006 0.5617 0.0455 0.0312 0.0016 -0.0010 3.4040 -0.4938 -0.4095 -0.0723 0.0239 286.6 0.7 0.7 -0.2 0.0 + -22.5 67.5 101825 -332 -309 -2 9 296.3 1.9 2.1 -0.1 0.0 12.61 2.10 1.88 -0.24 0.17 -9.4 0.2 0.0 -0.1 -0.0 -7.62 -0.00 1.2744 0.0038 0.0029 -0.0003 0.0003 0.5430 0.0339 0.0301 -0.0019 0.0015 3.6557 -0.3427 -0.3954 -0.0111 -0.0635 286.3 1.0 1.0 -0.1 0.0 + -22.5 82.5 101841 -230 -268 -13 11 295.3 1.6 2.1 -0.0 -0.0 11.69 1.53 1.73 -0.20 0.12 -9.4 0.1 0.1 -0.1 0.0 -34.67 -0.00 1.2739 0.0036 0.0029 -0.0001 0.0005 0.5341 0.0212 0.0245 -0.0035 0.0016 3.7414 -0.1740 -0.3271 0.0095 -0.0549 285.7 1.2 1.2 0.0 0.0 + -22.5 97.5 101779 -198 -222 -44 0 294.3 1.0 1.6 -0.0 -0.1 10.89 0.88 1.56 -0.09 0.06 -9.5 -0.1 0.2 -0.0 0.0 -45.71 -0.00 1.2739 0.0040 0.0032 0.0003 0.0010 0.5329 0.0135 0.0227 -0.0005 0.0021 3.8040 -0.0811 -0.2601 -0.0288 -0.0579 285.2 1.1 1.2 0.1 0.0 + -22.5 112.5 101352 -465 -216 -35 -41 296.0 0.8 2.6 -0.3 -0.0 12.54 2.88 2.17 -0.68 0.30 -6.3 2.5 0.3 -0.7 0.1 -23.52 -0.00 1.2781 0.0068 0.0036 0.0002 0.0012 0.5543 0.0215 0.0294 0.0051 0.0016 4.6036 0.3582 -0.7320 -0.3614 -0.2461 287.2 2.5 1.4 -0.3 0.2 + -22.5 127.5 96591 -556 -61 0 -44 299.5 7.5 0.7 -1.6 -0.2 6.31 2.51 1.35 0.53 0.48 -4.3 -1.9 -0.1 -0.4 -0.0 9.18 411.47 1.2800 0.0087 0.0022 -0.0008 0.0012 0.5900 0.0345 0.0136 -0.0044 -0.0044 2.0538 -0.3921 0.0039 0.2345 0.0491 284.5 3.7 0.4 -0.5 0.4 + -22.5 142.5 98918 -553 -102 -15 -33 299.1 6.4 0.7 -1.5 -0.4 7.49 2.91 1.73 0.63 0.71 -5.9 -1.7 -0.3 0.4 -0.1 39.16 209.13 1.2780 0.0079 0.0019 -0.0007 0.0009 0.5762 0.0349 0.0049 0.0034 -0.0030 2.3469 -0.3822 0.0547 0.1696 0.0859 285.4 3.3 0.8 -0.8 0.3 + -22.5 157.5 101449 -365 -223 -40 -59 296.4 2.1 1.7 -0.0 0.0 12.30 2.60 1.64 -0.13 0.29 -9.5 0.1 0.0 -0.0 -0.0 50.23 -0.00 1.2741 0.0050 0.0030 -0.0000 0.0010 0.5581 0.0223 0.0265 0.0072 0.0045 3.5003 -0.1530 -0.4269 -0.1784 -0.0719 285.7 1.9 0.7 -0.4 0.1 + -22.5 172.5 101362 -290 -248 -23 -59 296.5 1.8 2.1 -0.1 0.1 12.96 2.08 2.12 -0.32 0.35 -9.3 0.1 0.0 -0.1 0.0 55.17 -0.00 1.2745 0.0042 0.0036 0.0001 0.0007 0.5793 0.0129 0.0256 0.0008 0.0026 3.1768 -0.0669 -0.3250 -0.0331 -0.0573 285.2 1.7 1.3 -0.2 0.2 + -22.5 -172.5 101377 -256 -216 -33 -45 296.6 1.6 2.0 -0.2 0.1 13.34 1.81 1.94 -0.33 0.38 -9.2 0.1 -0.0 -0.1 0.0 31.06 -0.00 1.2748 0.0035 0.0035 0.0001 0.0004 0.5959 0.0114 0.0175 -0.0036 0.0013 2.9416 -0.0840 -0.1857 0.0162 -0.0609 285.0 1.4 1.4 0.0 0.2 + -22.5 -157.5 101445 -203 -186 -52 1 296.8 1.4 1.9 -0.1 0.2 13.56 1.75 1.71 -0.15 0.26 -9.2 0.1 -0.1 0.0 0.0 6.21 -0.00 1.2745 0.0029 0.0031 -0.0000 0.0003 0.5981 0.0171 0.0151 -0.0018 -0.0017 2.9085 -0.1764 -0.1705 -0.0024 0.0132 285.0 1.0 1.2 0.0 0.2 + -22.5 -142.5 101540 -124 -142 -34 37 297.1 1.2 1.7 -0.1 0.0 13.78 1.53 1.53 -0.02 -0.01 -9.2 0.2 -0.1 0.0 -0.1 -8.34 -0.00 1.2738 0.0021 0.0028 -0.0001 0.0000 0.5925 0.0217 0.0125 0.0005 -0.0044 2.9653 -0.2302 -0.1368 -0.0220 0.0544 285.4 0.4 1.2 0.0 0.2 + -22.5 -127.5 101704 -54 -123 2 41 296.8 1.1 1.6 -0.1 0.0 13.23 1.17 1.29 -0.16 0.03 -9.3 0.1 -0.1 -0.0 -0.0 -10.18 -0.00 1.2729 0.0014 0.0025 -0.0004 -0.0002 0.5738 0.0160 0.0083 0.0013 -0.0065 3.1746 -0.1869 -0.0880 -0.0223 0.0768 285.6 0.4 1.2 -0.2 0.2 + -22.5 -112.5 101878 -27 -147 9 29 295.8 1.0 1.6 -0.2 0.2 12.04 0.85 1.26 -0.19 0.11 -9.4 0.0 -0.0 -0.0 -0.0 -3.07 -0.00 1.2719 0.0015 0.0021 -0.0005 0.0003 0.5463 0.0073 0.0105 0.0007 -0.0087 3.5311 -0.0957 -0.1422 -0.0075 0.1274 285.6 0.7 1.1 -0.2 0.5 + -22.5 -97.5 101996 -66 -199 -10 4 293.7 0.8 2.0 -0.0 0.2 10.48 0.69 1.37 -0.01 0.13 -9.5 0.0 0.0 0.0 -0.0 -1.55 -0.00 1.2715 0.0025 0.0026 -0.0002 0.0008 0.5235 0.0020 0.0083 -0.0005 -0.0063 3.9543 0.0472 -0.1417 0.0114 0.0997 284.8 0.8 1.5 0.0 0.5 + -22.5 -82.5 101872 -149 -197 -0 -18 291.1 0.9 2.2 0.0 0.3 9.11 0.62 1.19 0.07 0.14 -9.6 0.0 0.0 0.1 -0.0 6.73 0.00 1.2728 0.0042 0.0032 0.0000 0.0007 0.5058 0.0026 0.0111 0.0003 -0.0021 4.7447 0.0527 -0.3215 -0.0022 0.0598 284.0 1.0 1.5 0.1 0.3 + -22.5 -67.5 58419 39 53 -23 13 274.8 2.6 0.7 -0.4 -0.1 2.39 1.58 1.08 0.14 0.62 -6.9 0.1 0.3 0.2 -0.0 43.26 4628.35 1.2707 0.0068 0.0030 -0.0004 0.0005 0.4193 -0.0177 -0.0174 0.0026 -0.0059 4.2696 0.4113 0.3054 -0.0286 -0.1619 263.5 3.7 2.0 -0.3 0.4 + -22.5 -52.5 97412 -367 -2 51 40 297.6 2.4 -0.0 -1.1 0.1 12.46 3.11 1.31 -0.16 -0.03 -4.5 -3.1 -0.1 1.0 0.5 -2.16 338.68 1.2776 0.0040 0.0010 -0.0011 0.0003 0.5726 0.0288 0.0020 -0.0039 0.0044 2.7343 -0.2611 0.0690 0.0695 -0.0302 286.4 1.0 0.2 -0.5 0.0 + -22.5 -37.5 101684 -352 -176 48 92 297.4 1.4 1.9 -0.1 0.1 13.61 1.97 1.33 -0.20 0.07 -9.2 0.3 -0.2 -0.0 -0.1 -9.16 -0.00 1.2742 0.0039 0.0026 -0.0003 0.0001 0.5771 0.0384 0.0073 -0.0053 -0.0076 3.1725 -0.3986 -0.0985 0.0661 0.0945 286.1 0.3 1.3 0.0 0.4 + -22.5 -22.5 101933 -220 -227 62 89 296.2 1.5 2.2 -0.1 0.0 12.25 1.43 1.70 -0.31 -0.14 -9.4 0.1 -0.0 -0.1 -0.1 -1.54 -0.00 1.2731 0.0039 0.0033 -0.0002 0.0000 0.5473 0.0203 0.0080 -0.0111 -0.0145 3.5521 -0.1961 -0.1236 0.1269 0.1716 286.0 1.0 1.8 0.3 0.5 + -22.5 -7.5 102015 -212 -211 43 37 294.1 1.2 2.2 -0.0 0.2 10.71 1.22 1.55 -0.19 0.08 -9.5 0.1 0.0 -0.0 -0.0 13.39 -0.00 1.2730 0.0044 0.0032 -0.0002 0.0002 0.5219 0.0153 0.0015 -0.0079 -0.0103 4.0597 -0.0800 -0.0622 0.0623 0.0729 285.4 1.2 2.0 0.1 0.4 + -37.5 7.5 101956 -22 -95 -47 21 286.7 1.4 2.1 0.2 0.1 7.35 0.72 1.05 0.08 0.00 -8.9 0.2 0.1 -0.0 -0.1 22.83 0.00 1.2501 0.0098 0.0069 -0.0001 0.0006 0.5577 0.0131 0.0059 -0.0017 -0.0025 3.4038 -0.0779 -0.0684 0.0308 -0.0269 276.7 1.9 2.2 0.2 0.2 + -37.5 22.5 101680 -164 -85 -15 20 290.5 1.6 1.9 -0.0 0.1 8.90 0.86 1.25 -0.12 0.08 -9.3 -0.3 0.2 -0.0 -0.1 29.58 0.00 1.2537 0.0094 0.0063 -0.0006 0.0009 0.5423 0.0095 0.0072 -0.0008 -0.0043 3.7604 -0.1856 -0.0455 0.0073 0.0367 279.9 1.9 2.0 -0.1 0.4 + -37.5 37.5 101824 -251 -69 -33 37 290.0 1.2 1.9 -0.0 0.3 9.03 0.93 1.20 -0.03 0.30 -8.8 0.3 0.1 0.1 0.1 30.61 0.00 1.2544 0.0092 0.0059 -0.0007 0.0015 0.5530 0.0183 0.0050 -0.0042 -0.0014 3.4387 -0.2306 -0.0367 0.0586 0.0172 279.7 1.4 2.0 0.1 0.6 + -37.5 52.5 102011 -260 -75 -40 74 288.7 1.5 1.9 -0.1 0.4 8.54 1.03 1.08 -0.13 0.35 -8.5 0.4 0.0 -0.0 0.2 36.90 0.00 1.2534 0.0096 0.0060 -0.0009 0.0018 0.5543 0.0194 0.0046 -0.0062 -0.0011 3.3900 -0.1788 -0.0125 0.0770 0.0260 278.8 1.7 2.0 0.0 0.7 + -37.5 67.5 102078 -109 -82 -87 130 287.9 1.9 2.1 -0.1 0.4 8.09 1.22 1.06 -0.04 0.27 -8.5 0.5 -0.1 0.0 -0.0 22.77 0.00 1.2520 0.0105 0.0063 -0.0011 0.0016 0.5540 0.0227 0.0074 -0.0042 -0.0034 3.4010 -0.2099 -0.0740 0.0481 0.0754 278.0 2.0 2.0 0.0 0.6 + -37.5 82.5 102021 98 -78 -109 126 286.9 2.0 2.1 0.1 0.2 7.60 1.15 1.00 0.11 0.06 -8.6 0.4 -0.1 0.1 -0.2 4.78 0.00 1.2500 0.0110 0.0062 -0.0007 0.0011 0.5511 0.0226 0.0096 -0.0019 -0.0039 3.4238 -0.2078 -0.1237 0.0139 0.0672 277.1 2.2 1.9 0.1 0.4 + -37.5 97.5 101903 178 -21 -88 37 286.1 1.6 2.0 0.2 0.1 7.16 0.80 0.89 0.13 0.01 -8.8 0.2 0.0 0.1 -0.1 -21.84 0.00 1.2482 0.0106 0.0065 -0.0003 0.0008 0.5464 0.0197 0.0099 -0.0017 -0.0043 3.4969 -0.1697 -0.1414 0.0017 0.0492 276.4 1.9 2.0 0.3 0.3 + -37.5 112.5 101729 60 63 -56 -30 286.8 1.0 1.9 0.1 0.0 7.23 0.55 0.91 0.00 -0.04 -9.1 0.1 0.2 -0.1 -0.1 -36.65 0.00 1.2486 0.0106 0.0072 -0.0001 0.0008 0.5409 0.0173 0.0099 -0.0013 -0.0043 3.5953 -0.1255 -0.0683 0.0117 0.0187 276.9 1.7 2.1 0.2 0.2 + -37.5 127.5 101732 -76 137 -61 -43 287.1 1.0 2.0 0.0 0.1 7.54 0.68 1.03 -0.10 -0.01 -8.2 0.3 -0.0 -0.4 -0.3 -30.09 0.00 1.2499 0.0116 0.0072 -0.0004 0.0011 0.5491 0.0229 0.0115 -0.0013 -0.0025 3.6361 -0.2054 -0.1187 -0.0097 -0.0041 277.4 2.0 2.1 0.0 0.3 + -37.5 142.5 98971 -298 129 -69 -71 287.2 4.6 2.0 -0.2 0.3 6.50 0.65 0.58 0.01 0.04 -6.4 -0.5 0.4 0.2 -0.1 1.50 230.34 1.2508 0.0141 0.0071 -0.0006 0.0014 0.5409 0.0261 0.0077 0.0013 -0.0011 3.3889 -0.3557 -0.0868 -0.0314 0.0146 277.2 3.5 2.2 -0.1 0.4 + -37.5 157.5 101647 -118 102 -81 -32 289.6 2.1 2.1 -0.0 0.3 8.49 1.51 1.04 -0.03 0.18 -9.1 0.4 -0.3 -0.1 -0.1 10.31 0.00 1.2502 0.0125 0.0062 -0.0005 0.0011 0.5441 0.0216 0.0057 -0.0004 -0.0012 3.5474 -0.1520 -0.0927 -0.0201 0.0244 278.7 2.6 2.0 0.0 0.3 + -37.5 172.5 101582 -1 64 -121 -7 288.7 1.9 2.2 -0.1 0.3 8.22 1.28 1.06 -0.00 0.27 -8.8 0.5 -0.1 0.1 0.0 26.93 0.00 1.2494 0.0119 0.0068 -0.0006 0.0012 0.5472 0.0206 0.0058 0.0034 -0.0009 3.4755 -0.1379 -0.0520 -0.0805 0.0394 278.0 2.4 2.2 -0.2 0.4 + -37.5 -172.5 101578 56 46 -147 29 288.3 2.3 2.3 0.0 0.3 8.21 1.37 1.09 0.08 0.19 -8.8 0.3 -0.2 0.0 -0.1 15.04 0.00 1.2491 0.0118 0.0070 -0.0005 0.0011 0.5495 0.0185 0.0083 0.0032 0.0010 3.4419 -0.1017 -0.0887 -0.0664 -0.0156 277.6 2.6 2.2 -0.1 0.3 + -37.5 -157.5 101628 137 -2 -159 11 288.0 2.2 2.5 0.1 0.3 8.22 1.36 1.23 0.10 0.23 -8.7 0.4 -0.1 0.1 -0.1 1.79 0.00 1.2492 0.0116 0.0073 -0.0006 0.0010 0.5545 0.0181 0.0097 0.0009 0.0029 3.3608 -0.1246 -0.1030 -0.0552 -0.0587 277.3 2.6 2.3 0.0 0.2 + -37.5 -142.5 101720 226 -15 -128 -28 287.7 2.0 2.5 0.1 0.3 8.10 1.20 1.29 0.13 0.20 -8.7 0.4 -0.0 0.2 -0.0 -9.70 0.00 1.2496 0.0112 0.0076 -0.0003 0.0009 0.5621 0.0169 0.0097 0.0008 0.0021 3.2672 -0.1031 -0.0957 -0.0260 -0.0387 277.0 2.4 2.4 0.0 0.2 + -37.5 -127.5 101814 242 -12 -72 -60 287.6 1.9 2.4 0.2 0.3 8.10 1.25 1.28 0.18 0.22 -8.5 0.5 0.1 0.1 -0.0 -11.49 0.00 1.2499 0.0108 0.0076 -0.0002 0.0006 0.5668 0.0180 0.0129 -0.0019 0.0020 3.2032 -0.1394 -0.1394 0.0150 -0.0522 276.9 2.4 2.3 0.2 0.2 + -37.5 -112.5 101918 276 -19 -57 -56 287.6 2.1 2.7 0.2 0.5 8.12 1.27 1.33 0.17 0.32 -8.5 0.4 -0.1 0.1 -0.1 -10.11 0.00 1.2500 0.0105 0.0075 -0.0001 0.0009 0.5701 0.0143 0.0094 -0.0011 0.0016 3.1403 -0.0967 -0.0852 0.0078 -0.0395 276.8 2.5 2.4 0.2 0.4 + -37.5 -97.5 102018 260 -55 -65 -2 287.4 2.0 2.6 0.2 0.6 7.97 1.14 1.26 0.18 0.31 -8.6 0.2 -0.1 0.1 -0.1 -4.29 0.00 1.2494 0.0100 0.0074 0.0002 0.0012 0.5669 0.0074 0.0056 -0.0015 -0.0019 3.1957 -0.0060 -0.0646 0.0199 0.0423 276.7 2.5 2.5 0.3 0.6 + -37.5 -82.5 101996 152 -86 -70 1 286.8 1.5 2.5 0.0 0.4 7.50 0.80 1.12 0.03 0.24 -8.9 -0.0 -0.1 0.1 0.1 4.27 0.00 1.2488 0.0101 0.0077 0.0003 0.0014 0.5551 0.0023 0.0032 0.0010 -0.0020 3.4200 0.0597 -0.0446 -0.0120 0.0459 276.6 2.4 2.6 0.1 0.6 + -37.5 -67.5 96973 -301 -66 -65 19 290.3 6.8 1.9 0.1 0.5 5.39 1.39 1.12 -0.12 0.35 -5.8 -2.1 0.4 0.2 0.0 19.01 383.48 1.2535 0.0134 0.0073 -0.0001 0.0014 0.5610 0.0028 -0.0023 0.0054 -0.0011 2.4683 -0.1911 0.1217 -0.0295 -0.0184 277.6 5.1 2.7 -0.2 0.6 + -37.5 -52.5 101572 -239 -98 -53 36 289.3 3.0 2.7 -0.1 0.4 8.63 1.86 1.27 -0.03 0.43 -8.3 0.6 -0.7 -0.0 0.1 2.63 0.00 1.2521 0.0112 0.0064 -0.0008 0.0016 0.5615 0.0054 0.0006 -0.0004 0.0011 3.6546 0.1163 -0.0517 0.0084 -0.1097 278.6 3.4 2.4 -0.2 0.6 + -37.5 -37.5 101647 -110 -128 -61 118 288.3 1.9 2.4 -0.0 0.4 8.25 1.25 1.19 -0.01 0.36 -8.7 0.4 -0.1 0.0 0.2 -5.08 0.00 1.2506 0.0104 0.0064 -0.0008 0.0019 0.5624 0.0091 0.0023 -0.0007 0.0022 3.3207 0.0141 -0.0270 -0.0127 -0.0207 277.6 2.5 2.3 -0.1 0.6 + -37.5 -22.5 101768 -4 -131 -46 143 287.5 1.9 2.4 0.1 0.3 8.04 1.23 1.18 0.05 0.20 -8.3 0.6 0.0 0.1 0.0 17.38 0.00 1.2501 0.0107 0.0069 -0.0003 0.0014 0.5689 0.0146 0.0056 0.0005 0.0011 3.1861 -0.0824 -0.0202 -0.0137 -0.0078 276.9 2.4 2.3 0.1 0.4 + -37.5 -7.5 101915 50 -124 -30 84 286.9 1.8 2.3 0.1 0.1 7.75 1.07 1.14 0.08 0.07 -8.1 0.5 0.1 0.0 -0.1 20.27 0.00 1.2499 0.0105 0.0071 -0.0000 0.0008 0.5682 0.0142 0.0055 -0.0001 -0.0037 3.2199 -0.0599 -0.0339 0.0085 0.0477 276.6 2.3 2.3 0.2 0.3 + -52.5 7.5 99757 -6 -149 -54 -103 273.5 1.2 1.6 0.1 -0.3 3.30 0.36 0.41 0.02 -0.03 -7.5 0.7 0.3 0.1 -0.2 26.86 0.00 1.2109 0.0136 0.0084 0.0002 -0.0011 0.5526 0.0164 0.0074 -0.0014 -0.0019 2.8398 -0.3092 -0.1342 0.0366 0.0522 263.9 1.6 1.4 0.1 -0.4 + -52.5 22.5 99784 -74 -17 -64 -111 273.4 0.9 1.6 0.1 -0.0 3.28 0.30 0.42 0.04 0.02 -7.1 0.4 0.4 0.1 -0.1 37.05 0.00 1.2113 0.0120 0.0092 0.0001 -0.0009 0.5556 0.0121 0.0097 -0.0021 -0.0024 2.7561 -0.2308 -0.1505 0.0283 0.0388 263.8 1.2 1.6 0.2 -0.2 + -52.5 37.5 99804 -85 95 -74 -27 274.4 0.9 1.5 0.1 0.2 3.48 0.30 0.43 0.03 0.07 -7.6 0.2 0.4 -0.0 0.0 44.84 0.00 1.2128 0.0115 0.0098 -0.0002 0.0001 0.5551 0.0118 0.0138 -0.0024 -0.0000 2.8213 -0.1977 -0.2127 0.0202 0.0094 264.5 1.0 1.7 0.1 0.1 + -52.5 52.5 99814 2 158 -97 105 274.6 1.0 1.5 -0.0 0.2 3.52 0.33 0.45 0.02 0.09 -7.7 0.2 0.3 0.0 0.1 43.75 0.00 1.2136 0.0123 0.0099 -0.0006 0.0011 0.5573 0.0141 0.0145 -0.0031 0.0021 2.8021 -0.2167 -0.2290 0.0042 -0.0125 264.6 1.3 1.8 0.0 0.4 + -52.5 67.5 99704 194 188 -109 159 274.9 1.2 1.5 -0.1 0.2 3.58 0.40 0.45 0.01 0.06 -7.8 0.4 0.3 0.0 0.2 35.46 0.00 1.2135 0.0134 0.0094 -0.0007 0.0014 0.5554 0.0169 0.0152 -0.0022 0.0039 2.8722 -0.2454 -0.2285 -0.0066 -0.0459 264.7 1.6 1.7 0.0 0.4 + -52.5 82.5 99548 348 173 -86 102 274.2 1.4 1.4 -0.1 0.1 3.47 0.44 0.41 0.00 0.05 -7.7 0.5 0.3 0.0 0.0 24.20 0.00 1.2125 0.0142 0.0086 -0.0006 0.0009 0.5531 0.0181 0.0139 -0.0016 0.0034 2.8892 -0.2586 -0.1995 -0.0089 -0.0512 264.2 1.8 1.5 0.1 0.2 + -52.5 97.5 99384 375 169 -51 -25 275.3 1.3 1.3 0.1 -0.1 3.72 0.43 0.42 0.06 -0.02 -8.0 0.3 0.3 0.0 -0.1 3.72 0.00 1.2130 0.0135 0.0079 -0.0003 -0.0003 0.5468 0.0157 0.0135 0.0002 0.0011 3.0010 -0.2088 -0.1914 -0.0322 -0.0140 264.9 1.6 1.3 0.2 -0.2 + -52.5 112.5 99499 246 204 12 -166 275.5 0.9 1.5 0.2 -0.1 3.84 0.32 0.47 0.07 -0.01 -7.4 0.1 0.3 0.1 -0.3 -15.35 0.00 1.2147 0.0121 0.0080 -0.0002 -0.0012 0.5476 0.0137 0.0149 -0.0002 -0.0010 2.9722 -0.1567 -0.2216 -0.0124 0.0092 265.5 1.1 1.4 0.3 -0.3 + -52.5 127.5 99748 84 295 33 -193 277.6 0.7 1.3 0.1 0.0 4.39 0.27 0.47 0.06 0.01 -8.0 -0.0 0.3 0.1 -0.1 -23.03 0.00 1.2182 0.0112 0.0080 -0.0005 -0.0012 0.5471 0.0127 0.0159 -0.0011 -0.0028 3.0584 -0.1489 -0.2245 0.0096 0.0183 267.2 0.7 1.4 0.2 -0.2 + -52.5 142.5 100110 -58 321 17 -127 278.3 0.9 1.3 0.1 0.2 4.67 0.35 0.46 0.06 0.08 -7.3 -0.0 0.2 0.1 -0.2 -18.50 0.00 1.2211 0.0113 0.0079 -0.0007 -0.0006 0.5495 0.0146 0.0152 -0.0018 -0.0025 3.0725 -0.1596 -0.1944 0.0107 0.0415 268.3 0.7 1.4 0.2 0.1 + -52.5 157.5 100352 -109 269 34 -13 279.6 1.1 1.2 -0.0 0.3 4.97 0.44 0.47 0.03 0.14 -7.9 0.2 0.3 0.1 0.1 -18.96 0.00 1.2232 0.0123 0.0076 -0.0010 0.0004 0.5476 0.0186 0.0145 -0.0029 0.0006 3.1872 -0.2634 -0.1938 0.0756 -0.0096 269.3 1.0 1.4 0.1 0.4 + -52.5 172.5 100435 -69 204 21 121 280.1 1.3 1.1 -0.1 0.4 5.11 0.53 0.47 -0.01 0.14 -7.8 0.4 0.3 -0.0 0.0 -19.23 0.00 1.2241 0.0132 0.0073 -0.0010 0.0009 0.5460 0.0204 0.0122 -0.0033 0.0006 3.2221 -0.2973 -0.1705 0.0834 0.0116 269.8 1.3 1.4 0.0 0.5 + -52.5 -172.5 100407 -5 155 10 191 280.4 1.5 1.4 -0.1 0.3 5.19 0.63 0.54 0.00 0.11 -8.1 0.4 0.1 0.1 0.1 -26.53 0.00 1.2241 0.0138 0.0073 -0.0008 0.0007 0.5446 0.0225 0.0107 -0.0015 0.0010 3.2480 -0.3247 -0.1189 0.0288 0.0132 269.9 1.5 1.5 0.0 0.4 + -52.5 -157.5 100390 36 105 40 152 279.8 1.6 1.4 -0.1 0.1 5.07 0.61 0.54 -0.03 0.03 -7.8 0.4 0.2 -0.0 -0.2 -21.84 0.00 1.2234 0.0139 0.0074 -0.0005 0.0000 0.5450 0.0222 0.0113 -0.0012 -0.0004 3.1977 -0.3114 -0.1417 0.0500 0.0410 269.6 1.6 1.4 0.0 0.1 + -52.5 -142.5 100363 17 63 64 44 279.2 1.5 1.2 0.1 -0.0 4.92 0.59 0.47 0.04 -0.02 -8.0 0.4 0.3 0.0 -0.3 -18.05 0.00 1.2225 0.0134 0.0073 0.0002 -0.0007 0.5465 0.0209 0.0129 0.0007 -0.0024 3.1539 -0.3197 -0.1595 0.0017 0.0580 269.1 1.5 1.2 0.2 0.0 + -52.5 -127.5 100299 -22 24 101 -65 279.0 1.3 1.1 0.2 -0.1 4.82 0.52 0.42 0.09 -0.03 -8.0 0.3 0.3 0.1 -0.2 -14.70 0.00 1.2214 0.0125 0.0072 0.0007 -0.0011 0.5457 0.0207 0.0131 0.0014 -0.0031 3.1738 -0.3208 -0.1863 -0.0055 0.0530 268.8 1.1 1.1 0.3 -0.1 + -52.5 -112.5 100232 -88 1 111 -93 278.9 1.2 1.2 0.2 0.1 4.75 0.48 0.43 0.07 0.02 -8.1 0.2 0.2 0.1 -0.1 -10.98 0.00 1.2206 0.0119 0.0074 0.0008 -0.0008 0.5448 0.0179 0.0119 0.0009 -0.0012 3.1786 -0.2863 -0.1651 0.0224 0.0054 268.5 1.0 1.1 0.3 -0.1 + -52.5 -97.5 100196 -151 20 67 -23 278.9 1.2 1.2 0.1 0.2 4.77 0.48 0.45 0.05 0.08 -8.0 0.1 0.1 0.1 -0.0 -8.06 0.00 1.2200 0.0117 0.0080 0.0007 0.0001 0.5451 0.0152 0.0129 0.0006 0.0008 3.1734 -0.2345 -0.1685 0.0014 -0.0218 268.5 1.0 1.2 0.2 0.2 + -52.5 -82.5 100257 -170 -16 -5 76 279.0 1.3 1.4 -0.1 0.4 4.82 0.55 0.49 -0.00 0.15 -8.0 0.2 0.1 0.1 0.1 1.37 0.00 1.2196 0.0119 0.0083 0.0002 0.0010 0.5441 0.0161 0.0125 -0.0006 0.0035 3.2095 -0.2757 -0.1747 0.0172 -0.0481 268.5 1.1 1.3 0.0 0.4 + -52.5 -67.5 100298 -204 -92 -74 146 280.4 2.9 1.6 -0.1 0.3 4.74 0.71 0.57 0.02 0.17 -6.7 0.3 -0.5 -0.4 0.2 11.93 0.00 1.2202 0.0134 0.0082 -0.0004 0.0015 0.5484 0.0151 0.0101 0.0004 0.0034 3.2699 -0.2834 -0.1156 -0.0117 -0.0353 269.2 2.0 1.3 -0.2 0.4 + -52.5 -52.5 100238 -152 -198 -150 158 278.1 2.2 1.5 -0.1 0.2 4.48 0.74 0.52 -0.02 0.11 -6.5 1.2 0.4 -0.1 0.1 7.31 0.00 1.2183 0.0146 0.0080 -0.0008 0.0016 0.5458 0.0175 0.0088 -0.0008 0.0047 3.2970 -0.2417 -0.1674 -0.0218 -0.0699 268.1 2.3 1.3 -0.3 0.4 + -52.5 -37.5 100142 -44 -252 -153 118 275.2 2.0 1.7 -0.1 0.2 3.78 0.61 0.48 -0.02 0.10 -6.2 1.3 0.1 -0.1 -0.1 9.74 0.00 1.2152 0.0153 0.0076 -0.0008 0.0012 0.5505 0.0181 0.0069 -0.0008 0.0039 2.9770 -0.2653 -0.1248 0.0126 -0.0818 266.1 2.4 1.3 -0.2 0.3 + -52.5 -22.5 99965 46 -254 -133 58 274.5 1.8 1.5 -0.0 0.1 3.56 0.55 0.41 0.01 0.05 -7.4 1.0 0.1 0.2 -0.0 20.02 0.00 1.2130 0.0155 0.0073 -0.0004 0.0005 0.5512 0.0193 0.0049 -0.0007 0.0025 2.9161 -0.3394 -0.1018 -0.0103 -0.0272 265.1 2.3 1.2 -0.1 0.1 + -52.5 -7.5 99832 77 -230 -83 -30 273.6 1.4 1.5 0.0 -0.1 3.34 0.44 0.39 0.01 -0.00 -7.1 0.9 0.4 0.1 -0.1 27.44 0.00 1.2115 0.0149 0.0077 -0.0000 -0.0005 0.5520 0.0184 0.0061 -0.0007 -0.0009 2.8204 -0.3200 -0.1305 0.0057 0.0391 264.3 2.1 1.2 0.0 -0.2 + -67.5 7.5 98336 94 21 123 -67 264.2 7.2 4.7 0.6 -0.8 1.77 0.91 0.54 0.18 -0.01 -5.1 -3.5 -2.6 0.6 0.7 14.67 -0.00 1.1789 0.0230 0.0146 0.0027 -0.0006 0.5395 -0.0064 -0.0072 0.0011 0.0034 2.9096 0.5280 0.2857 -0.0341 -0.1482 255.6 4.7 3.0 0.8 -0.5 + -67.5 22.5 98234 112 37 194 -70 264.2 7.0 5.4 0.9 -1.0 1.81 0.88 0.65 0.17 -0.00 -4.7 -3.9 -3.0 0.4 0.7 15.86 -0.00 1.1787 0.0226 0.0156 0.0032 -0.0008 0.5357 -0.0055 -0.0083 0.0017 0.0038 2.9088 0.5696 0.3945 -0.0307 -0.2037 255.7 4.5 3.6 1.0 -0.6 + -67.5 37.5 98403 56 81 197 -56 264.4 6.6 5.2 0.9 -1.1 1.76 0.81 0.64 0.14 0.01 -5.2 -2.9 -2.6 0.5 0.8 24.44 -0.00 1.1785 0.0224 0.0159 0.0034 -0.0006 0.5405 -0.0078 -0.0047 0.0024 0.0035 2.8000 0.5289 0.2976 0.0460 -0.2217 255.4 4.5 3.6 1.0 -0.6 + -67.5 52.5 82438 272 318 203 -56 254.7 5.9 2.1 2.1 -0.2 0.61 0.44 0.09 0.19 0.02 -3.0 -2.3 0.3 -0.8 0.1 38.75 1379.92 1.1741 0.0242 0.0160 0.0037 0.0002 0.5190 -0.0080 0.0131 -0.0039 0.0010 1.7743 0.8157 -0.1845 0.3189 -0.1140 246.6 4.3 1.9 1.5 -0.1 + -67.5 67.5 95697 -9 253 118 -33 260.4 7.9 1.8 2.6 -0.1 1.16 0.82 0.12 0.38 0.03 -5.5 -2.0 0.1 -0.0 -0.1 25.44 239.59 1.1764 0.0244 0.0137 0.0042 0.0001 0.5599 -0.0197 0.0118 -0.0097 0.0021 2.5302 0.5906 -0.1537 0.1987 -0.0462 251.8 5.8 1.5 2.2 -0.1 + -67.5 82.5 92509 200 243 187 -56 261.1 6.3 1.8 2.1 -0.2 1.04 0.68 0.12 0.30 0.02 -3.8 -2.2 -0.0 -0.6 0.2 15.35 489.24 1.1781 0.0241 0.0134 0.0035 0.0001 0.5421 -0.0063 0.0079 -0.0049 0.0014 2.1398 0.6406 -0.1292 0.2673 -0.0476 252.1 4.8 1.6 1.6 0.0 + -67.5 97.5 78758 384 278 258 -82 252.9 5.8 1.3 2.3 0.2 0.62 0.34 0.07 0.19 0.03 -1.8 -2.8 0.5 -1.2 -0.1 1.05 1728.66 1.1759 0.0252 0.0141 0.0033 0.0001 0.4992 0.0027 0.0132 -0.0045 -0.0006 2.1980 0.4181 -0.2091 0.2834 -0.0009 245.6 4.1 1.4 1.6 0.2 + -67.5 112.5 85333 241 248 274 -70 256.6 5.9 1.3 2.1 0.5 0.87 0.51 0.11 0.26 0.09 -2.0 -2.8 0.4 -0.7 -0.3 -17.43 1121.25 1.1788 0.0235 0.0126 0.0033 0.0001 0.5175 -0.0033 0.0080 -0.0040 -0.0010 2.1772 0.6366 -0.1046 0.2089 0.0227 249.5 4.0 1.4 1.7 0.3 + -67.5 127.5 84669 189 240 290 -10 257.0 5.6 1.3 2.2 0.6 0.77 0.45 0.11 0.23 0.09 -2.3 -2.7 0.1 -1.0 -0.2 -34.13 1194.19 1.1796 0.0233 0.0121 0.0033 0.0002 0.5249 -0.0041 0.0093 -0.0030 -0.0007 1.9858 0.5412 -0.1689 0.2167 -0.0188 248.8 3.9 1.3 1.7 0.5 + -67.5 142.5 87704 124 242 275 37 256.5 6.7 1.1 2.7 0.5 0.67 0.53 0.07 0.29 0.08 -2.9 -2.8 -0.3 -0.8 0.2 -43.58 921.49 1.1810 0.0233 0.0116 0.0033 0.0005 0.5556 -0.0120 0.0128 -0.0078 0.0013 1.4983 0.8734 -0.2595 0.3870 -0.1272 249.5 4.3 1.2 1.9 0.6 + -67.5 157.5 98547 -8 179 262 13 262.3 8.8 1.9 1.7 1.1 1.60 1.08 0.27 0.33 0.22 -1.7 -6.1 0.2 0.5 -0.6 -50.28 -0.00 1.1847 0.0228 0.0111 0.0027 0.0012 0.5605 -0.0171 0.0100 -0.0007 0.0017 2.3924 0.7659 -0.0892 0.0776 -0.0396 255.5 5.2 1.5 1.5 0.9 + -67.5 172.5 98466 64 165 267 64 264.3 7.6 2.6 0.7 1.0 1.90 1.00 0.35 0.19 0.21 -4.7 -3.4 -0.9 1.3 -0.2 -55.77 -0.00 1.1861 0.0226 0.0111 0.0020 0.0013 0.5502 -0.0072 0.0071 0.0016 0.0018 2.5228 0.4929 0.0045 -0.0376 -0.0234 256.6 4.8 1.6 1.0 0.9 + -67.5 -172.5 98308 135 168 235 138 265.6 6.5 2.5 0.7 0.8 2.08 0.87 0.34 0.17 0.17 -5.6 -2.1 -0.5 0.7 -0.4 -57.72 0.00 1.1875 0.0221 0.0112 0.0019 0.0010 0.5444 -0.0009 0.0076 0.0015 0.0036 2.6202 0.3317 -0.0479 -0.0512 -0.0476 257.5 4.2 1.7 0.8 0.6 + -67.5 -157.5 98237 151 164 252 165 266.3 5.6 2.7 0.7 0.3 2.18 0.78 0.37 0.15 0.10 -5.8 -1.9 -0.9 0.5 -0.0 -54.39 0.00 1.1882 0.0215 0.0116 0.0021 0.0004 0.5423 0.0024 0.0076 0.0014 0.0045 2.6829 0.2617 -0.0371 -0.0355 -0.0773 258.0 3.7 1.8 0.8 0.3 + -67.5 -142.5 98221 82 183 315 156 267.1 4.7 3.3 0.7 -0.5 2.29 0.68 0.43 0.15 -0.00 -6.3 -1.4 -1.4 0.4 0.5 -47.81 0.00 1.1888 0.0204 0.0122 0.0023 -0.0003 0.5400 0.0029 0.0051 0.0016 0.0050 2.7728 0.1586 0.0617 0.0034 -0.1296 258.6 3.2 2.0 0.8 -0.1 + -67.5 -127.5 98224 -2 186 379 105 267.9 4.0 3.3 0.6 -0.5 2.39 0.59 0.45 0.14 0.01 -6.5 -1.1 -1.1 0.2 0.5 -38.27 0.00 1.1893 0.0191 0.0126 0.0023 -0.0005 0.5387 0.0027 0.0050 0.0010 0.0040 2.8259 0.1353 0.0284 0.0358 -0.0977 259.1 2.6 2.1 0.7 -0.2 + -67.5 -112.5 98245 -107 190 388 70 268.8 3.2 3.0 0.5 -0.3 2.52 0.49 0.44 0.10 0.03 -7.0 -0.9 -0.8 0.3 0.4 -30.87 0.00 1.1898 0.0178 0.0127 0.0023 -0.0003 0.5352 0.0027 0.0046 0.0010 0.0033 2.9505 0.0994 0.0183 0.0463 -0.0794 259.8 2.1 2.0 0.6 -0.1 + -67.5 -97.5 98280 -205 184 338 77 269.6 2.6 2.7 0.3 -0.1 2.62 0.43 0.43 0.07 0.08 -7.3 -0.7 -0.5 0.2 0.4 -23.42 0.00 1.1901 0.0171 0.0127 0.0020 0.0001 0.5325 0.0021 0.0045 0.0018 0.0016 3.0429 0.0610 0.0011 0.0193 -0.0464 260.3 1.7 1.9 0.4 0.1 + -67.5 -82.5 98399 -273 164 263 110 269.3 3.4 2.6 -0.3 -0.2 2.63 0.56 0.41 0.00 0.07 -6.5 -1.5 -0.8 0.8 0.4 -10.63 0.00 1.1897 0.0174 0.0126 0.0015 0.0004 0.5319 0.0003 0.0047 0.0023 0.0022 3.0218 0.0940 0.0329 -0.0319 -0.0792 260.4 2.1 1.7 0.1 0.1 + -67.5 -67.5 95784 -214 146 152 102 266.5 5.0 1.8 0.4 -0.0 2.09 0.78 0.26 0.07 0.08 -5.3 -1.5 -0.4 -0.0 0.5 8.64 241.84 1.1875 0.0192 0.0127 0.0011 0.0008 0.5323 -0.0040 0.0079 -0.0009 0.0024 2.5932 0.4049 -0.0756 0.0639 -0.0777 258.3 3.0 1.4 0.1 0.2 + -67.5 -52.5 98840 -244 206 83 82 264.0 8.1 0.9 0.7 0.4 1.86 1.10 0.15 0.20 0.11 0.6 -8.2 0.2 -0.6 0.2 7.64 0.00 1.1865 0.0207 0.0114 0.0009 0.0012 0.5518 -0.0111 0.0100 -0.0015 0.0023 2.1965 0.8925 -0.1275 0.1047 -0.0468 258.2 4.0 0.7 0.1 0.3 + -67.5 -37.5 98745 -108 158 98 88 264.1 8.1 1.5 0.2 0.2 1.88 1.03 0.18 0.13 0.07 -2.5 -6.5 -0.9 0.6 -0.1 8.22 0.00 1.1845 0.0217 0.0115 0.0009 0.0010 0.5479 -0.0089 0.0079 0.0007 0.0037 2.4342 0.7223 -0.0208 -0.0350 -0.0575 257.5 4.2 0.7 0.1 0.2 + -67.5 -22.5 98599 -8 101 140 63 264.1 8.0 2.8 -0.2 -0.1 1.84 0.98 0.32 0.07 0.02 -3.9 -5.6 -1.6 1.4 0.1 6.14 0.00 1.1824 0.0224 0.0120 0.0012 0.0008 0.5446 -0.0076 0.0017 0.0019 0.0054 2.5895 0.6479 0.1696 -0.0750 -0.0986 256.8 4.4 1.3 0.1 0.0 + -67.5 -7.5 98456 65 48 123 -1 264.2 7.7 4.1 0.2 -0.7 1.80 0.95 0.45 0.11 -0.03 -4.5 -4.8 -2.5 1.2 1.0 8.62 -0.00 1.1803 0.0230 0.0132 0.0018 0.0001 0.5411 -0.0061 -0.0054 0.0011 0.0053 2.7498 0.5396 0.3419 -0.0852 -0.1531 256.1 4.7 2.2 0.4 -0.3 + -82.5 7.5 66654 520 240 259 -65 241.5 6.5 1.5 2.5 -0.4 0.27 0.18 0.04 0.09 0.00 -3.4 -1.1 -0.1 -0.8 0.1 -9.93 2968.14 1.1543 0.0338 0.0197 0.0048 0.0010 0.4504 -0.0008 0.0111 -0.0021 0.0006 2.6844 0.5204 -0.1667 0.3661 -0.0985 235.0 5.0 1.6 1.5 -0.3 + -82.5 22.5 64022 547 249 288 -78 239.7 6.6 1.6 2.6 -0.6 0.24 0.16 0.04 0.08 -0.00 -4.4 -0.7 0.1 -0.5 0.1 -5.53 3270.73 1.1528 0.0349 0.0205 0.0053 0.0010 0.4441 -0.0033 0.0119 -0.0021 0.0002 2.6564 0.4851 -0.1500 0.3192 -0.1150 232.7 5.4 1.7 1.7 -0.3 + -82.5 37.5 61354 572 260 316 -86 235.9 7.3 1.2 3.1 -0.5 0.19 0.15 0.02 0.09 -0.00 -2.3 -1.8 0.1 -1.3 0.4 -0.84 3589.26 1.1513 0.0360 0.0211 0.0056 0.0011 0.4387 -0.0064 0.0139 -0.0029 0.0002 2.2933 0.6667 -0.2978 0.4696 -0.1334 230.1 5.7 1.7 1.9 -0.2 + -82.5 52.5 60042 574 256 331 -84 231.5 9.4 1.5 4.4 -1.0 0.15 0.15 0.01 0.09 -0.00 -1.4 -1.5 2.6 -0.9 0.2 -2.07 3756.25 1.1497 0.0370 0.0213 0.0061 0.0011 0.4443 -0.0131 0.0152 -0.0070 0.0015 1.6828 1.0571 -0.1799 0.6281 -0.2748 228.0 6.2 1.7 2.3 -0.3 + -82.5 67.5 57927 621 267 366 -84 232.1 7.4 1.1 3.4 -0.5 0.13 0.13 0.01 0.08 -0.00 -3.3 -1.1 0.1 -0.9 0.3 -0.16 4008.06 1.1501 0.0373 0.0218 0.0059 0.0013 0.4345 -0.0095 0.0164 -0.0062 -0.0000 1.6977 0.9421 -0.4075 0.6263 -0.1470 226.9 5.8 1.8 2.2 -0.1 + -82.5 82.5 60415 562 253 346 -76 230.9 9.5 0.9 4.7 -0.7 0.12 0.15 0.00 0.10 -0.00 1.7 -3.7 3.2 -2.4 -0.5 -4.59 3719.25 1.1491 0.0371 0.0206 0.0063 0.0011 0.4585 -0.0189 0.0175 -0.0127 0.0012 1.0829 1.3057 -0.2416 0.7816 -0.2672 227.4 6.3 1.6 2.6 -0.2 + -82.5 97.5 63132 520 250 350 -64 232.9 9.6 1.1 4.3 -0.8 0.14 0.16 0.00 0.10 -0.01 1.2 -4.3 -0.7 -2.1 1.1 -15.13 3403.22 1.1493 0.0365 0.0198 0.0062 0.0009 0.4670 -0.0176 0.0156 -0.0114 0.0022 1.1855 1.2080 -0.2881 0.6807 -0.1987 229.0 6.4 1.5 2.6 -0.3 + -82.5 112.5 65578 480 234 340 -51 231.5 11.9 0.5 5.3 -1.1 0.15 0.19 0.00 0.12 -0.01 7.9 -8.8 3.9 -5.0 -0.3 -25.83 3121.46 1.1498 0.0360 0.0187 0.0061 0.0007 0.4763 -0.0200 0.0169 -0.0124 0.0029 1.0447 1.4158 -0.2622 0.8225 -0.2588 230.6 6.4 1.2 2.5 -0.3 + -82.5 127.5 68457 490 239 357 -38 235.3 10.8 0.5 3.9 -1.1 0.18 0.21 0.00 0.13 -0.01 1.4 -4.2 -0.5 -1.7 0.9 -37.46 2787.16 1.1518 0.0348 0.0181 0.0054 0.0005 0.4773 -0.0148 0.0143 -0.0086 0.0047 1.2646 1.3270 -0.3091 0.7305 -0.2737 233.2 6.1 1.2 2.1 -0.4 + -82.5 142.5 71861 518 238 352 -26 239.5 10.3 1.2 3.3 -1.3 0.24 0.24 0.01 0.13 -0.01 6.0 -8.6 -0.7 -2.7 1.4 -41.74 2404.42 1.1540 0.0337 0.0172 0.0051 0.0002 0.4823 -0.0119 0.0121 -0.0060 0.0053 1.5404 1.2168 -0.2101 0.6457 -0.2941 236.2 5.9 1.1 1.9 -0.5 + -82.5 157.5 77401 521 265 320 -20 248.2 7.5 1.2 2.3 -1.0 0.35 0.27 0.03 0.14 -0.01 -2.4 -3.2 -0.3 -0.7 0.5 -37.62 1838.98 1.1570 0.0319 0.0161 0.0049 0.0001 0.4959 -0.0053 0.0096 -0.0026 0.0043 1.9140 0.8232 -0.1394 0.4391 -0.1932 240.4 5.4 0.9 1.8 -0.6 + -82.5 172.5 98927 38 251 202 73 256.2 9.9 1.1 3.3 -1.4 0.56 0.60 0.03 0.35 -0.02 6.1 -6.7 -2.0 -2.5 0.9 -44.99 0.00 1.1610 0.0301 0.0136 0.0052 -0.0005 0.5933 -0.0219 0.0052 -0.0110 0.0047 1.2154 1.2866 -0.1102 0.7035 -0.2212 248.2 6.8 1.0 2.4 -0.8 + -82.5 -172.5 98735 85 268 217 95 252.8 11.0 1.3 3.9 -1.4 0.64 0.68 0.03 0.39 -0.03 12.1 -9.6 -0.3 -4.2 2.5 -47.03 0.00 1.1615 0.0300 0.0138 0.0051 -0.0005 0.5836 -0.0235 0.0074 -0.0114 0.0067 1.3494 1.4195 -0.1432 0.7523 -0.2618 248.6 6.6 1.1 2.4 -0.8 + -82.5 -157.5 98571 126 277 225 106 251.8 11.5 1.3 4.3 -1.5 0.65 0.72 0.03 0.41 -0.03 13.9 -10.9 1.7 -4.6 2.5 -46.69 -0.00 1.1628 0.0295 0.0141 0.0049 -0.0006 0.5801 -0.0244 0.0068 -0.0112 0.0082 1.1981 1.5175 -0.1337 0.8019 -0.2589 249.4 6.3 1.2 2.2 -0.9 + -82.5 -142.5 93097 298 293 282 63 253.2 9.3 1.6 3.4 -1.3 0.73 0.63 0.07 0.33 -0.02 9.0 -8.3 2.4 -4.0 0.9 -43.63 427.00 1.1640 0.0289 0.0150 0.0042 -0.0003 0.5509 -0.0140 0.0058 -0.0072 0.0062 1.6989 1.2755 -0.1022 0.6579 -0.2541 249.0 5.4 1.4 1.7 -0.7 + -82.5 -127.5 89159 329 300 280 20 253.7 8.2 1.5 2.8 -1.0 0.72 0.56 0.08 0.28 -0.01 4.4 -6.8 0.9 -3.4 1.0 -40.52 767.24 1.1638 0.0290 0.0156 0.0039 -0.0000 0.5356 -0.0107 0.0065 -0.0070 0.0039 1.8091 1.0902 -0.1014 0.5723 -0.1988 247.9 5.1 1.5 1.5 -0.5 + -82.5 -112.5 80355 429 312 268 -23 250.4 6.7 1.6 2.1 -0.6 0.58 0.37 0.08 0.17 0.01 -0.1 -4.5 -0.0 -2.2 0.5 -33.87 1569.57 1.1617 0.0295 0.0168 0.0036 0.0005 0.4985 -0.0030 0.0067 -0.0047 0.0019 1.9426 0.8013 -0.0468 0.4603 -0.0950 244.1 4.4 1.5 1.1 -0.3 + -82.5 -97.5 74298 453 300 236 -42 245.5 6.4 1.6 2.1 -0.3 0.43 0.27 0.06 0.13 0.01 -1.4 -1.6 -0.4 -0.7 0.4 -29.19 2164.18 1.1600 0.0302 0.0178 0.0035 0.0009 0.4783 0.0009 0.0079 -0.0032 0.0021 1.8536 0.7998 -0.0267 0.5033 -0.0833 240.8 4.0 1.5 0.9 -0.1 + -82.5 -82.5 86490 168 238 150 -33 248.9 8.7 1.2 3.1 -0.6 0.43 0.42 0.02 0.23 -0.01 -1.2 -3.5 0.8 -1.4 0.4 -28.77 1049.66 1.1602 0.0292 0.0159 0.0038 0.0007 0.5577 -0.0233 0.0098 -0.0132 0.0052 1.1929 1.2729 -0.1717 0.6642 -0.1663 244.1 5.2 1.4 1.5 -0.3 + -82.5 -67.5 96005 -59 176 91 -31 251.8 10.1 1.0 3.5 -0.5 0.55 0.60 0.02 0.33 -0.01 3.0 -5.5 1.1 -2.7 0.8 -25.52 256.20 1.1619 0.0284 0.0147 0.0038 0.0008 0.5957 -0.0337 0.0075 -0.0173 0.0039 1.2278 1.4570 -0.1941 0.7441 -0.1804 247.5 5.9 1.3 1.7 -0.2 + -82.5 -52.5 88361 187 187 156 -31 252.8 7.3 1.4 2.2 -0.2 0.52 0.42 0.06 0.20 0.02 -0.6 -3.5 0.4 -1.4 0.3 -21.98 867.46 1.1618 0.0285 0.0155 0.0033 0.0010 0.5471 -0.0146 0.0056 -0.0104 0.0010 1.6486 0.9553 -0.0551 0.5259 -0.0920 246.2 5.0 1.3 1.2 0.0 + -82.5 -37.5 84477 262 191 169 -23 250.2 8.0 1.3 2.6 -0.3 0.46 0.41 0.04 0.21 0.00 1.4 -5.3 0.1 -2.5 0.4 -18.26 1207.48 1.1602 0.0294 0.0160 0.0035 0.0010 0.5299 -0.0147 0.0079 -0.0115 0.0030 1.6939 1.0651 -0.1251 0.5782 -0.1755 244.4 5.1 1.2 1.3 -0.1 + -82.5 -22.5 75291 429 210 211 -33 247.6 6.0 1.5 1.9 -0.3 0.40 0.26 0.06 0.12 0.01 -1.9 -1.6 0.6 -1.3 -0.2 -13.77 2061.01 1.1583 0.0308 0.0176 0.0037 0.0010 0.4840 -0.0022 0.0096 -0.0052 0.0019 2.4558 0.5672 -0.0598 0.4039 -0.1384 240.8 4.5 1.3 1.1 -0.1 + -82.5 -7.5 68257 516 232 241 -49 244.5 5.3 1.6 1.7 -0.3 0.32 0.17 0.05 0.08 0.00 -4.3 -0.5 0.1 -0.4 0.0 -10.43 2782.40 1.1562 0.0328 0.0192 0.0042 0.0010 0.4542 0.0019 0.0112 -0.0016 0.0003 2.9026 0.2761 -0.0948 0.2403 -0.0823 236.9 4.6 1.4 1.1 -0.2 diff --git a/src/test/resources/gpt2-grid/gpt2_5_extract.grd b/src/test/resources/gpt-grid/gpt2_5_extract.grd similarity index 90% rename from src/test/resources/gpt2-grid/gpt2_5_extract.grd rename to src/test/resources/gpt-grid/gpt2_5_extract.grd index 59789f9dc1..2108fecf3f 100644 --- a/src/test/resources/gpt2-grid/gpt2_5_extract.grd +++ b/src/test/resources/gpt-grid/gpt2_5_extract.grd @@ -1,4 +1,3 @@ -% This is not an original GPT2 grid, it has been edited for test purposes and should not be used except for test % lat lon p:a0 A1 B1 A2 B2 T:a0 A1 B1 A2 B2 Q:a0 A1 B1 A2 B2 dT:a0 A1 B1 A2 B2 undu Hs h:a0 A1 B1 A2 B2 w:a0 A1 B1 A2 B2 52.5 12.5 100913 30 -28 9 -14 283.4 -9.1 -2.9 -0.0 0.3 6.02 -2.61 -1.33 0.29 0.32 -4.6 1.0 -0.3 -0.1 -0.0 41.28 52.71 1.2352 -0.0211 -0.0108 0.0008 0.0009 0.5583 -0.0126 -0.0125 0.0040 0.0012 52.5 17.5 100434 46 -48 -8 -16 282.7 -10.0 -3.0 0.0 0.2 5.88 -2.85 -1.38 0.39 0.36 -4.3 1.1 -0.2 -0.2 0.1 34.11 93.18 1.2344 -0.0225 -0.0111 0.0010 0.0007 0.5578 -0.0120 -0.0115 0.0049 0.0017 diff --git a/src/test/resources/gpt-grid/gpt3_15.grd b/src/test/resources/gpt-grid/gpt3_15.grd new file mode 100644 index 0000000000..06a7e07cf2 --- /dev/null +++ b/src/test/resources/gpt-grid/gpt3_15.grd @@ -0,0 +1,289 @@ +% lat lon p:a0 A1 B1 A2 B2 T:a0 A1 B1 A2 B2 Q:a0 A1 B1 A2 B2 dT:a0 A1 B1 A2 B2 undu Hs a_h:a0 A1 B1 A2 B2 a_w:a0 A1 B1 A2 B2 lambda:a0 A1 B1 A2 B2 Tm:a0 A1 B1 A2 B2 Gn_h:a0 A1 B1 A2 B2 Ge_h:a0 A1 B1 A2 B2 Gn_w:a0 A1 B1 A2 B2 Ge_w:a0 A1 B1 A2 B2 + 82.5 7.5 101260 -239 308 -169 -108 261.7 -10.6 -5.2 2.7 0.3 1.88 -1.45 -0.64 0.49 0.28 -0.0 3.9 1.4 -1.0 1.4 29.80 -0.00 1.18699 -0.03410 -0.01032 0.00476 0.00373 0.56673 -0.00107 0.00343 -0.00174 0.00244 1.9560 -0.4181 -0.1116 0.1128 -0.1728 257.0 -8.4 -4.0 2.4 1.2 -13.52 0.98 2.64 -0.11 0.76 -7.81 0.19 -0.26 1.93 0.69 -1.82 0.03 0.45 0.11 -0.11 0.62 -0.04 -0.38 0.13 0.20 + 82.5 22.5 101209 -249 315 -141 -97 262.5 -9.8 -5.5 2.7 0.2 1.95 -1.39 -0.70 0.47 0.28 -0.8 3.2 1.9 -1.3 1.6 25.99 -0.00 1.18640 -0.03410 -0.01049 0.00484 0.00368 0.56361 -0.00524 0.00420 -0.00069 0.00276 2.0569 -0.2638 -0.1576 0.1266 -0.2021 257.2 -8.0 -4.1 2.3 1.2 -10.48 1.55 3.24 -0.60 0.81 -8.03 0.79 -0.19 1.99 0.79 -1.78 0.17 0.42 0.05 -0.12 -0.17 -0.05 -0.22 -0.07 0.34 + 82.5 37.5 101198 -203 328 -118 -95 262.5 -9.8 -5.7 2.5 0.3 1.93 -1.38 -0.72 0.45 0.29 -1.3 2.9 2.1 -1.0 1.6 20.77 -0.00 1.18676 -0.03459 -0.01070 0.00495 0.00381 0.56340 -0.00476 0.00381 -0.00118 0.00305 2.0776 -0.2508 -0.1828 0.0898 -0.2222 257.1 -8.1 -4.2 2.3 1.3 -8.35 2.17 3.72 -1.31 0.73 -9.47 1.07 0.05 2.02 1.28 -1.67 0.41 0.41 -0.31 -0.31 -0.54 -0.24 -0.19 0.14 0.47 + 82.5 52.5 101202 -129 343 -93 -88 262.4 -9.8 -5.7 2.3 0.4 1.91 -1.38 -0.72 0.42 0.31 -1.9 2.3 1.8 -0.9 1.6 17.04 -0.00 1.18547 -0.03525 -0.01080 0.00505 0.00398 0.56358 -0.00458 0.00485 0.00101 0.00295 2.1030 -0.2041 -0.1650 0.0329 -0.2203 256.8 -8.3 -4.3 2.2 1.3 -4.39 2.67 4.09 -2.08 0.21 -10.21 1.42 0.51 1.54 1.87 -1.35 0.57 0.38 -0.29 -0.53 -0.50 -0.78 0.03 0.25 0.34 + 82.5 67.5 101230 -27 368 -70 -72 261.6 -10.7 -6.2 2.1 0.3 1.83 -1.44 -0.76 0.41 0.32 -1.1 3.4 2.6 -1.0 2.0 12.16 -0.00 1.18547 -0.03609 -0.01088 0.00503 0.00415 0.56726 -0.00298 0.00804 0.00152 0.00583 1.9777 -0.3149 -0.2799 0.0275 -0.2594 256.3 -8.7 -4.4 2.1 1.3 1.40 3.21 4.49 -2.85 -0.75 -4.79 2.32 1.20 0.53 1.81 -1.03 0.57 0.40 -0.20 -0.60 -0.42 -0.75 0.10 -0.08 0.14 + 82.5 82.5 101270 82 392 -57 -55 261.2 -11.3 -6.3 1.8 0.3 1.79 -1.50 -0.77 0.40 0.34 -0.8 4.0 2.6 -0.4 1.9 8.58 -0.00 1.18427 -0.03677 -0.01081 0.00495 0.00426 0.56972 -0.00031 0.00976 0.00277 0.00603 1.9214 -0.3769 -0.3040 -0.0205 -0.2414 255.9 -9.2 -4.6 1.9 1.3 2.35 3.13 4.48 -3.38 -1.66 -4.00 3.29 1.85 -0.57 0.76 -1.16 1.17 0.59 -0.31 -0.65 -0.32 -0.62 0.31 0.04 -0.31 + 82.5 97.5 101329 185 420 -61 -40 260.7 -11.9 -6.3 1.6 0.1 1.76 -1.55 -0.76 0.40 0.33 -0.3 5.0 2.8 -0.1 1.9 6.42 -0.00 1.18371 -0.03715 -0.01067 0.00488 0.00417 0.57154 0.00402 0.01183 0.00251 0.00641 1.8637 -0.4648 -0.3231 -0.0552 -0.2292 255.6 -9.6 -4.6 1.8 1.2 1.05 2.61 4.05 -3.60 -1.86 -0.05 4.01 2.34 -1.68 -0.89 -0.81 0.97 0.46 -0.16 -0.29 -0.31 -0.23 0.22 0.09 -0.56 + 82.5 112.5 101393 273 445 -81 -37 260.4 -12.4 -6.4 1.6 0.0 1.74 -1.59 -0.76 0.40 0.32 -0.1 5.2 3.3 -0.2 2.0 6.03 -0.00 1.18403 -0.03719 -0.01043 0.00484 0.00397 0.57321 0.00477 0.01246 0.00438 0.00583 1.8270 -0.5105 -0.3435 -0.0747 -0.2249 255.5 -9.8 -4.6 1.7 1.0 1.93 1.92 3.52 -3.20 -1.71 3.58 4.49 2.43 -2.77 -2.00 -0.53 0.75 0.24 -0.25 -0.11 -0.08 -0.30 0.15 0.07 -0.33 + 82.5 127.5 101461 336 460 -116 -48 260.5 -12.4 -6.3 1.5 0.1 1.73 -1.60 -0.75 0.40 0.31 -0.3 5.1 3.1 -0.2 1.8 4.83 -0.00 1.18415 -0.03691 -0.01016 0.00480 0.00380 0.57336 0.00648 0.01094 0.00371 0.00518 1.8286 -0.5075 -0.3288 -0.0616 -0.2261 255.4 -10.0 -4.6 1.7 0.9 -0.75 0.52 2.90 -2.19 -1.45 4.60 4.22 1.98 -3.24 -2.28 -0.52 0.71 0.40 -0.30 -0.18 0.08 0.12 -0.06 -0.37 -0.11 + 82.5 142.5 101531 376 471 -149 -65 260.4 -12.6 -6.2 1.4 0.0 1.73 -1.62 -0.74 0.39 0.29 -0.1 5.4 3.1 0.1 1.7 7.02 0.00 1.18497 -0.03641 -0.00990 0.00477 0.00373 0.57565 0.00906 0.01030 0.00319 0.00481 1.7865 -0.5557 -0.3242 -0.0658 -0.2326 255.5 -10.1 -4.5 1.6 0.8 -3.06 -0.94 2.29 -1.19 -1.37 7.99 3.20 1.62 -2.61 -1.94 -0.63 0.67 0.61 -0.17 -0.30 0.44 -0.27 -0.09 0.03 -0.06 + 82.5 157.5 101599 393 477 -182 -84 260.3 -12.7 -6.2 1.5 -0.0 1.72 -1.63 -0.73 0.40 0.29 0.2 5.5 3.1 0.0 1.7 3.81 0.00 1.18559 -0.03589 -0.00967 0.00487 0.00374 0.57913 0.00989 0.01040 0.00378 0.00567 1.7415 -0.5856 -0.3322 -0.0694 -0.2513 255.5 -10.2 -4.5 1.7 0.8 -5.26 -2.13 1.66 -0.45 -1.59 8.83 1.43 1.30 -1.79 -1.62 -0.59 0.28 0.38 -0.01 -0.07 0.46 -0.66 -0.08 0.21 0.24 + 82.5 172.5 101664 387 480 -208 -107 260.2 -12.7 -6.2 1.6 0.0 1.71 -1.63 -0.73 0.41 0.29 0.5 5.3 3.1 -0.1 1.6 4.40 0.00 1.18586 -0.03539 -0.00946 0.00500 0.00379 0.58002 0.00960 0.01061 0.00446 0.00482 1.7352 -0.5597 -0.3433 -0.0664 -0.2479 255.4 -10.3 -4.5 1.7 0.8 -8.20 -3.01 0.86 -0.02 -1.61 7.67 -0.58 0.75 -0.70 -1.83 -0.88 0.75 0.42 -0.35 -0.39 -0.15 -0.19 0.17 0.20 0.05 + 82.5 -172.5 101719 361 471 -220 -134 260.0 -12.9 -6.2 1.7 0.1 1.69 -1.64 -0.72 0.43 0.30 1.0 5.4 3.1 0.0 1.4 5.02 0.00 1.18596 -0.03504 -0.00924 0.00510 0.00379 0.58076 0.01081 0.01094 0.00368 0.00453 1.7124 -0.5970 -0.3451 -0.0599 -0.2331 255.4 -10.4 -4.5 1.8 0.8 -9.84 -3.30 0.12 -0.11 -1.26 2.59 -2.84 -0.61 0.05 -1.85 -0.97 1.05 0.39 -0.60 -0.61 -0.47 0.27 0.25 -0.14 -0.11 + 82.5 -157.5 101751 314 444 -226 -158 259.6 -13.1 -6.2 1.9 0.0 1.66 -1.65 -0.70 0.45 0.32 1.5 5.8 2.7 -0.1 1.4 5.29 0.00 1.18608 -0.03489 -0.00913 0.00517 0.00377 0.58093 0.01086 0.01180 0.00383 0.00356 1.6769 -0.6479 -0.3504 -0.0326 -0.2207 255.2 -10.6 -4.5 1.9 0.8 -9.23 -2.90 -0.05 -0.46 -0.76 -0.80 -4.45 -2.45 0.34 -1.54 -0.77 0.92 0.21 -0.48 -0.36 -0.59 0.42 0.12 -0.23 -0.03 + 82.5 -142.5 101749 254 409 -227 -176 259.1 -13.4 -6.3 2.0 0.0 1.62 -1.65 -0.69 0.47 0.33 1.9 5.8 2.6 -0.2 1.5 4.69 0.00 1.18552 -0.03486 -0.00924 0.00517 0.00373 0.58129 0.01210 0.01037 0.00231 0.00327 1.6346 -0.7046 -0.3518 -0.0008 -0.2237 255.0 -10.7 -4.5 2.0 0.9 -6.98 -2.00 0.66 -0.65 -0.06 -4.21 -5.06 -3.65 0.23 -1.02 -0.60 0.51 0.31 -0.22 -0.21 -0.54 0.43 -0.15 -0.34 -0.06 + 82.5 -127.5 101709 193 382 -225 -183 258.9 -13.6 -6.4 2.1 0.1 1.61 -1.65 -0.70 0.48 0.34 1.9 5.7 2.4 0.0 1.3 5.35 0.00 1.18500 -0.03495 -0.00954 0.00506 0.00366 0.58182 0.01209 0.00842 0.00203 0.00294 1.6240 -0.7259 -0.3550 0.0225 -0.2151 254.8 -10.7 -4.6 2.1 0.9 -3.84 -0.98 1.65 -0.45 0.43 -6.01 -5.15 -3.55 0.02 -0.01 -0.51 0.39 0.37 -0.03 -0.04 -0.64 0.52 -0.11 -0.29 -0.17 + 82.5 -112.5 101652 136 368 -223 -178 258.4 -14.0 -6.7 2.3 0.1 1.59 -1.67 -0.71 0.49 0.36 2.3 6.1 2.4 -0.1 1.3 6.59 0.00 1.18381 -0.03518 -0.00985 0.00491 0.00362 0.58032 0.01421 0.01010 0.00016 0.00241 1.6063 -0.7934 -0.3832 0.0487 -0.2023 254.5 -10.8 -4.7 2.1 0.9 0.04 0.39 2.36 0.05 0.46 -3.90 -5.06 -2.86 0.02 1.22 -0.23 0.20 0.35 0.06 -0.02 -0.72 0.69 -0.02 -0.13 -0.03 + 82.5 -97.5 101600 99 361 -232 -166 257.8 -14.5 -6.9 2.6 0.3 1.56 -1.69 -0.71 0.54 0.39 4.0 6.3 1.8 -0.4 1.3 11.67 -0.00 1.18359 -0.03575 -0.01016 0.00492 0.00369 0.58158 0.01600 0.01226 -0.00139 0.00362 1.5500 -0.8873 -0.3773 0.0966 -0.1847 254.4 -11.0 -4.9 2.3 1.0 0.91 1.61 2.79 0.56 0.15 0.65 -4.54 -2.30 -0.10 1.60 -0.02 0.13 0.35 0.01 -0.16 -0.16 -0.01 -0.17 0.27 0.42 + 82.5 -82.5 99050 -81 258 -220 -167 261.1 -13.2 -6.2 2.6 1.0 1.34 -1.58 -0.65 0.61 0.49 -4.5 0.6 -0.0 0.4 0.3 17.64 188.83 1.18394 -0.03616 -0.01016 0.00490 0.00393 0.58271 0.02185 0.01184 -0.00318 0.00251 1.2302 -0.9783 -0.3967 0.2244 0.0093 253.8 -11.2 -4.8 2.5 1.3 0.73 2.71 3.23 0.91 0.27 4.72 -2.47 -1.58 0.23 0.95 0.19 0.65 0.21 -0.55 -0.46 -0.11 -0.39 0.05 0.70 0.41 + 82.5 -67.5 92840 -376 108 -149 -128 258.9 -13.0 -6.0 2.8 1.2 1.43 -1.52 -0.65 0.58 0.47 0.6 2.6 1.6 -0.4 0.3 20.20 681.12 1.18400 -0.03592 -0.00982 0.00471 0.00405 0.54469 0.01090 0.00718 -0.00145 0.00040 1.9037 -0.8414 -0.3820 0.1871 0.0180 252.9 -10.5 -4.4 2.5 1.4 0.43 2.96 3.14 0.45 0.40 2.37 -0.58 -0.31 0.71 0.83 0.05 0.03 -0.06 -0.03 0.09 -0.16 0.87 0.15 -0.36 -0.33 + 82.5 -52.5 100915 4 312 -252 -156 259.8 -14.5 -6.4 3.4 0.9 1.38 -1.60 -0.56 0.63 0.39 1.1 2.9 0.4 -1.2 0.7 20.43 48.17 1.18246 -0.03649 -0.01046 0.00498 0.00388 0.59191 0.02646 0.01057 -0.00226 0.00408 1.2764 -0.9954 -0.2442 0.2199 -0.1263 254.5 -11.3 -4.7 2.8 1.3 -5.94 2.05 2.82 -0.15 0.36 11.28 1.15 0.80 0.87 0.86 0.07 0.31 -0.41 -0.23 0.17 0.36 0.07 0.16 0.18 -0.00 + 82.5 -37.5 90881 -488 62 -120 -103 260.1 -12.2 -5.0 3.0 1.4 1.26 -1.31 -0.50 0.51 0.35 -2.4 2.4 1.1 0.1 0.1 29.76 844.30 1.18536 -0.03553 -0.00962 0.00470 0.00411 0.54427 0.01383 0.00513 -0.00305 0.00124 1.6863 -0.8212 -0.2243 0.1605 -0.0381 252.8 -9.9 -4.0 2.7 1.4 -15.72 0.75 1.89 -0.05 0.67 7.58 2.23 1.69 0.57 0.74 -0.62 0.41 -0.17 -0.23 0.08 0.58 0.19 0.12 -0.03 -0.12 + 82.5 -22.5 101250 -34 332 -257 -148 260.7 -13.3 -5.8 3.4 1.0 1.45 -1.62 -0.54 0.66 0.36 -4.1 0.4 0.7 -0.8 0.3 28.54 19.42 1.18433 -0.03576 -0.01033 0.00510 0.00395 0.59041 0.02197 0.01030 -0.00542 0.00256 1.2989 -1.0064 -0.2328 0.2887 -0.1259 255.3 -10.4 -4.4 2.9 1.4 -13.81 -0.51 1.61 0.16 0.82 -1.44 1.30 1.07 0.85 0.62 -0.44 0.12 -0.23 0.05 -0.08 1.17 0.92 0.00 -0.78 -0.10 + 82.5 -7.5 101382 -142 323 -212 -129 260.2 -12.1 -5.3 2.7 0.5 1.72 -1.56 -0.60 0.52 0.32 1.2 3.4 0.9 -0.5 1.5 29.74 -0.00 1.18590 -0.03479 -0.01031 0.00487 0.00385 0.57558 0.00809 0.00547 -0.00352 0.00277 1.7377 -0.6742 -0.1482 0.1247 -0.1428 256.3 -9.3 -4.2 2.6 1.3 -14.41 0.19 2.02 0.18 0.80 -2.36 -0.34 0.01 1.65 0.89 -1.43 0.14 0.34 0.14 -0.49 1.40 0.03 -0.57 0.00 -0.18 + 67.5 7.5 100811 -511 83 -19 -49 279.3 -3.4 -2.5 0.2 0.8 4.84 -1.37 -1.03 0.17 0.48 -7.7 -1.6 -0.3 0.5 0.1 42.63 0.00 1.21032 -0.02507 -0.00986 0.00105 0.00250 0.53956 -0.02647 -0.01276 0.00344 0.00386 3.3427 0.3733 0.1801 -0.0878 -0.0187 268.3 -4.2 -2.5 0.6 0.9 -20.79 -8.25 0.18 1.90 0.05 -2.80 -4.01 -2.81 0.65 -0.15 -3.73 0.83 0.61 0.23 -0.55 0.87 -1.78 -1.22 0.01 0.25 + 67.5 22.5 97486 -333 54 -41 -22 275.0 -10.7 -3.7 1.2 0.5 3.88 -2.39 -1.28 0.47 0.65 -3.8 4.0 0.2 0.5 0.3 27.29 287.37 1.20772 -0.03002 -0.01131 0.00202 0.00206 0.53819 -0.01155 -0.00769 0.00265 0.00272 2.6473 -0.1842 -0.0657 -0.1127 -0.0108 265.6 -7.5 -3.5 1.2 0.7 -17.15 -5.33 1.43 2.17 -0.40 -9.50 -4.40 -2.37 2.16 -0.22 -1.86 0.68 -0.04 0.07 -0.09 -1.53 0.42 0.50 -0.24 -0.49 + 67.5 37.5 98191 -322 75 -1 -9 273.8 -9.8 -4.4 1.1 0.4 3.74 -2.18 -1.38 0.40 0.60 -4.8 2.7 0.7 0.4 0.7 16.14 232.51 1.20439 -0.03219 -0.01211 0.00256 0.00188 0.54167 -0.01660 -0.00575 0.00451 0.00265 2.7153 0.0253 -0.1338 -0.1359 -0.0306 264.6 -7.7 -4.0 1.3 0.8 -22.40 -2.57 1.91 2.27 -0.27 -11.03 -3.43 -0.88 2.13 0.34 -2.91 1.43 0.35 -0.30 0.32 -1.73 0.77 0.10 -0.25 -0.11 + 67.5 52.5 100337 -98 156 15 6 271.9 -11.8 -5.9 0.8 0.2 3.50 -2.46 -1.51 0.41 0.65 -3.1 4.1 1.7 0.8 1.1 2.81 61.95 1.20028 -0.03469 -0.01282 0.00311 0.00182 0.55410 -0.01168 -0.00284 0.00695 0.00515 2.3952 -0.3040 -0.2552 -0.1804 -0.1204 263.7 -8.9 -4.6 1.3 0.8 -23.29 -3.05 1.57 2.51 1.55 -10.78 -0.37 -0.36 1.48 0.81 -4.10 2.72 0.72 -0.63 -0.04 -1.28 -0.06 0.44 0.52 0.22 + 67.5 67.5 99392 68 181 40 21 268.4 -14.2 -6.9 0.9 0.9 3.02 -2.67 -1.40 0.56 0.71 -1.6 6.4 2.3 1.4 0.8 -6.67 142.69 1.19708 -0.03713 -0.01317 0.00378 0.00226 0.55851 0.00037 0.00291 0.00639 0.00157 2.0627 -0.7500 -0.3815 -0.2323 -0.0811 261.4 -10.3 -5.1 1.7 1.2 -21.27 -4.96 0.13 3.06 3.07 -13.92 -0.37 -0.91 2.22 1.72 -3.95 2.81 0.54 -0.56 0.45 -2.37 1.15 1.13 -0.08 -0.92 + 67.5 82.5 100480 411 194 89 30 266.7 -17.7 -7.3 0.9 1.4 2.97 -2.93 -1.36 0.66 0.80 0.2 7.8 2.8 2.0 0.4 -15.39 59.97 1.19362 -0.04021 -0.01355 0.00442 0.00303 0.56181 0.00927 0.00117 0.00972 0.00123 2.0476 -0.9638 -0.3150 -0.2972 -0.0866 260.6 -12.2 -5.3 1.8 1.5 -16.20 -4.60 0.44 3.08 3.25 -3.46 -0.32 -1.30 2.31 1.38 -2.88 2.31 0.93 0.03 -0.49 -0.07 -1.31 -0.39 0.22 0.73 + 67.5 97.5 92001 85 -10 120 67 264.1 -17.9 -6.3 1.5 1.2 2.60 -2.92 -1.13 0.81 0.77 -1.5 7.9 1.6 2.3 0.7 -17.15 763.19 1.19100 -0.04280 -0.01333 0.00532 0.00368 0.53198 0.01364 -0.00164 0.00861 0.00523 2.1032 -1.1293 -0.2044 -0.3511 -0.1533 256.8 -13.4 -5.2 2.3 1.4 -12.22 -1.72 1.50 2.12 1.86 -2.28 -4.75 -0.00 1.06 1.15 -1.67 1.05 0.51 0.32 0.20 0.18 -0.85 -0.12 0.57 0.26 + 67.5 112.5 96927 410 83 81 75 264.7 -20.9 -5.9 0.8 0.9 2.60 -2.99 -1.16 0.80 0.84 -0.5 9.4 1.0 3.7 1.1 -14.17 358.33 1.19027 -0.04424 -0.01292 0.00596 0.00420 0.55653 0.01396 0.00054 0.01384 0.00497 1.8429 -1.1076 -0.2433 -0.4032 -0.0866 257.7 -14.6 -5.2 2.2 1.3 -12.51 1.66 2.21 1.30 0.27 1.46 -2.67 0.62 -0.14 0.62 -1.78 2.10 0.14 -0.68 0.58 -0.67 -0.00 0.58 0.50 -0.36 + 67.5 127.5 88256 -162 -101 71 75 262.9 -18.3 -5.1 2.0 1.2 2.36 -2.79 -0.96 0.89 0.77 -0.8 8.2 0.8 1.7 0.5 -7.23 1084.25 1.19184 -0.04330 -0.01190 0.00641 0.00461 0.51796 0.00830 -0.00217 0.01025 0.00514 2.2926 -1.0869 -0.1408 -0.3353 -0.0581 255.2 -14.3 -4.7 2.4 1.3 -12.00 5.70 2.72 1.10 -0.40 13.47 0.61 2.28 -1.99 -1.25 -1.55 2.13 0.43 -0.62 -0.09 2.38 -3.60 -0.89 1.70 0.96 + 67.5 142.5 97004 438 124 -19 52 265.3 -20.6 -5.9 1.5 0.5 2.52 -3.14 -1.04 0.96 0.78 -1.7 6.3 0.7 1.2 0.2 2.48 358.01 1.19401 -0.04197 -0.01147 0.00582 0.00392 0.56688 0.02506 0.00168 0.01088 0.00676 1.5608 -1.2083 -0.2722 -0.2502 -0.1000 258.0 -15.3 -4.8 2.1 0.9 -12.83 5.96 2.83 0.83 -0.29 7.11 5.56 2.45 -1.85 -0.49 -3.83 5.85 1.53 -1.70 -1.72 -0.46 2.16 -0.13 -1.31 -0.13 + 67.5 157.5 98639 507 207 -17 56 265.2 -21.5 -6.2 0.2 -0.2 2.52 -2.89 -1.09 0.66 0.68 1.9 12.1 1.9 4.9 1.7 6.34 226.33 1.19757 -0.03794 -0.01080 0.00520 0.00392 0.57097 0.01698 0.00453 0.01091 0.00783 1.6082 -1.2193 -0.2922 -0.4250 -0.1653 259.1 -14.0 -4.9 1.8 0.8 -10.52 5.14 2.17 -0.34 0.04 5.81 5.98 2.69 -1.43 0.43 -3.51 4.87 1.05 -1.76 -0.84 0.46 0.48 0.01 0.01 -0.01 + 67.5 172.5 95382 64 176 -40 20 265.7 -16.4 -6.2 1.6 0.6 2.64 -2.77 -1.14 0.72 0.63 -0.8 8.2 2.6 1.9 2.0 6.74 486.17 1.20003 -0.03230 -0.00974 0.00532 0.00486 0.54912 0.01025 0.00816 0.00497 0.00970 2.0645 -0.9526 -0.3695 -0.2593 -0.2280 258.9 -11.8 -4.7 2.0 1.2 -8.19 4.86 1.25 -0.74 -0.30 0.91 1.84 2.94 0.26 1.12 -2.38 2.36 0.82 -0.57 -0.59 -0.51 1.43 0.23 -0.33 0.03 + 67.5 -172.5 101325 42 390 -17 50 267.6 -12.0 -8.1 0.5 -0.6 2.75 -2.11 -1.32 0.35 0.44 -2.6 -0.8 3.0 2.3 1.8 2.05 -0.00 1.20175 -0.02841 -0.00946 0.00508 0.00454 0.56784 -0.00093 0.01231 0.00767 0.01141 2.2146 -0.2531 -0.5207 -0.2315 -0.3237 261.2 -10.1 -5.0 1.5 0.8 -6.57 3.63 0.68 -1.61 -0.97 -1.37 -1.78 2.48 1.67 0.21 -2.72 1.82 0.48 -0.65 -0.46 0.16 1.67 0.38 -0.77 -0.45 + 67.5 -157.5 95027 -324 197 15 76 269.1 -14.6 -4.7 2.1 0.7 2.99 -2.86 -1.08 0.84 0.61 -2.7 4.9 0.9 0.4 0.3 5.63 496.39 1.20481 -0.02651 -0.00779 0.00563 0.00422 0.53864 0.00608 0.00136 0.00425 0.00429 2.2660 -0.9312 -0.2473 -0.0343 -0.0407 261.4 -10.3 -3.6 2.1 0.9 -11.00 0.93 0.05 -1.67 -1.73 11.02 2.36 0.92 0.09 -1.40 -2.38 0.26 -0.04 -0.15 0.13 2.80 -2.53 -0.46 1.12 -0.42 + 67.5 -142.5 95374 -155 228 -11 88 268.8 -17.2 -4.7 2.0 0.2 2.89 -3.00 -1.06 0.82 0.59 -1.8 6.5 0.7 1.7 0.6 7.47 481.73 1.20590 -0.02713 -0.00825 0.00564 0.00342 0.54691 0.01619 0.00259 0.00566 0.00519 1.9303 -1.1646 -0.2621 -0.1311 -0.0585 261.6 -11.3 -3.8 2.3 0.6 -14.88 -2.00 0.47 -1.19 -2.57 1.65 1.60 -1.26 -0.66 -1.35 -3.17 -1.01 0.74 1.05 0.20 0.49 0.02 -0.55 -0.35 0.46 + 67.5 -127.5 97957 9 318 -33 59 267.2 -17.3 -6.8 1.9 0.2 2.67 -2.68 -1.18 0.62 0.60 0.5 8.9 2.6 1.9 1.4 -5.79 284.22 1.20409 -0.02805 -0.00991 0.00509 0.00309 0.56275 0.01667 0.00561 0.00467 0.00699 1.8190 -1.0873 -0.4150 -0.1564 -0.1594 261.1 -11.3 -4.7 2.0 0.5 -20.20 -4.55 -0.87 -0.87 -2.03 -8.52 -0.97 -3.29 -2.03 -1.18 -5.42 2.39 0.75 -0.82 0.30 -1.62 0.53 0.09 -0.47 0.08 + 67.5 -112.5 99347 37 348 -71 -33 266.0 -14.7 -7.3 1.4 1.3 2.36 -2.34 -1.21 0.49 0.69 -4.0 1.0 1.1 0.7 -0.1 -24.21 180.21 1.19895 -0.02999 -0.01138 0.00400 0.00339 0.56989 0.01553 0.00850 0.00483 0.00444 1.7645 -0.8780 -0.4501 -0.0910 -0.1150 259.2 -11.4 -5.4 1.5 1.0 -20.90 -4.02 -1.11 -0.47 -1.12 -19.46 -4.71 -3.08 -4.00 -1.49 -5.35 3.80 1.73 -1.20 -0.74 -2.21 1.29 0.22 -0.78 -0.24 + 67.5 -97.5 101033 104 409 -145 -83 262.4 -17.7 -9.4 0.9 0.4 2.21 -2.33 -1.29 0.50 0.75 0.1 7.6 3.1 2.3 2.1 -34.87 37.36 1.19502 -0.03275 -0.01299 0.00256 0.00308 0.58112 0.01685 0.01411 0.00118 0.00646 1.6360 -1.0662 -0.6359 -0.1070 -0.1875 257.8 -12.0 -6.1 1.2 1.1 -13.30 -1.56 -0.19 1.20 -0.53 -11.18 -4.59 -2.64 -3.71 -1.01 -2.88 1.72 1.10 0.15 -0.25 -0.62 -0.33 -0.06 -0.40 0.09 + 67.5 -82.5 98675 -156 286 -178 -78 263.4 -14.7 -8.3 0.9 0.3 2.15 -2.06 -1.19 0.41 0.54 -0.5 4.4 2.2 1.4 1.9 -26.50 195.61 1.19399 -0.03360 -0.01309 0.00154 0.00244 0.56745 0.00934 0.01216 0.00127 0.00719 1.9100 -0.7079 -0.5631 -0.1232 -0.2620 257.2 -11.5 -6.1 0.9 0.7 -7.01 -0.50 0.84 3.07 1.16 -3.56 -2.11 -0.46 -1.43 -1.37 -1.58 0.53 0.68 0.90 0.07 -0.42 1.33 0.31 -1.01 -0.88 + 67.5 -67.5 91791 -528 42 -152 -90 263.4 -12.2 -7.0 1.3 0.4 2.09 -1.89 -1.05 0.45 0.52 -0.3 3.1 3.3 0.0 0.9 0.69 749.61 1.19484 -0.03261 -0.01164 0.00095 0.00188 0.53795 0.00344 0.00947 -0.00226 0.00494 2.2808 -0.5192 -0.5834 -0.0068 -0.1806 256.1 -10.3 -5.1 1.1 0.6 -4.70 -1.01 1.32 4.34 2.51 20.64 3.00 2.28 0.98 -1.82 -1.25 0.71 0.72 0.32 -0.33 0.75 -0.35 0.14 0.43 -0.05 + 67.5 -52.5 97432 -491 103 -147 -121 270.6 -9.5 -5.1 2.0 0.1 2.63 -1.97 -0.87 0.52 0.35 -4.2 2.0 0.4 -0.2 0.6 28.08 276.78 1.19859 -0.02942 -0.01003 0.00140 0.00131 0.54992 -0.00154 0.00309 -0.00116 0.00186 2.5307 -0.3694 -0.1182 -0.0393 -0.0870 260.8 -8.4 -4.1 1.5 0.4 -12.10 -2.18 1.11 3.03 2.52 19.23 5.67 2.92 1.80 -0.43 -3.69 2.28 0.75 -0.25 0.16 2.91 -0.83 -0.36 0.23 0.05 + 67.5 -37.5 72078 -984 -199 48 -56 259.3 -7.8 -2.9 1.6 0.8 1.35 -0.85 -0.32 0.25 0.20 -3.5 1.2 -0.1 -0.6 -0.1 53.57 2643.97 1.20042 -0.02783 -0.00876 0.00054 0.00183 0.46115 -0.00363 -0.00667 -0.00102 -0.00280 2.7624 -0.2256 0.1363 0.1596 0.0928 250.6 -6.9 -2.7 1.2 0.8 -9.44 -5.74 -0.39 1.67 1.83 13.76 6.46 2.21 0.42 0.88 -0.74 -0.42 0.15 0.33 0.11 0.52 -0.07 -0.18 -0.15 0.07 + 67.5 -22.5 100916 -512 142 -83 -148 274.0 -3.6 -2.9 0.6 0.7 3.67 -1.15 -0.76 0.27 0.37 -4.6 -3.6 -0.2 1.8 0.9 61.84 -0.02 1.20662 -0.02370 -0.00852 0.00124 0.00238 0.55648 -0.01757 -0.00791 -0.00155 0.00149 2.7593 0.2175 0.0923 0.0730 -0.0465 265.0 -4.9 -2.5 1.2 0.9 -11.90 -7.30 -1.18 -0.26 0.23 6.07 4.51 0.10 -0.03 1.90 -6.08 -0.06 0.94 0.24 0.13 1.78 0.18 -0.42 -0.17 0.30 + 67.5 -7.5 100825 -532 124 -10 -81 276.7 -3.0 -2.8 0.2 0.9 4.29 -1.16 -0.93 0.15 0.42 -6.6 -1.7 0.4 0.6 -0.2 54.98 -0.00 1.20931 -0.02317 -0.00901 0.00074 0.00255 0.54410 -0.02167 -0.01041 0.00134 0.00146 3.0721 0.3201 0.1295 -0.0151 -0.0102 266.9 -4.0 -2.4 0.7 0.9 -18.43 -10.37 -0.96 0.70 -0.19 -2.80 -1.50 -2.48 0.26 1.98 -3.59 0.21 1.03 -0.23 -0.12 0.37 -0.04 -0.50 -0.09 0.08 + 52.5 7.5 101113 -10 -13 24 -7 283.7 -7.7 -2.8 -0.2 0.3 6.32 -2.39 -1.42 0.20 0.38 -5.0 1.0 -0.2 0.3 -0.1 42.83 35.28 1.22815 -0.02048 -0.01051 0.00059 0.00136 0.55315 -0.01353 -0.01223 0.00298 0.00147 2.9968 0.0633 0.0775 -0.0744 0.0212 273.4 -5.7 -2.8 0.0 0.3 -28.31 -2.74 1.65 -2.12 0.29 -1.03 -5.77 -1.97 0.24 -1.65 -5.53 2.98 -1.02 -0.11 1.58 0.54 -2.55 0.22 1.03 -0.44 + 52.5 22.5 99832 60 -78 -21 -20 282.1 -10.9 -3.2 -0.0 0.0 5.81 -3.14 -1.45 0.50 0.40 -5.0 1.7 -0.2 -0.3 0.2 29.59 143.43 1.22514 -0.02528 -0.01156 0.00108 0.00078 0.55205 -0.00973 -0.01097 0.00411 0.00241 2.7986 -0.1531 0.0203 -0.1189 -0.0107 271.9 -8.0 -3.3 0.4 0.0 -22.84 -5.27 0.98 -0.76 1.28 -6.33 -3.58 -2.58 0.18 -0.72 -5.09 2.04 0.03 0.02 0.97 0.20 -1.36 0.14 0.41 0.12 + 52.5 37.5 99397 209 -93 -59 -46 280.8 -13.6 -3.9 0.0 -0.3 5.35 -3.45 -1.40 0.60 0.41 -4.8 1.8 0.4 0.0 0.7 12.60 184.72 1.22313 -0.02886 -0.01233 0.00133 0.00095 0.55779 -0.00569 -0.01133 0.00530 0.00297 2.5426 -0.2959 0.0105 -0.1211 -0.0843 270.7 -9.7 -3.6 0.5 0.1 -20.63 -7.08 0.98 0.35 1.54 -3.44 -0.57 -0.02 -0.65 0.14 -2.17 0.19 -0.79 -0.27 0.67 -0.81 0.30 -0.11 -0.25 -0.04 + 52.5 52.5 99970 505 -61 -77 -57 279.8 -16.3 -4.5 -0.4 -0.4 4.72 -3.39 -1.15 0.56 0.37 -3.5 4.0 1.0 0.3 0.9 -4.50 148.66 1.22328 -0.03126 -0.01200 0.00082 0.00069 0.56473 0.00149 -0.00827 0.00206 0.00164 2.4144 -0.3959 0.0036 -0.1907 -0.0966 270.3 -10.9 -3.7 0.5 0.1 -15.39 -6.47 1.24 1.31 1.07 -4.09 2.43 2.15 -1.49 -1.04 -1.40 -0.11 -1.22 0.21 0.40 -0.62 0.02 0.52 -1.00 0.15 + 52.5 67.5 98245 635 11 -104 -70 278.3 -16.4 -4.2 -0.7 -0.7 4.21 -3.28 -0.98 0.58 0.32 -2.0 5.3 1.0 1.3 0.8 -25.30 298.82 1.22093 -0.03208 -0.01087 0.00037 0.00007 0.56067 0.00722 -0.00746 0.00402 0.00353 2.3337 -0.4560 -0.0254 -0.2471 -0.1308 268.9 -11.3 -3.3 0.2 -0.3 -23.77 -8.01 -1.13 2.86 2.26 -5.38 2.76 3.24 -1.29 -1.09 -0.85 -0.32 -0.77 -0.41 0.13 -0.42 -1.08 0.39 1.22 -0.27 + 52.5 82.5 99564 958 114 -106 -52 278.4 -16.6 -4.3 -0.9 -0.9 4.27 -3.63 -1.19 0.78 0.51 -3.2 3.4 0.6 1.8 0.9 -40.58 200.18 1.21647 -0.03395 -0.01072 0.00064 0.00020 0.56530 0.00887 -0.00258 0.00550 0.00080 2.3523 -0.5135 -0.0920 -0.2439 -0.0598 268.3 -12.3 -3.7 0.1 -0.2 -27.02 -9.88 -1.93 3.37 2.01 -1.58 -0.99 -1.00 2.17 1.69 -0.71 -0.33 -1.36 0.78 0.62 0.69 -1.72 -0.73 0.24 0.68 + 52.5 97.5 84614 242 -132 -72 -35 271.5 -15.6 -4.0 -0.0 0.3 3.30 -3.19 -1.06 0.77 0.61 -5.3 2.6 -0.1 1.6 0.2 -37.65 1516.58 1.21027 -0.03791 -0.01193 0.00296 0.00226 0.50403 0.00387 -0.00378 0.00924 0.00146 2.6159 -0.4412 -0.0503 -0.2585 0.0256 261.0 -13.2 -4.1 0.7 0.5 -24.79 -9.17 -1.68 4.33 1.70 -4.78 -6.60 -2.44 2.46 2.95 -1.38 1.14 -0.39 0.11 0.33 1.97 -3.65 -0.62 1.57 0.76 + 52.5 112.5 89324 365 -122 -35 43 272.5 -18.3 -4.4 -0.2 0.5 3.45 -3.52 -1.34 0.94 0.86 -4.5 3.7 -0.0 2.5 0.4 -23.29 1044.99 1.20886 -0.04173 -0.01304 0.00374 0.00337 0.52259 -0.00669 -0.00356 0.00956 0.00322 2.5010 -0.3025 -0.1153 -0.2500 -0.0399 261.9 -14.9 -4.6 0.8 0.9 -21.88 -7.40 0.30 4.53 1.12 -5.60 -8.06 -2.35 1.24 2.06 -2.28 1.41 0.21 0.07 -0.06 0.34 -1.47 -0.34 0.46 0.32 + 52.5 127.5 97719 667 -69 24 64 274.0 -21.0 -4.0 -1.5 0.5 3.93 -4.42 -1.55 1.12 1.10 -3.0 4.5 -0.2 1.4 1.0 7.96 293.46 1.20821 -0.04373 -0.01333 0.00305 0.00315 0.55925 -0.00020 -0.00586 0.01446 0.00313 2.1988 -0.5900 -0.0493 -0.3360 0.0276 263.7 -16.9 -4.6 0.2 0.8 -20.07 -4.64 4.04 4.84 0.93 -1.60 -7.50 -1.62 -0.45 -0.93 -1.47 0.54 0.41 0.35 -0.43 0.29 -0.84 0.11 0.15 0.12 + 52.5 142.5 99640 158 35 -4 10 273.4 -15.4 -6.6 -1.4 0.3 3.88 -3.43 -1.80 0.41 0.92 -4.3 0.3 0.7 1.9 0.1 18.53 116.21 1.21030 -0.03936 -0.01488 0.00244 0.00274 0.55829 -0.02215 -0.00291 0.01049 0.00609 2.4942 0.0569 -0.1532 -0.3128 -0.0381 264.1 -14.0 -5.8 0.0 0.4 -19.37 -3.11 6.24 5.49 1.74 -4.29 -1.20 -1.24 -1.28 -1.21 -4.56 4.68 1.53 -0.81 -1.19 -2.49 4.79 0.35 -1.88 -0.45 + 52.5 157.5 95112 -576 -198 -97 -93 273.7 -9.1 -5.2 0.5 1.0 3.78 -2.28 -1.53 0.28 0.67 -5.1 -1.6 -0.3 1.4 0.1 24.73 469.56 1.21700 -0.03148 -0.01377 0.00276 0.00351 0.53782 -0.03756 -0.00481 0.00302 0.00307 3.0705 0.6096 -0.0873 -0.1696 -0.0408 264.2 -9.5 -5.1 0.9 1.0 -13.42 -1.48 8.30 7.64 3.45 -3.90 5.61 1.78 -1.01 -0.12 0.10 -1.56 0.14 1.58 0.49 1.06 0.16 0.48 -0.13 0.15 + 52.5 172.5 100648 -677 -142 -140 -131 277.0 -3.5 -3.6 -0.5 0.8 4.31 -1.47 -1.28 -0.04 0.53 -7.5 -2.1 -0.6 0.7 0.8 1.96 6.97 1.21808 -0.02375 -0.01147 0.00213 0.00419 0.55305 -0.04308 -0.00847 0.00318 0.00369 3.0003 0.7569 0.0679 -0.1898 -0.0687 267.2 -5.8 -3.9 0.5 1.2 -11.01 0.47 9.00 8.76 3.87 4.51 5.80 3.40 -0.55 1.27 -3.59 1.78 1.01 -0.19 -1.23 0.28 1.42 0.67 -0.19 0.32 + 52.5 -172.5 100726 -782 -85 -169 -66 277.6 -3.1 -3.1 -0.1 0.8 4.54 -1.34 -1.11 0.12 0.51 -7.4 -1.8 -0.4 0.8 0.7 7.31 0.00 1.22172 -0.01919 -0.00954 0.00279 0.00496 0.55631 -0.03802 -0.00741 0.00207 0.00653 2.9653 0.6523 0.0749 -0.1401 -0.1441 267.7 -4.6 -3.3 0.8 1.3 -16.82 0.39 7.47 8.06 1.21 4.81 3.38 3.48 0.85 3.14 -4.69 2.07 1.92 -0.02 -1.80 1.06 0.77 0.02 0.02 -0.13 + 52.5 -157.5 100773 -761 13 -94 86 278.8 -3.1 -3.4 -0.0 1.1 4.96 -1.39 -1.24 0.15 0.61 -7.2 -1.4 -0.0 0.8 0.4 11.07 0.00 1.22354 -0.01621 -0.00915 0.00277 0.00527 0.55850 -0.03139 -0.00811 0.00412 0.00757 2.9722 0.4765 0.0820 -0.1152 -0.1058 268.7 -4.1 -3.2 0.6 1.4 -24.44 -1.94 6.82 6.69 -0.47 5.95 5.68 0.13 1.60 1.93 -4.42 1.51 1.61 1.17 -1.38 1.70 1.04 -1.23 -0.13 0.77 + 52.5 -142.5 101014 -663 -15 -7 171 280.3 -2.4 -3.3 -0.1 1.0 5.46 -1.22 -1.27 0.11 0.57 -7.2 -1.0 -0.0 0.5 0.3 -1.16 0.00 1.22511 -0.01326 -0.00947 0.00261 0.00507 0.56037 -0.02217 -0.01137 0.00345 0.00770 2.9725 0.3417 0.1301 -0.1222 -0.0938 270.0 -3.3 -3.0 0.5 1.3 -22.05 -4.18 6.71 5.41 -0.10 5.93 5.22 -0.49 0.59 -0.73 -4.51 0.79 1.68 0.83 -0.89 1.41 0.62 0.21 0.37 -1.06 + 52.5 -127.5 93713 -352 -69 6 103 278.8 -6.1 -2.8 0.5 1.2 4.70 -1.85 -1.16 0.21 0.54 -5.8 0.5 -0.3 0.3 0.2 -12.45 658.38 1.22379 -0.01538 -0.00997 0.00306 0.00470 0.53770 -0.00311 -0.00892 -0.00015 0.00328 2.7708 -0.1771 0.0239 -0.0948 0.0145 268.1 -5.3 -3.1 0.8 1.4 -20.64 -6.09 4.84 3.06 -0.49 9.05 1.79 0.55 0.53 -2.52 -0.49 -4.45 -0.13 1.60 1.37 4.68 -5.69 -0.57 1.19 0.07 + 52.5 -112.5 92579 -64 -25 -6 86 279.1 -11.7 -4.1 0.2 0.5 4.27 -2.90 -1.18 0.65 0.66 -2.4 4.3 -0.2 1.4 -0.0 -20.16 753.56 1.22311 -0.02176 -0.01081 0.00285 0.00329 0.54020 0.00052 -0.00776 0.00249 0.00030 2.3906 -0.3461 -0.0851 -0.0669 0.0319 268.3 -8.7 -3.7 0.8 1.0 -28.36 -6.81 3.34 0.43 -1.75 -8.77 -8.13 -1.21 -1.40 -1.76 -4.53 1.74 0.66 -0.05 -0.33 -2.29 -0.02 0.35 -0.13 -0.64 + 52.5 -97.5 98802 206 173 -50 64 276.1 -16.3 -6.0 -0.9 -0.5 4.43 -3.80 -1.84 0.53 0.84 -3.5 1.9 1.0 0.8 0.3 -32.73 222.14 1.21941 -0.02868 -0.01282 0.00124 0.00154 0.56928 0.00907 0.00126 0.00638 0.00479 2.2461 -0.6878 -0.3379 -0.2108 -0.0865 267.2 -12.1 -5.1 0.2 0.2 -30.60 -4.52 2.62 0.15 -2.54 -12.96 -7.66 -1.45 -3.14 -2.66 -6.21 3.52 0.42 -0.43 1.05 -0.22 -2.30 -0.58 0.29 -0.07 + 52.5 -82.5 101196 176 253 -124 25 274.2 -15.3 -7.1 -0.9 -1.0 3.77 -3.05 -1.90 0.31 0.68 -4.4 -0.4 0.6 0.6 0.4 -43.62 20.24 1.21600 -0.03101 -0.01414 0.00005 0.00006 0.58708 0.00554 0.00888 0.00321 0.00643 2.0112 -0.4160 -0.3877 -0.1284 -0.1402 265.4 -12.4 -6.0 -0.1 -0.4 -31.85 -3.72 3.25 -0.21 -1.61 -6.96 -1.79 -2.07 -2.11 -2.94 -6.06 2.58 0.52 0.19 0.89 -2.04 2.58 0.13 -0.70 -0.20 + 52.5 -67.5 93782 -314 -75 -166 -94 271.9 -14.6 -6.7 -0.2 -0.9 3.63 -2.91 -1.60 0.38 0.53 -5.0 2.8 1.2 1.1 0.6 -21.65 618.60 1.21532 -0.02903 -0.01405 -0.00019 -0.00033 0.55238 0.00502 0.00247 -0.00029 0.00326 2.2718 -0.5043 -0.2521 -0.1484 -0.1093 263.0 -11.3 -5.6 0.2 -0.4 -28.10 -2.37 5.65 -0.68 -0.61 5.66 1.22 -1.61 0.19 -1.12 -3.67 1.35 1.03 -0.02 0.04 0.49 0.21 -0.06 0.41 -0.24 + 52.5 -52.5 100978 -417 -88 -171 -202 275.6 -5.3 -4.8 -0.3 0.5 4.14 -1.68 -1.42 0.07 0.52 -2.8 -6.8 -1.1 1.9 0.3 17.12 0.00 1.22098 -0.02349 -0.01335 0.00029 0.00080 0.57494 -0.01545 -0.00863 -0.00226 -0.00281 2.3411 0.3391 0.0266 -0.0887 0.0860 267.5 -7.5 -4.6 0.4 0.4 -27.98 -2.70 7.68 -0.09 -1.19 15.13 5.25 0.08 2.18 1.40 -5.38 2.23 1.65 -0.28 0.34 1.23 2.72 0.32 -0.58 0.30 + 52.5 -37.5 100982 -490 -174 -59 -139 281.0 -2.7 -2.7 -0.2 0.4 5.63 -1.31 -1.14 0.06 0.35 -7.4 -0.9 -0.3 0.4 0.3 52.75 0.00 1.22792 -0.01750 -0.01118 0.00063 0.00212 0.56310 -0.02090 -0.01268 0.00344 0.00240 2.9291 0.2414 0.1176 -0.1067 -0.0463 270.8 -3.9 -3.0 0.1 0.6 -30.98 -4.05 6.40 -0.69 -2.27 16.88 10.42 4.11 2.22 3.32 -6.69 2.21 0.65 -1.79 -1.55 0.82 1.39 1.04 -0.03 -0.25 + 52.5 -22.5 101206 -244 -48 22 -8 283.8 -2.0 -2.1 0.0 0.5 6.53 -1.14 -1.03 0.08 0.32 -8.2 -0.3 -0.2 0.0 0.2 61.33 0.00 1.23131 -0.01437 -0.00973 0.00073 0.00248 0.55963 -0.01852 -0.01260 0.00247 0.00305 3.1140 0.2588 0.1195 -0.0744 -0.0514 273.0 -2.5 -2.2 0.1 0.6 -34.42 -4.25 4.28 -1.84 -2.03 6.15 4.80 2.91 0.57 1.74 -4.93 0.58 1.15 -0.98 -0.75 0.77 0.72 -0.86 -1.26 0.01 + 52.5 -7.5 99974 -97 6 37 44 283.4 -4.3 -2.4 0.1 0.4 6.51 -1.57 -1.22 0.09 0.31 -6.7 0.8 -0.1 0.2 -0.1 56.87 120.83 1.23044 -0.01603 -0.00988 0.00047 0.00225 0.54988 -0.01607 -0.01374 0.00101 0.00321 3.2371 0.2402 0.1315 -0.0566 0.0171 273.1 -3.5 -2.4 0.1 0.4 -25.65 -3.23 2.69 -2.30 -0.98 -1.16 -3.37 -0.95 -0.34 -1.13 -0.35 -1.44 -0.35 0.35 -0.11 -0.30 -1.88 -0.25 -0.14 0.13 + 37.5 7.5 101620 177 17 66 44 292.5 -5.6 -3.9 -0.0 0.5 9.28 -2.90 -2.02 -0.08 0.50 -4.8 -3.3 0.1 0.7 -0.1 42.97 0.00 1.24919 -0.01976 -0.01064 0.00128 0.00225 0.55904 -0.01966 -0.01092 -0.00256 -0.00252 3.5358 -0.2021 0.0601 0.0992 0.1434 281.4 -6.0 -3.5 0.4 0.8 -22.05 -3.83 -2.43 1.00 -0.58 -4.41 -2.37 -1.53 -1.48 -2.58 -13.24 9.98 3.85 1.97 -3.43 -1.89 2.00 1.03 -0.90 -0.54 + 37.5 22.5 95757 146 -84 -14 -82 289.3 -7.8 -4.0 0.5 0.5 7.68 -2.00 -1.79 0.01 0.16 -6.0 -0.2 0.2 -0.2 -0.2 31.66 495.02 1.24806 -0.02115 -0.01172 0.00134 0.00198 0.53028 -0.00579 -0.00319 -0.00624 -0.00765 3.6210 0.1142 0.0123 0.0046 0.0753 278.3 -7.0 -3.9 0.7 0.7 -10.19 -4.98 -2.85 0.79 -0.87 8.08 2.07 -0.78 -0.25 -0.65 6.23 -8.42 -0.88 2.87 2.08 -2.03 0.70 1.93 -0.29 -0.41 + 37.5 37.5 88824 335 -69 -39 -145 287.6 -10.3 -4.9 -0.6 0.5 5.59 -1.99 -0.95 -0.14 0.30 -6.4 0.4 -0.1 0.5 0.4 31.30 1114.27 1.24740 -0.02585 -0.01380 0.00142 0.00368 0.49976 0.00969 0.00098 -0.00296 -0.00783 3.3330 -0.1241 -0.0507 -0.0235 0.1685 276.4 -9.5 -4.9 0.2 1.2 -14.73 -3.25 -2.46 0.84 -0.96 -0.88 -0.15 0.45 -1.65 0.01 9.71 -11.37 -4.22 2.35 3.45 -0.17 0.59 0.69 -1.02 -0.28 + 37.5 52.5 101535 648 87 -42 -152 291.4 -6.9 -4.7 -0.5 0.4 8.95 -3.76 -2.88 0.34 0.65 -4.4 -1.4 1.3 -0.1 -0.3 -10.95 -0.00 1.24849 -0.02568 -0.01169 0.00026 0.00237 0.57207 -0.02240 0.00323 -0.00144 0.00171 3.3654 0.1658 -0.3835 0.1726 0.0648 280.8 -7.5 -4.6 -0.3 0.4 -32.44 -4.91 -3.39 0.78 0.07 -3.32 -0.08 2.65 -0.93 -1.30 -15.69 9.43 2.56 -1.52 0.08 -10.21 3.65 7.04 2.27 -1.90 + 37.5 67.5 94606 790 43 -97 -166 292.7 -12.3 -3.6 -0.9 0.2 6.25 -2.57 0.29 0.07 -0.17 -6.0 2.5 0.1 1.1 -0.0 -40.60 594.09 1.24690 -0.02756 -0.00966 0.00009 0.00195 0.53822 -0.00003 0.00012 0.00190 -0.00318 2.7011 0.2488 0.1934 -0.0404 -0.0308 279.8 -10.1 -3.1 -0.6 0.3 -21.98 -3.23 -1.12 -0.89 -1.33 -2.38 3.47 2.16 -0.06 0.23 13.29 -24.20 -7.62 6.05 3.47 10.61 -12.35 -2.86 4.68 2.73 + 37.5 82.5 87775 665 -89 -57 -143 289.6 -13.9 -2.7 -2.3 0.1 2.63 -1.53 -0.50 0.28 0.28 -5.0 2.3 -0.1 0.4 0.1 -53.38 1221.77 1.24144 -0.02860 -0.00882 -0.00018 0.00259 0.57927 -0.02498 -0.00457 0.00473 0.00818 1.5222 0.2078 0.0075 -0.0469 -0.1070 272.9 -8.7 -2.5 -0.8 0.0 -29.50 -4.95 -0.74 -1.27 -0.47 -1.46 -1.93 -0.98 0.21 0.95 -10.95 11.12 6.40 -0.68 -8.36 -1.53 -0.47 -0.83 0.63 1.12 + 37.5 97.5 60519 -401 -314 -107 -66 269.5 -10.6 -3.2 -0.5 1.0 2.27 -1.98 -0.74 0.42 0.63 -7.4 0.7 -0.2 0.4 -0.0 -48.57 4252.68 1.23218 -0.02866 -0.01081 0.00141 0.00457 0.42692 -0.01452 -0.00757 0.00234 0.00081 2.9269 0.2251 0.0874 -0.0031 -0.0193 257.2 -9.5 -3.3 -0.1 1.3 -23.48 -8.21 -1.62 0.57 0.24 -0.55 -1.79 -1.01 -0.22 0.71 -0.93 0.09 1.02 0.29 -0.49 0.99 -1.62 -0.38 0.25 0.31 + 37.5 112.5 88423 544 -161 -113 -44 283.3 -13.0 -2.8 -1.0 -0.1 5.46 -4.58 -2.17 0.60 1.38 -5.1 0.8 -0.1 -0.1 -0.2 -17.76 1171.24 1.24095 -0.03037 -0.01081 0.00009 0.00295 0.53708 -0.02220 -0.00401 0.00601 0.00965 2.7688 0.1102 -0.1470 -0.0698 -0.1236 271.3 -10.7 -3.5 -0.6 0.2 -28.49 -12.72 -4.06 1.15 1.43 1.16 -4.52 -2.80 -0.31 1.51 -8.88 4.55 3.18 0.68 -2.63 -1.70 -0.21 1.78 1.44 -1.11 + 37.5 127.5 98175 690 -31 -82 -34 284.2 -12.5 -4.8 -1.3 0.1 6.71 -5.31 -2.72 0.66 1.45 -5.7 0.4 -0.0 -0.0 -0.3 24.85 288.58 1.24269 -0.03077 -0.01397 -0.00074 0.00283 0.56782 -0.03262 -0.00785 0.00589 0.01848 3.0422 0.4054 -0.0339 -0.1322 -0.1985 273.6 -10.9 -4.9 -0.8 0.0 -37.14 -16.71 -5.94 0.12 1.27 1.98 -4.70 -4.07 -0.54 -0.34 -8.95 3.21 4.76 1.94 -4.50 3.35 -2.71 -2.22 -1.22 1.27 + 37.5 142.5 101393 319 -16 -156 -77 287.4 -6.7 -5.6 -0.6 0.2 8.21 -4.61 -3.18 0.22 1.08 -7.0 -3.4 0.0 0.5 -0.1 27.42 0.00 1.24507 -0.02688 -0.01640 -0.00113 0.00131 0.58231 -0.05109 -0.01926 -0.00512 0.00688 2.9204 0.4536 0.1511 0.0091 -0.0820 276.0 -7.9 -5.5 -0.2 0.1 -31.18 -18.44 -7.63 -1.31 2.00 -16.40 -1.21 -3.11 -1.94 -2.78 -16.03 1.63 2.44 3.39 5.41 -1.26 6.60 1.74 -2.39 -4.19 + 37.5 157.5 101425 -120 -147 -223 -169 288.1 -5.0 -5.7 -0.9 0.4 8.79 -3.45 -3.24 -0.14 0.96 -8.5 -1.6 -0.2 0.4 0.0 1.14 0.00 1.24764 -0.02154 -0.01605 -0.00216 -0.00011 0.58788 -0.04321 -0.01676 -0.00732 -0.00575 2.8980 0.3737 0.1011 0.0477 0.0772 276.6 -5.9 -5.6 -0.6 0.3 -42.07 -19.10 -10.02 -2.75 2.46 0.93 1.01 0.64 -1.46 -1.32 -14.38 -0.26 0.27 -2.04 5.02 -0.06 2.47 2.26 0.35 -1.44 + 37.5 172.5 101572 -396 -181 -269 -172 288.5 -3.8 -4.8 -0.6 0.7 8.94 -2.85 -2.79 -0.05 0.86 -8.1 -1.6 -0.0 0.6 0.1 -12.72 0.00 1.24785 -0.01748 -0.01335 -0.00180 0.00022 0.58905 -0.03690 -0.01024 -0.00443 -0.00498 2.9057 0.2913 0.0303 0.0108 0.0583 277.1 -4.7 -4.8 -0.5 0.5 -39.53 -18.20 -9.41 -3.15 1.39 2.90 0.56 3.68 0.86 2.03 -11.63 -0.55 0.34 -0.91 2.08 0.54 0.66 1.29 0.34 0.44 + 37.5 -172.5 101756 -534 -104 -257 -80 288.4 -3.4 -4.3 -0.4 1.2 8.87 -2.53 -2.46 0.04 1.05 -7.8 -1.2 0.2 0.5 0.1 -12.29 0.00 1.24824 -0.01516 -0.01089 -0.00068 0.00154 0.58792 -0.02829 -0.00701 -0.00254 -0.00417 2.9418 0.2148 -0.0189 0.0075 0.0855 277.3 -4.2 -4.0 -0.3 1.0 -33.94 -16.13 -7.74 -2.85 3.46 2.40 1.27 2.72 1.93 2.35 -7.43 -2.53 -1.78 -0.47 2.99 -1.19 2.29 0.61 0.75 -0.64 + 37.5 -157.5 101958 -529 -47 -174 5 288.3 -2.9 -4.2 -0.4 1.0 8.85 -2.03 -2.24 -0.00 0.75 -7.7 -0.8 0.4 0.5 -0.1 -15.18 0.00 1.24843 -0.01284 -0.01005 -0.00004 0.00136 0.58745 -0.02192 -0.00515 -0.00078 -0.00823 2.9442 0.1984 -0.0131 -0.0183 0.1503 277.5 -3.5 -3.7 -0.1 1.0 -29.76 -13.40 -6.24 -2.12 6.09 2.36 4.03 1.02 1.37 -0.59 -4.28 -4.22 -1.91 -0.25 4.72 -0.89 2.18 0.51 0.71 -2.21 + 37.5 -142.5 102172 -371 -24 -99 -17 288.5 -1.8 -3.6 -0.2 0.6 8.64 -1.33 -1.79 0.09 0.44 -8.2 -0.5 0.3 0.4 -0.1 -30.28 0.00 1.24952 -0.01098 -0.00985 0.00061 0.00104 0.58101 -0.01436 0.00124 0.00334 -0.00833 3.1148 0.1451 -0.0558 -0.0471 0.1291 278.0 -2.6 -3.5 -0.1 0.7 -25.68 -10.23 -6.40 -1.33 5.61 -1.37 4.97 0.59 2.22 -0.31 -1.39 -4.01 -3.08 0.39 2.75 -3.11 2.88 0.84 0.53 0.13 + 37.5 -127.5 101969 35 93 4 -67 286.5 -1.0 -2.7 -0.1 0.3 7.87 -0.67 -1.41 0.02 0.32 -6.9 0.4 0.4 0.0 -0.0 -38.40 0.00 1.24941 -0.01291 -0.01071 0.00167 0.00196 0.56649 -0.00362 0.00526 0.00397 -0.01257 3.7470 0.0314 -0.2267 -0.1514 0.2123 278.6 -3.0 -3.6 0.3 1.1 -18.01 -6.23 -5.06 -0.30 3.42 -4.34 2.37 0.58 2.61 1.47 1.44 -0.83 -1.55 -0.13 1.67 -6.32 4.04 1.61 -0.23 -0.44 + 37.5 -112.5 78277 -145 -238 71 80 283.2 -11.0 -4.3 0.4 1.1 3.62 -1.14 -0.93 -0.03 0.75 -6.6 2.7 -0.1 1.1 0.0 -20.68 2190.97 1.24541 -0.02040 -0.01189 0.00249 0.00299 0.49198 -0.01467 -0.00779 0.01723 -0.00169 2.8510 0.5458 0.2250 -0.3009 0.0687 270.4 -7.3 -3.9 0.0 1.1 -15.25 -4.42 -2.61 1.35 2.45 2.34 -4.90 -0.85 -0.63 -0.20 1.94 -0.58 1.68 -0.65 -2.58 5.09 -6.09 -3.55 0.46 3.38 + 37.5 -97.5 96791 222 -86 59 87 288.6 -12.1 -4.0 -0.7 0.2 7.37 -5.00 -1.69 0.52 0.55 -4.6 2.6 0.0 1.2 0.0 -28.70 410.88 1.24839 -0.02195 -0.01073 0.00058 0.00131 0.56192 -0.00188 -0.00818 0.00940 0.00881 2.5358 -0.2562 -0.0097 -0.1095 -0.1489 278.4 -9.4 -3.5 -0.3 0.0 -24.08 -10.77 -4.06 0.41 2.25 -2.57 -3.31 -0.01 -1.90 -1.61 -3.35 -0.75 0.68 1.48 0.06 -0.24 2.92 2.48 -0.30 -2.58 + 37.5 -82.5 97220 77 -93 20 18 287.5 -10.5 -3.7 -1.0 -0.3 7.69 -4.40 -1.88 0.42 0.64 -5.6 0.1 -0.4 0.1 -0.1 -32.48 388.87 1.24872 -0.02081 -0.01138 -0.00023 0.00057 0.56337 -0.00371 -0.00324 0.01072 0.00352 2.6680 -0.1291 -0.1031 -0.1048 -0.0384 276.9 -8.6 -3.9 -0.7 -0.2 -32.26 -15.20 -6.09 -2.42 0.47 -0.91 0.92 -1.59 -0.40 -0.81 -10.56 7.27 3.20 -1.92 -3.80 1.29 -1.37 -1.21 0.04 0.08 + 37.5 -67.5 101660 -45 -79 -5 -15 291.6 -5.7 -4.3 0.0 0.1 9.96 -3.65 -2.42 0.53 0.57 -9.4 -0.7 0.0 0.1 -0.2 -39.75 0.00 1.24918 -0.01708 -0.01224 0.00044 0.00056 0.57581 -0.02159 -0.01094 0.00617 0.00521 3.0858 0.0777 0.0801 -0.0245 -0.0412 279.5 -5.7 -4.1 -0.1 -0.2 -30.37 -15.64 -8.48 -4.42 -1.76 4.87 0.52 -1.82 1.29 0.15 -11.80 0.59 2.33 -0.85 -5.00 2.30 1.77 -1.51 0.19 0.77 + 37.5 -52.5 101805 -269 -140 74 -16 292.2 -3.4 -3.9 0.1 0.7 10.52 -2.73 -2.33 0.44 0.68 -9.0 -0.7 0.1 0.4 -0.1 -9.77 0.00 1.25175 -0.01375 -0.01166 0.00071 0.00124 0.57579 -0.02311 -0.01095 0.00599 0.00237 3.1000 0.0883 0.0646 -0.0489 -0.0265 280.4 -3.7 -3.8 0.1 0.4 -26.02 -13.46 -10.37 -3.37 -2.75 12.03 2.16 2.09 1.34 1.19 -4.29 -1.00 -1.17 1.20 1.97 0.10 2.29 0.89 -0.86 -1.27 + 37.5 -37.5 102034 -276 -50 131 21 292.0 -2.4 -3.3 0.1 0.8 10.31 -1.85 -2.02 0.19 0.58 -8.7 -0.3 0.2 0.2 -0.1 38.00 0.00 1.25286 -0.01147 -0.01035 0.00096 0.00189 0.57020 -0.01635 -0.00908 0.00248 -0.00030 3.2050 0.0944 0.0789 -0.0152 0.0209 280.7 -2.7 -3.2 0.2 0.7 -20.59 -7.77 -7.57 -0.69 -1.29 9.16 2.82 2.37 0.61 1.02 -1.81 -2.69 -0.43 1.18 1.91 -1.65 2.42 1.16 -0.99 -1.15 + 37.5 -22.5 102106 -116 12 152 49 290.9 -1.9 -2.8 -0.0 0.7 9.57 -1.37 -1.58 0.09 0.50 -8.8 -0.1 0.2 0.2 -0.0 50.71 0.00 1.25102 -0.01150 -0.00950 0.00127 0.00214 0.56034 -0.01345 -0.00779 0.00199 -0.00280 3.3600 0.1172 0.1256 -0.0303 0.0581 280.3 -2.4 -2.8 0.3 0.8 -10.62 -2.99 -3.49 0.47 0.63 -5.71 1.47 1.35 0.98 0.50 0.86 -4.40 0.69 1.76 1.43 -4.11 1.60 0.76 0.13 -0.66 + 37.5 -7.5 99636 216 34 117 36 290.7 -5.7 -3.0 -0.1 0.9 8.22 -1.48 -1.33 -0.22 0.29 -5.0 -1.1 -0.6 0.9 0.6 54.00 178.43 1.25172 -0.01694 -0.00980 0.00175 0.00322 0.54492 -0.00637 -0.01048 -0.00130 -0.00208 3.7640 0.0160 0.0991 0.1017 0.1601 280.6 -5.5 -2.9 0.5 1.2 -9.14 -1.32 -2.43 0.37 -0.01 -2.98 -4.45 -1.01 -1.24 0.61 5.65 -6.95 0.17 1.74 1.12 5.41 -5.28 -1.14 0.18 -0.11 + 22.5 7.5 89835 197 -64 39 20 299.3 -8.2 -1.8 -1.5 -0.5 3.46 -0.82 -0.97 -0.09 0.13 -7.1 0.9 0.2 0.3 -0.0 32.55 1043.35 1.27083 -0.01175 -0.00355 -0.00044 0.00044 0.58803 -0.00655 0.01416 -0.00122 -0.00654 1.8023 0.4700 -0.0340 0.1802 0.1110 281.2 -4.6 -2.0 -0.5 0.1 -5.45 -10.11 -6.90 0.48 1.89 -0.98 1.00 0.20 0.10 -0.50 1.39 -1.54 -3.18 -3.29 -2.24 -1.61 1.19 1.78 -0.07 -0.57 + 22.5 22.5 95718 391 -1 55 -20 298.9 -8.2 -1.6 -1.4 -0.3 4.20 -0.74 -1.33 -0.04 0.14 -0.7 1.2 0.3 -0.4 -0.3 15.27 495.56 1.27256 -0.01216 -0.00410 -0.00010 0.00083 0.57633 0.00352 0.03119 -0.00661 -0.00607 2.2514 0.4778 -0.2424 0.1980 0.0498 284.7 -5.5 -2.8 -0.4 0.2 -8.23 -9.66 -6.74 0.18 0.59 -3.90 1.13 0.13 0.14 0.11 -4.85 5.75 3.16 -2.23 -6.43 -2.93 1.29 0.40 0.42 1.58 + 22.5 37.5 100866 574 147 15 -53 300.9 -4.0 -2.9 0.1 -0.0 14.62 -2.80 -2.55 -1.07 -0.04 -0.9 -7.8 0.2 -0.4 -0.8 6.47 0.00 1.27511 -0.01281 -0.00530 0.00028 0.00140 0.56905 -0.04993 -0.01467 0.00212 0.00921 5.4703 -0.7690 0.3538 -0.4423 -0.2145 290.3 -2.4 -1.8 -0.6 -0.6 -8.79 -8.99 -6.80 -0.41 0.77 -1.73 0.70 0.69 -0.56 -0.86 -20.88 9.38 2.49 -5.32 -3.00 7.63 5.22 6.04 1.33 -1.41 + 22.5 52.5 99886 985 187 -61 -136 303.7 -9.7 -2.5 -1.3 -0.5 7.48 -0.50 -1.69 0.04 0.32 -2.7 0.6 0.1 0.5 0.3 -33.13 82.37 1.27693 -0.01573 -0.00472 0.00015 0.00104 0.58174 -0.03835 0.01948 0.00527 0.01716 2.8707 0.5211 -0.3299 0.0588 -0.1427 289.6 -4.7 -2.7 -0.9 -0.8 -6.39 -10.51 -6.15 -0.79 0.21 -2.08 1.15 0.56 -0.88 -0.24 -5.35 10.78 3.66 -0.87 -5.54 1.54 -7.90 -8.03 3.00 4.55 + 22.5 67.5 100875 756 103 -67 -33 299.1 -2.7 -1.1 -0.2 -1.1 15.97 -4.65 -0.68 -1.27 -0.76 -5.7 1.9 1.4 -2.8 -0.4 -50.06 0.00 1.27561 -0.01025 -0.00306 -0.00058 -0.00114 0.56123 -0.04885 -0.01917 0.01792 0.02637 5.8978 1.5981 1.6607 -1.5429 -0.8005 289.7 -2.0 0.0 -1.3 -2.1 -2.22 -8.26 -6.18 -0.25 -0.27 -2.78 0.13 -0.14 0.28 -0.24 -9.15 9.58 9.54 -0.95 0.55 9.69 -12.21 -10.31 0.39 3.72 + 22.5 82.5 95697 718 17 -15 -30 299.7 -4.5 2.6 -2.4 -2.0 11.86 -4.85 -3.71 0.35 1.42 -5.5 1.3 0.1 0.2 0.0 -60.13 449.47 1.27241 -0.00973 -0.00162 -0.00084 -0.00191 0.57379 -0.06771 -0.01486 0.00308 0.01558 2.7599 0.8824 -0.2252 0.1690 -0.1666 286.9 -1.4 0.7 -1.1 -1.3 -7.62 -9.13 -6.75 -0.22 0.60 -0.20 0.03 -0.87 0.05 0.34 -8.18 -1.21 1.38 6.05 6.88 3.28 -5.19 -3.98 -2.83 -0.72 + 22.5 97.5 89345 387 -17 -29 -58 294.4 -1.9 1.0 -1.9 -0.1 12.65 -4.16 -2.66 0.28 -0.53 -6.1 -1.7 -1.1 0.2 -0.1 -42.81 1056.91 1.27000 -0.00814 -0.00280 -0.00041 -0.00038 0.54829 -0.07212 -0.02861 -0.00413 0.00153 3.0166 0.9867 0.1923 0.3551 -0.0535 283.3 -0.2 0.5 -0.7 -0.3 -11.11 -10.95 -7.72 -0.67 0.43 3.93 0.28 -0.70 -0.17 -0.30 -1.00 0.24 2.62 0.43 4.36 4.79 5.22 7.15 0.80 -6.36 + 22.5 112.5 99313 739 109 -3 -75 295.2 -6.0 -2.4 -1.1 -0.4 12.97 -5.77 -0.98 -0.38 0.41 -5.9 0.6 0.5 0.2 0.3 -8.41 167.74 1.27241 -0.00914 -0.00409 -0.00044 -0.00018 0.60533 -0.04568 -0.01057 -0.00282 -0.00666 2.2958 -0.0634 -0.0220 0.0031 0.0389 284.7 -3.1 -1.1 -0.6 0.1 -13.47 -9.75 -8.92 -1.31 0.71 9.80 -0.16 1.22 0.74 0.40 0.78 -5.02 4.09 1.66 -0.03 -4.35 5.21 0.28 -0.96 0.93 + 22.5 127.5 101264 517 237 51 -17 298.3 -3.2 -2.1 -0.2 -0.4 15.18 -3.66 -1.77 -0.28 -0.28 -9.0 -0.3 0.2 -0.2 -0.1 32.14 -0.00 1.27536 -0.00675 -0.00396 -0.00033 -0.00049 0.60631 -0.03614 -0.01445 -0.00114 -0.00633 2.7823 0.1670 0.1289 -0.0165 0.0264 286.5 -2.1 -1.3 -0.3 -0.2 -12.96 -8.19 -8.41 -0.52 0.10 5.60 -0.84 0.06 0.64 0.09 -7.00 -5.80 8.57 -4.54 -0.93 -1.25 2.24 -2.77 1.50 -0.87 + 22.5 142.5 101326 264 217 36 -0 299.0 -2.2 -2.0 -0.0 -0.8 15.38 -2.65 -2.12 -0.36 -0.83 -9.3 -0.1 -0.0 -0.1 -0.1 45.92 -0.00 1.27581 -0.00411 -0.00379 -0.00003 -0.00089 0.59636 -0.03186 -0.01893 0.00076 -0.00170 2.9041 0.2368 0.1813 -0.0665 -0.0565 287.3 -1.1 -1.3 -0.2 -0.8 -13.74 -8.48 -8.64 -0.78 -1.71 -6.91 -0.13 -0.55 -0.15 -0.23 -5.18 -0.03 6.65 -2.73 -8.58 -3.31 0.78 -1.03 0.79 3.37 + 22.5 157.5 101451 111 143 -13 -19 299.0 -1.6 -1.7 0.0 -0.6 15.12 -1.90 -1.87 -0.18 -0.46 -9.3 -0.1 -0.0 -0.1 -0.0 20.60 -0.00 1.27449 -0.00240 -0.00344 0.00029 -0.00101 0.58223 -0.03747 -0.02109 0.00278 0.00696 3.1345 0.3697 0.2302 -0.0785 -0.1735 287.7 -0.1 -1.0 -0.1 -1.0 -7.56 -7.83 -7.84 -1.69 -2.92 -4.26 -0.14 -0.29 -0.18 0.34 -1.03 0.60 7.20 3.70 -5.38 -5.16 0.57 0.77 0.29 2.22 + 22.5 172.5 101564 11 109 -27 -7 298.5 -1.3 -1.6 0.0 -0.1 14.48 -1.60 -1.58 -0.08 -0.10 -9.4 -0.1 0.0 -0.0 0.0 2.38 -0.00 1.27167 -0.00172 -0.00338 0.00059 -0.00050 0.56798 -0.03221 -0.02146 0.00162 0.00436 3.3658 0.3924 0.2778 -0.0257 -0.1189 287.5 0.0 -0.8 0.0 -0.4 -7.92 -7.52 -5.91 -1.84 -2.11 -1.72 -0.66 -0.05 0.24 0.72 -1.91 2.26 3.68 3.27 -0.11 -2.09 1.73 0.90 -0.82 -2.44 + 22.5 -172.5 101651 -55 106 -25 13 297.8 -1.3 -1.6 0.1 -0.1 13.86 -1.27 -1.36 -0.02 -0.15 -9.4 -0.1 0.0 -0.0 -0.0 5.64 -0.00 1.27039 -0.00233 -0.00379 0.00077 -0.00007 0.56074 -0.01804 -0.01600 -0.00180 -0.00460 3.4687 0.2615 0.2091 0.0376 0.0538 287.0 -0.5 -1.0 0.2 0.0 -5.83 -6.80 -4.03 -1.48 -1.30 1.02 -1.01 -0.50 0.13 0.09 -0.73 -1.75 2.46 3.20 1.05 -2.58 4.34 2.30 -0.49 -0.72 + 22.5 -157.5 101704 -54 97 -12 -5 297.2 -0.9 -1.5 0.0 -0.2 13.62 -0.92 -1.25 -0.01 -0.21 -9.2 -0.0 0.0 -0.0 0.0 4.02 -0.00 1.26732 -0.00353 -0.00464 0.00061 0.00023 0.54917 -0.00255 -0.00576 -0.00168 -0.00420 3.6614 0.0249 0.0462 0.0561 0.0395 286.9 -1.0 -1.4 0.2 0.0 -7.90 -5.68 -3.49 -0.44 -1.31 -3.54 -0.56 -0.64 0.12 0.00 -1.03 -1.68 -1.29 2.80 -0.47 -3.35 1.89 2.60 1.23 1.35 + 22.5 -142.5 101870 34 128 1 -50 294.7 -0.5 -1.3 -0.1 0.0 11.55 -0.58 -1.03 0.07 0.11 -9.4 0.0 -0.0 0.0 0.0 -19.90 -0.00 1.26622 -0.00557 -0.00570 0.00068 0.00078 0.54146 -0.00085 -0.00074 0.00042 -0.00228 3.7861 0.0207 0.0377 0.0683 -0.0180 285.3 -1.1 -1.6 0.1 0.2 -3.67 -4.29 -3.41 0.41 -0.68 -3.52 -0.31 -0.06 -0.05 0.02 -4.60 -1.74 2.33 1.75 -0.77 -2.46 0.06 -1.39 0.24 1.37 + 22.5 -127.5 101798 179 182 20 -57 292.2 -0.3 -1.8 -0.2 0.2 10.23 -0.89 -1.39 0.11 0.36 -9.3 -0.4 -0.2 0.2 0.2 -42.21 -0.00 1.26749 -0.00760 -0.00597 0.00012 0.00047 0.55384 -0.01198 -0.00336 0.00034 0.00662 3.9102 0.3323 0.4106 -0.0200 -0.2976 284.2 -1.6 -2.1 0.2 0.2 -5.07 -4.50 -4.69 1.76 0.95 -3.62 -0.31 0.21 -0.43 -0.01 -10.64 1.27 2.72 1.72 -1.46 1.14 -4.48 -3.91 0.42 3.62 + 22.5 -112.5 101368 233 160 76 -9 294.0 -0.3 -3.4 -0.5 0.9 12.64 -2.04 -2.27 -0.59 0.62 -4.0 -3.6 2.4 0.4 -0.8 -38.71 -0.00 1.26967 -0.00777 -0.00456 -0.00071 -0.00058 0.59160 -0.03131 0.00257 0.00095 0.01490 4.7716 0.7761 1.5686 -0.6252 -0.6926 285.6 -1.6 -2.6 -0.2 -0.5 -7.97 -7.13 -6.73 1.26 1.61 4.59 0.09 0.27 -0.11 0.13 -13.64 -2.92 1.58 0.68 1.80 10.37 -6.57 -8.54 -0.33 2.74 + 22.5 -97.5 101424 314 -9 130 56 297.8 -3.9 -1.7 -0.6 -0.3 14.77 -3.53 -1.04 -0.38 -0.08 -7.7 0.4 0.5 -0.2 0.0 -18.40 3.32 1.26910 -0.00662 -0.00286 -0.00127 -0.00034 0.58707 -0.02353 -0.01918 -0.00123 0.01182 3.1576 0.0739 0.3661 -0.0738 -0.1712 287.1 -2.3 -0.2 -0.6 -0.8 -10.97 -7.52 -5.67 0.41 0.84 -5.78 0.94 1.11 0.63 0.52 -13.54 -1.09 2.67 1.53 1.11 -10.17 9.45 -3.78 3.97 1.94 + 22.5 -82.5 101332 142 123 126 61 299.1 -2.5 -1.5 -0.3 -0.0 14.92 -2.60 -1.69 -0.13 0.20 -8.1 -0.1 0.2 -0.0 0.0 -19.52 19.27 1.27283 -0.00421 -0.00341 -0.00055 0.00003 0.58438 -0.03805 -0.02806 0.00318 0.00503 3.3107 0.4195 0.4491 -0.0439 -0.0065 287.4 -0.8 -0.4 -0.4 -0.2 -8.44 -5.85 -5.70 0.60 0.49 -0.32 0.15 -0.17 0.46 0.50 -2.07 -5.00 4.40 2.24 8.92 -0.13 -0.32 -0.38 -0.01 -2.89 + 22.5 -67.5 101669 1 139 134 90 299.0 -1.4 -1.7 0.0 0.0 14.92 -1.66 -1.62 -0.05 -0.04 -9.4 -0.2 0.0 0.0 -0.1 -50.80 -0.00 1.26953 -0.00321 -0.00377 -0.00012 0.00035 0.57458 -0.03098 -0.02055 -0.00421 -0.00808 3.2996 0.3081 0.2718 0.0534 0.1230 287.5 -0.2 -0.9 0.2 0.3 -4.11 -4.95 -5.68 1.64 1.00 -0.50 -1.11 0.07 0.11 0.34 -6.10 -2.18 -0.68 1.66 7.23 -1.54 1.75 0.87 -0.94 -1.56 + 22.5 -52.5 101808 -77 149 128 83 298.1 -1.0 -1.7 -0.0 0.0 13.95 -1.31 -1.72 -0.21 0.07 -9.4 -0.1 -0.0 -0.0 -0.0 -35.60 -0.00 1.26819 -0.00399 -0.00412 -0.00014 0.00088 0.56324 -0.02115 -0.02161 -0.01035 -0.00981 3.4020 0.2360 0.2787 0.1380 0.1536 287.1 -0.5 -1.0 0.4 0.6 -1.32 -4.14 -4.89 1.59 1.92 4.70 -1.21 -0.41 -0.47 0.29 -0.25 0.60 0.83 0.05 3.09 -2.74 1.73 -0.76 -0.34 0.19 + 22.5 -37.5 101875 -69 145 82 56 296.7 -0.7 -1.8 -0.2 -0.1 12.74 -0.98 -1.99 -0.21 0.02 -9.5 -0.1 -0.1 -0.0 0.0 0.50 -0.00 1.26751 -0.00600 -0.00490 -0.00003 0.00119 0.55399 -0.01403 -0.02707 -0.00316 -0.00694 3.5803 0.0900 0.3795 0.0321 0.1059 286.4 -0.9 -1.1 0.0 0.5 -0.29 -4.22 -4.48 1.61 2.81 3.63 -1.34 -0.81 -0.19 -0.54 -2.09 4.63 2.74 -1.32 1.21 -2.51 -0.28 -0.44 -0.13 0.88 + 22.5 -22.5 101714 74 99 67 -7 294.8 -0.5 -2.0 -0.3 -0.2 12.37 -1.31 -2.22 -0.49 0.38 -8.1 -0.4 -0.3 -0.4 0.5 24.23 -0.00 1.26940 -0.00903 -0.00529 0.00020 0.00126 0.56252 -0.02937 -0.03611 0.00124 0.00299 4.5408 0.3767 0.7883 -0.0416 0.1131 286.0 -1.4 -1.1 -0.1 0.3 -5.91 -4.85 -5.63 2.03 2.52 0.86 -0.52 -0.51 -0.03 -0.29 -7.66 11.17 3.60 -3.43 0.12 -1.71 -2.61 -1.16 0.59 2.47 + 22.5 -7.5 96968 404 55 73 -26 302.4 -8.7 -3.1 -1.3 0.8 4.62 -1.08 -1.42 -0.21 0.43 -6.3 0.1 0.0 1.1 0.3 30.61 373.24 1.27398 -0.01339 -0.00495 -0.00043 0.00120 0.61266 -0.02239 -0.00790 0.00170 -0.00196 1.9035 0.4024 0.1526 0.0996 0.0798 285.0 -4.7 -1.8 -0.6 0.5 -8.27 -7.43 -6.55 2.11 3.11 -2.84 0.20 0.53 -0.59 0.01 -11.37 5.73 0.02 -1.74 0.97 -0.26 -0.93 1.95 -0.34 0.01 + 7.5 7.5 98675 -141 -129 62 40 299.8 2.1 1.8 -0.0 -0.3 15.40 -2.45 -0.32 -1.60 -0.83 -5.8 1.4 -0.6 0.6 -0.2 22.93 207.44 1.27692 0.00150 0.00148 0.00016 -0.00028 0.61084 -0.03548 -0.01213 -0.00772 0.00504 2.5823 0.4111 0.0851 0.0450 -0.1169 287.4 2.1 1.6 -0.1 -0.6 1.53 -0.71 -0.39 -0.08 -0.22 -1.45 -0.09 -0.20 0.17 0.09 -24.42 -16.22 -4.58 4.38 11.30 -2.75 -7.50 -2.93 -1.69 0.07 + 7.5 22.5 92650 -106 -104 68 15 298.4 2.5 2.0 -0.3 -0.0 12.67 -4.36 -1.09 -1.66 -1.05 -4.5 1.7 -0.1 0.6 -0.6 -2.89 751.24 1.27541 0.00147 0.00164 -0.00027 -0.00007 0.58676 -0.02661 -0.00834 -0.00403 0.00571 2.3918 -0.0925 -0.0112 -0.1407 -0.1619 285.3 1.4 1.3 -0.4 -0.6 3.56 -0.41 -0.35 -0.01 -0.11 -1.87 0.13 0.00 0.07 0.06 -15.55 -17.46 -8.77 4.98 8.11 -0.80 -0.32 0.34 1.92 -1.96 + 7.5 37.5 81555 -55 -36 31 11 291.4 1.8 1.1 -0.5 -0.3 10.90 -1.84 0.00 -0.41 -0.08 -5.6 0.0 0.0 -0.0 -0.1 -9.16 1865.08 1.27205 -0.00012 0.00069 -0.00053 -0.00053 0.51611 -0.02812 -0.00039 -0.00436 0.00338 2.7385 0.1990 0.0087 -0.0056 -0.1069 280.1 1.5 0.7 -0.2 -0.3 4.11 -0.18 -0.39 0.03 0.05 -1.15 0.45 -0.03 -0.16 -0.11 -2.71 -10.57 -0.60 6.09 8.28 -10.94 -1.54 -3.30 0.49 -0.23 + 7.5 52.5 101081 179 -6 16 49 299.7 -0.7 0.6 -0.4 -0.9 16.02 -1.01 -0.02 -0.48 -0.80 -9.3 -0.1 -0.3 -0.0 0.0 -52.34 0.00 1.27529 -0.00183 0.00054 -0.00057 -0.00031 0.57551 -0.02565 -0.02275 0.01056 -0.01245 3.4813 0.3710 0.4807 -0.2845 0.2603 289.1 0.1 1.3 -0.9 -0.1 2.63 2.99 0.68 -0.59 -0.23 -3.99 -0.23 0.04 -0.20 0.19 -9.08 -20.95 -8.02 2.46 11.06 2.21 9.26 1.26 1.55 -4.77 + 7.5 67.5 101027 88 -13 -2 61 300.6 -0.4 0.4 -0.1 -0.2 16.65 -0.58 0.17 -0.10 -0.46 -9.4 -0.1 -0.1 0.1 -0.0 -86.05 -0.00 1.27517 -0.00095 0.00075 -0.00036 -0.00045 0.60643 -0.02401 -0.02691 0.00434 -0.01911 2.9865 0.3236 0.4588 -0.1066 0.3149 288.3 0.6 1.6 -0.4 0.5 0.87 1.07 0.37 -0.04 0.15 -6.90 -0.01 0.16 0.28 -0.08 -12.63 -15.98 -7.95 0.25 4.43 6.47 -4.82 -0.81 -2.74 -0.78 + 7.5 82.5 100897 205 35 1 52 300.9 -1.4 -0.0 -0.2 -0.2 17.11 -0.62 0.21 -0.29 -0.62 -8.9 -0.1 -0.2 0.0 -0.1 -94.84 -0.00 1.27644 -0.00220 0.00034 -0.00027 -0.00026 0.63051 -0.01702 -0.02336 0.00077 -0.01365 2.7224 0.2156 0.4148 -0.0708 0.2475 287.7 -0.2 1.2 -0.3 0.2 1.32 1.41 0.24 0.08 0.10 1.17 -0.71 -0.18 -0.17 -0.06 -13.68 -20.17 -17.02 1.23 -2.20 3.56 1.22 2.06 0.22 1.67 + 7.5 97.5 100919 99 -10 5 34 301.2 -0.4 0.5 -0.1 -0.0 17.21 -0.58 0.01 -0.19 -0.40 -9.2 0.1 -0.1 -0.1 -0.1 -28.31 -0.00 1.27783 -0.00138 0.00050 -0.00008 -0.00027 0.64409 -0.02177 -0.01814 -0.00275 -0.01140 2.5166 0.1803 0.2426 0.0297 0.1834 287.1 0.5 1.1 -0.1 0.2 -2.99 1.07 0.12 -0.05 0.01 10.25 -0.09 -0.01 0.10 -0.05 -9.19 -25.08 -6.97 -1.57 1.92 0.27 -0.42 0.67 0.46 1.50 + 7.5 112.5 100907 79 32 -4 42 300.8 -0.9 -0.0 -0.2 -0.3 17.49 -0.27 -0.07 -0.02 -0.33 -9.2 0.1 -0.0 0.0 -0.0 28.13 -0.00 1.27785 -0.00127 0.00028 -0.00039 -0.00049 0.64241 -0.01214 -0.02007 0.00873 -0.00602 2.4859 0.0980 0.2685 -0.1191 0.1149 287.1 0.1 0.9 -0.5 -0.1 -3.26 1.47 0.99 0.08 0.01 8.68 -0.12 -0.05 0.09 -0.04 -10.04 -15.27 -14.08 4.94 -0.12 2.53 0.98 1.92 -0.50 0.62 + 7.5 127.5 100890 18 46 -12 40 300.6 -0.3 -0.3 -0.1 -0.2 17.60 -0.14 -0.18 0.02 -0.32 -9.0 0.0 -0.0 0.0 -0.0 57.90 -0.00 1.27791 -0.00056 0.00003 -0.00022 -0.00061 0.64338 -0.01377 -0.01446 0.00885 -0.00219 2.4194 0.1210 0.1692 -0.1074 0.0379 287.2 0.4 0.5 -0.4 -0.3 2.48 1.32 1.08 0.13 0.27 3.87 -0.34 -0.24 0.02 0.08 -8.87 -9.38 -14.25 2.09 4.37 -2.64 -3.24 -0.50 0.10 -0.83 + 7.5 142.5 100880 -32 12 -15 36 300.7 0.1 0.0 -0.0 -0.2 17.46 -0.17 -0.15 0.00 -0.38 -9.2 -0.0 -0.0 -0.0 -0.0 62.45 -0.00 1.27936 -0.00004 0.00037 0.00004 -0.00031 0.64095 -0.02009 -0.01601 0.00263 -0.00600 2.4496 0.2310 0.2068 -0.0322 0.0850 287.3 0.9 0.7 -0.1 -0.1 -2.37 1.05 0.66 0.36 0.27 -1.42 -0.22 -0.07 -0.02 0.11 -20.11 -14.52 -21.95 -1.20 2.25 -0.45 -0.84 -0.49 -1.89 -1.39 + 7.5 157.5 100888 -49 10 -23 20 300.6 0.0 0.0 0.0 -0.1 17.47 -0.29 -0.23 -0.02 -0.39 -9.2 -0.1 -0.1 -0.0 -0.0 45.72 -0.00 1.27945 0.00023 0.00049 0.00024 -0.00000 0.63406 -0.02291 -0.01951 -0.00210 -0.01215 2.5124 0.2559 0.2287 0.0288 0.1391 287.6 0.9 0.8 0.1 0.2 -4.95 0.72 0.29 0.48 0.29 -1.74 -0.15 -0.06 0.03 0.03 -28.94 -17.00 -24.35 -4.84 1.19 -1.97 0.64 -0.01 -0.92 -0.52 + 7.5 172.5 100922 -53 9 -25 15 300.5 -0.1 0.0 -0.0 -0.1 17.38 -0.32 -0.25 -0.05 -0.33 -9.2 -0.0 -0.1 -0.0 -0.0 24.07 -0.00 1.27815 0.00006 0.00038 0.00035 0.00006 0.62585 -0.02222 -0.02136 -0.00395 -0.01145 2.5791 0.2146 0.2560 0.0395 0.1130 287.8 0.8 0.8 0.2 0.3 -1.43 0.35 -0.30 0.62 0.42 -3.23 -0.23 -0.21 0.11 0.04 -31.99 -12.79 -23.30 -4.64 -0.10 -0.79 0.22 -0.32 -0.08 -0.09 + 7.5 -172.5 100959 -58 9 -24 10 300.0 -0.1 -0.0 -0.1 -0.1 16.98 -0.26 -0.18 0.01 -0.26 -9.3 -0.1 -0.0 0.0 -0.0 11.21 -0.00 1.27685 -0.00014 0.00008 0.00045 -0.00000 0.62639 -0.01876 -0.01789 -0.00750 -0.01195 2.5096 0.1792 0.2318 0.0577 0.1052 287.4 0.6 0.6 0.3 0.3 -0.31 -0.26 -0.76 0.49 0.38 0.11 -0.31 -0.24 -0.00 -0.16 -24.98 -9.66 -23.58 -11.37 -0.93 0.26 0.99 0.84 -0.07 0.82 + 7.5 -157.5 101003 -48 9 -14 9 299.4 -0.1 -0.0 -0.2 -0.1 16.70 -0.17 -0.10 -0.01 -0.28 -9.2 -0.1 -0.1 0.1 -0.0 12.93 -0.00 1.27651 -0.00084 -0.00033 0.00026 -0.00015 0.62590 -0.01078 -0.01406 -0.00514 -0.00875 2.4404 0.1273 0.2099 0.0303 0.0588 287.2 0.2 0.4 0.2 0.1 -2.52 -0.79 -0.93 0.28 0.14 -1.81 -0.25 -0.18 -0.05 -0.13 -16.25 -5.15 -26.26 -11.87 1.02 0.60 1.07 0.80 0.42 0.22 + 7.5 -142.5 101054 -27 3 -11 12 298.8 -0.2 -0.0 -0.2 -0.0 16.47 -0.06 0.13 0.11 -0.32 -9.0 -0.0 -0.1 0.1 -0.1 -3.96 -0.00 1.27525 -0.00149 -0.00049 -0.00011 -0.00025 0.62676 -0.00879 -0.00892 -0.00120 -0.00700 2.3651 0.1239 0.1876 -0.0052 0.0547 286.8 0.1 0.3 0.0 0.0 -2.41 -0.70 -0.82 0.08 0.07 -3.42 -0.18 0.05 -0.17 -0.08 -3.74 -5.39 -31.40 -6.59 5.34 -0.61 1.00 2.09 0.83 -1.09 + 7.5 -127.5 101085 -14 -21 -3 22 298.8 -0.4 0.2 -0.3 0.0 16.37 0.05 0.55 0.11 -0.41 -8.8 0.2 0.0 0.2 -0.3 -27.71 -0.00 1.27463 -0.00191 -0.00042 -0.00042 -0.00028 0.62606 -0.01277 -0.00575 0.00207 -0.00678 2.3709 0.1428 0.1779 -0.0307 0.0806 286.8 0.2 0.5 -0.2 -0.1 -3.90 -0.48 -0.53 -0.18 -0.08 -2.38 0.09 0.17 -0.10 -0.03 10.13 -7.64 -33.70 1.96 2.07 -0.46 -0.93 1.64 0.07 -0.49 + 7.5 -112.5 101100 -11 -46 10 24 298.9 -0.2 0.7 -0.2 0.0 16.39 0.07 0.62 0.01 -0.35 -8.9 0.1 -0.1 0.2 -0.2 -30.61 0.00 1.27538 -0.00163 0.00003 -0.00050 -0.00016 0.62512 -0.02402 -0.01008 0.00246 -0.00595 2.4369 0.2547 0.2424 -0.0519 0.1237 286.8 0.8 0.9 -0.3 -0.1 -1.76 -0.15 -0.10 -0.15 -0.12 2.78 0.37 0.13 0.12 0.08 10.53 -21.63 -26.21 0.20 0.59 0.14 -3.41 -3.33 0.42 0.57 + 7.5 -97.5 101088 -7 -44 23 23 299.2 0.1 0.9 -0.2 -0.1 16.73 -0.23 0.16 -0.08 -0.29 -8.9 -0.1 -0.4 0.1 -0.1 -7.98 -0.00 1.27557 -0.00078 0.00080 -0.00050 -0.00017 0.62193 -0.03282 -0.02196 0.00114 -0.00782 2.5606 0.3948 0.3970 -0.0623 0.1470 287.1 1.1 1.3 -0.2 0.1 1.07 0.25 0.05 0.14 0.11 2.75 0.13 0.14 0.16 0.11 -6.47 -24.70 -26.64 2.39 1.95 1.54 -1.54 -0.98 -1.04 -1.45 + 7.5 -82.5 101007 -30 -34 16 7 299.9 0.2 1.1 -0.1 0.4 17.57 -0.46 -0.36 0.03 -0.38 -8.0 0.0 -0.2 0.1 -0.1 12.73 -0.00 1.27608 0.00001 0.00118 -0.00021 0.00030 0.63414 -0.02879 -0.02189 -0.00418 -0.01470 2.4870 0.3303 0.2886 0.0395 0.2030 287.2 1.0 1.3 0.0 0.6 1.78 -0.67 -0.35 0.23 0.07 1.97 0.00 -0.01 0.12 0.08 -8.92 -18.91 -20.62 -2.78 -7.86 0.20 -0.78 -2.54 1.16 -2.40 + 7.5 -67.5 100166 -123 -26 71 55 300.3 1.2 1.5 -0.7 0.2 15.99 -1.32 -1.52 -0.08 -0.99 -6.0 -0.8 -1.1 -0.2 -0.4 -10.78 75.31 1.27595 0.00075 0.00112 -0.00055 -0.00007 0.61143 -0.02852 -0.01052 -0.00373 -0.01282 2.4761 0.1525 -0.0792 0.0310 0.0240 288.0 1.3 0.6 -0.3 0.3 -1.47 0.84 0.40 0.75 0.62 -3.86 -0.35 -0.13 0.10 0.11 -39.47 -0.30 -11.20 2.49 0.41 -4.83 8.80 -3.57 1.57 0.35 + 7.5 -52.5 101195 -85 39 33 65 299.9 -0.1 -0.4 -0.3 -0.2 16.23 -0.31 -0.48 0.04 -0.52 -9.3 -0.1 0.0 0.1 -0.1 -38.54 -0.00 1.27509 -0.00050 0.00002 -0.00047 -0.00027 0.61129 -0.01724 -0.01948 0.00274 -0.01222 2.7261 0.2133 0.2316 -0.1102 0.1279 287.5 0.4 0.5 -0.2 0.1 -6.17 -1.04 -0.36 0.01 0.17 -5.04 -0.46 -0.45 -0.19 -0.10 -21.66 -6.86 -34.02 -9.11 2.44 -0.48 1.65 -6.12 -1.18 1.97 + 7.5 -37.5 101244 -81 3 28 56 299.0 -0.0 -0.3 -0.0 -0.2 15.91 -0.42 -0.75 0.15 -0.56 -9.2 -0.3 -0.3 0.1 -0.1 -10.51 -0.00 1.27451 -0.00086 0.00065 -0.00060 -0.00028 0.62145 -0.00932 -0.02972 0.00301 -0.00533 2.5842 0.1613 0.4124 -0.0769 0.0680 286.6 0.1 1.0 -0.2 -0.1 0.39 -1.03 -0.23 -0.12 -0.38 5.72 -0.14 -0.12 -0.10 -0.13 -24.03 -7.24 -22.44 -7.92 8.34 2.26 -0.53 -1.30 -0.17 0.75 + 7.5 -22.5 101223 -99 -39 41 46 298.8 0.3 -0.5 0.4 -0.4 15.95 -0.38 -0.69 -0.02 -0.56 -9.1 -0.3 -0.2 -0.1 0.0 16.63 -0.00 1.27631 -0.00042 0.00136 -0.00079 -0.00011 0.63098 -0.01362 -0.01342 0.00235 0.00020 2.6218 0.4080 0.3973 -0.0244 0.1219 286.5 0.5 0.6 -0.3 -0.4 -0.77 -1.09 -0.42 -0.11 -0.49 3.33 0.02 0.07 -0.01 -0.05 -20.34 -22.84 -23.62 3.70 13.72 0.48 -4.36 4.62 -2.13 0.18 + 7.5 -7.5 96717 -124 -99 48 27 297.9 1.7 1.4 0.1 -0.1 15.24 -1.60 -0.08 -1.32 -0.91 -5.4 0.6 -0.1 0.1 -0.0 32.13 392.57 1.27584 0.00052 0.00101 0.00008 -0.00011 0.59726 -0.01871 -0.00181 -0.00668 0.00328 2.6961 0.3455 0.0231 0.1115 -0.1184 286.2 1.2 1.0 -0.1 -0.4 1.51 -1.09 -0.37 -0.16 -0.39 -2.54 0.44 0.09 0.10 -0.11 -10.86 -19.85 -5.27 -4.27 8.50 -7.64 -0.87 0.25 -0.40 -1.41 + -7.5 7.5 101223 -197 -171 69 49 297.3 1.7 3.0 -0.4 -0.4 14.64 1.30 1.80 -0.44 -0.55 -9.5 -0.1 -0.2 -0.1 0.1 8.51 -0.00 1.27618 -0.00010 0.00139 -0.00035 -0.00051 0.60114 0.04743 0.00885 -0.01712 -0.00049 2.7967 -0.6320 0.1672 0.3669 -0.0577 287.2 -1.0 0.8 0.2 -0.3 -2.08 -0.05 0.26 0.08 -0.16 -0.38 0.10 -0.02 -0.03 0.01 24.09 -4.80 4.52 -0.32 -3.52 13.40 4.63 -0.60 -2.23 -1.23 + -7.5 22.5 91286 -72 -8 56 -2 296.1 -2.2 -0.2 0.1 0.2 13.55 3.76 0.42 -1.99 -0.33 -3.9 -0.7 0.1 0.0 0.0 -5.61 887.66 1.27228 -0.00184 0.00033 -0.00010 -0.00004 0.57130 0.04015 0.01129 -0.00706 -0.00235 2.6347 -0.0816 -0.0848 -0.1483 0.0123 284.3 -1.7 -0.5 -0.2 0.1 -6.71 -0.03 0.12 0.34 -0.16 -3.88 0.18 0.15 0.06 0.04 8.00 -20.18 -0.54 0.75 -4.70 -3.99 0.52 1.75 2.81 -0.22 + -7.5 37.5 96586 -296 -117 43 21 297.0 1.1 0.1 -0.2 -0.3 13.44 2.31 1.63 -0.51 -0.31 -6.8 0.4 0.5 -0.1 0.1 -22.74 420.39 1.27227 0.00078 0.00062 -0.00039 -0.00042 0.56582 0.03361 0.03185 -0.00411 -0.00315 2.7123 -0.1672 -0.2282 -0.0236 -0.0011 285.9 0.4 -0.4 -0.2 -0.2 -0.51 0.38 -0.08 0.15 -0.06 -3.71 0.88 -0.26 -0.17 -0.06 4.12 -14.77 -7.79 -2.79 -10.93 -9.21 -8.89 -1.44 -1.70 -2.99 + -7.5 52.5 101170 -131 -136 30 50 299.4 0.8 1.1 -0.3 -0.4 16.25 0.84 1.02 -0.25 -0.41 -9.3 0.0 -0.0 0.0 0.0 -37.07 -0.00 1.27541 -0.00028 0.00092 -0.00051 -0.00056 0.59880 0.03248 0.01826 0.00112 -0.00310 2.8997 -0.3865 -0.1999 -0.0322 0.0138 287.8 -0.5 0.3 -0.3 -0.3 -4.63 1.27 0.81 -0.08 0.05 -1.98 0.50 0.12 -0.25 -0.04 8.80 -5.85 -9.54 -2.83 -6.28 6.58 -0.76 -3.07 0.01 1.21 + -7.5 67.5 101077 -63 -81 1 49 299.6 0.3 0.7 -0.2 -0.3 16.63 0.61 0.70 -0.15 -0.33 -9.3 0.1 -0.0 0.0 -0.0 -62.99 -0.00 1.27483 -0.00067 0.00072 -0.00031 -0.00063 0.62101 0.01980 0.00747 -0.00117 0.00188 2.6190 -0.2478 -0.0593 0.0088 -0.0477 287.2 -0.4 0.4 -0.1 -0.4 -7.18 1.31 0.86 0.01 0.08 -4.44 -0.01 0.08 0.05 0.02 16.07 -8.65 -16.89 2.54 -2.29 2.55 -1.07 -1.51 -1.10 -0.66 + -7.5 82.5 101027 -34 -69 3 30 299.8 0.2 0.5 -0.2 -0.2 16.82 0.19 0.53 -0.12 -0.14 -9.2 -0.0 -0.0 0.0 0.0 -80.37 -0.00 1.27448 -0.00057 0.00068 -0.00031 -0.00069 0.62910 0.00686 0.00493 0.00158 0.00369 2.5359 -0.0855 -0.0392 -0.0343 -0.0731 287.0 -0.1 0.4 -0.2 -0.4 -5.96 1.39 0.98 0.00 0.09 2.12 0.13 -0.08 -0.03 -0.18 20.90 0.11 -5.05 6.18 -4.94 1.49 -3.33 1.80 1.86 -0.75 + -7.5 97.5 100989 -31 -86 -22 27 300.1 -0.2 0.5 0.1 -0.1 16.77 0.18 0.72 -0.03 -0.28 -9.3 0.1 0.0 -0.0 -0.0 -31.68 -0.00 1.27574 -0.00089 0.00041 -0.00004 -0.00054 0.62689 0.01952 0.01757 0.00215 -0.00147 2.5696 -0.2325 -0.2094 -0.0552 0.0104 287.2 -0.7 -0.1 -0.1 -0.2 2.94 1.15 1.02 0.08 0.13 10.60 -0.05 -0.14 0.08 0.02 27.19 3.87 -11.58 1.80 -3.59 -1.34 6.18 4.12 -1.14 -1.54 + -7.5 112.5 97556 -79 -55 -9 31 298.0 -0.1 -0.1 -0.3 -0.5 15.86 1.14 1.04 -0.01 -0.49 -6.8 1.0 0.7 0.1 -0.0 29.21 302.99 1.27780 -0.00091 -0.00006 -0.00030 -0.00059 0.60420 0.03705 0.02978 0.00651 -0.00135 2.7613 -0.3957 -0.3337 -0.0713 0.0279 286.3 -1.1 -0.8 -0.5 -0.4 5.43 0.84 0.77 0.15 0.01 5.99 -0.14 -0.13 0.03 -0.15 11.53 -4.03 0.18 5.63 -0.25 -9.00 1.53 3.40 5.42 3.28 + -7.5 127.5 100964 -186 -88 -15 36 300.4 0.9 0.5 -0.1 -0.6 16.53 1.46 0.75 -0.16 -0.47 -9.4 0.1 0.1 0.0 0.0 47.00 -0.00 1.27817 0.00002 0.00004 -0.00026 -0.00063 0.61964 0.02688 0.02848 0.00863 0.00622 2.7706 -0.2513 -0.4870 -0.2222 -0.0777 287.6 -0.2 -0.6 -0.4 -0.8 9.87 1.73 1.07 0.28 0.28 -2.17 -0.28 -0.09 0.10 0.05 28.82 -16.80 -5.04 -0.78 -4.65 3.63 -5.08 -0.40 -0.62 0.96 + -7.5 142.5 100395 -162 -78 -17 32 298.3 0.9 0.2 -0.1 -0.3 17.25 0.67 0.70 0.05 -0.44 -6.3 0.4 0.5 0.1 -0.1 75.56 49.12 1.27766 0.00060 0.00042 -0.00013 -0.00047 0.64596 0.01423 0.01598 0.00038 0.00542 2.2946 -0.0451 -0.1187 -0.0263 -0.1018 286.3 0.1 -0.2 0.0 -0.6 1.46 1.29 0.83 -0.05 -0.17 1.92 -0.19 -0.12 0.01 0.13 44.83 -17.58 -17.99 -3.58 -6.99 13.88 -5.68 -3.06 -2.33 -4.22 + -7.5 157.5 100642 -120 -57 -31 13 300.3 0.3 0.2 0.0 -0.2 17.60 0.14 0.22 0.07 -0.10 -9.3 -0.0 -0.0 0.0 0.0 66.68 18.59 1.27875 0.00017 0.00047 0.00007 -0.00035 0.65409 0.00567 0.00753 0.00091 0.00374 2.2463 -0.0424 -0.0734 0.0016 -0.0339 286.6 0.1 -0.1 0.0 -0.3 -3.74 1.15 0.70 0.05 -0.11 -6.53 -0.09 0.11 -0.04 -0.03 4.09 -1.01 -2.46 0.18 1.88 -4.39 2.06 -1.19 -0.23 -0.10 + -7.5 172.5 100867 -106 -43 -39 6 300.7 0.2 0.0 -0.0 -0.2 17.50 0.06 0.32 0.04 -0.06 -9.3 -0.0 0.0 -0.0 0.0 39.63 -0.00 1.27888 -0.00020 0.00020 0.00019 -0.00019 0.63997 0.01417 0.01258 0.00058 0.00113 2.4276 -0.1257 -0.1296 -0.0071 -0.0036 287.4 -0.5 -0.3 0.0 -0.2 -4.71 1.00 0.29 0.17 -0.08 -2.81 -0.04 -0.05 0.07 -0.02 -9.73 -2.62 4.14 -0.03 -2.20 -5.58 1.19 2.54 0.47 -0.87 + -7.5 -172.5 100902 -90 -46 -33 7 300.8 0.0 0.0 -0.0 -0.2 17.16 -0.01 0.54 -0.00 -0.08 -9.3 -0.0 0.1 -0.0 0.0 20.68 -0.00 1.27833 -0.00050 -0.00014 0.00031 -0.00020 0.61501 0.02309 0.02547 0.00204 0.00032 2.7374 -0.1951 -0.2645 -0.0219 0.0180 288.3 -0.9 -0.8 -0.1 -0.2 -4.22 0.83 0.29 0.11 0.04 -1.03 -0.03 -0.11 0.12 -0.02 -18.84 -10.10 7.05 1.08 -0.88 -6.99 -0.38 1.71 -0.09 -0.08 + -7.5 -157.5 100969 -70 -54 -22 14 300.5 0.0 0.2 0.0 -0.2 16.53 -0.18 0.76 -0.15 -0.10 -9.4 -0.0 0.1 -0.0 0.0 10.66 -0.00 1.27747 -0.00086 -0.00038 0.00030 -0.00038 0.58031 0.02316 0.03516 0.00097 -0.00003 3.2103 -0.2052 -0.4164 0.0023 0.0201 289.3 -1.0 -1.1 -0.1 -0.2 0.40 0.52 0.11 0.08 -0.13 -1.06 -0.19 -0.07 -0.06 -0.01 -20.85 -12.69 5.64 -2.56 1.38 -7.57 -1.48 -0.29 -1.40 0.44 + -7.5 -142.5 101061 -61 -71 -10 18 300.0 0.0 0.5 -0.0 -0.2 15.86 -0.06 0.96 -0.22 -0.09 -9.5 0.0 0.1 -0.0 0.0 1.98 0.00 1.27657 -0.00140 -0.00027 0.00002 -0.00055 0.54767 0.01540 0.03446 -0.00417 0.00217 3.7347 -0.1166 -0.4621 0.0752 -0.0535 290.0 -0.7 -0.8 0.0 -0.3 1.83 0.54 0.12 0.07 -0.12 -0.97 -0.27 -0.07 -0.13 -0.10 -5.92 -8.12 8.34 -5.07 2.73 -4.48 -1.28 -0.87 -1.17 0.18 + -7.5 -127.5 101169 -47 -95 4 20 299.0 -0.1 0.8 -0.1 -0.2 14.89 -0.06 1.21 -0.24 -0.12 -9.5 0.0 0.1 -0.0 0.0 -10.80 -0.00 1.27556 -0.00155 0.00001 -0.00033 -0.00059 0.53063 0.01000 0.03419 -0.00947 0.00415 3.9676 -0.0382 -0.5261 0.1519 -0.0741 289.7 -0.5 -0.4 0.2 -0.4 -0.74 0.64 0.25 0.09 -0.15 -0.99 0.12 0.06 -0.06 0.10 6.45 -2.24 10.54 -6.17 0.97 -2.13 -0.13 0.43 -0.86 0.05 + -7.5 -112.5 101253 -38 -126 23 15 297.9 -0.4 1.2 -0.2 -0.2 13.90 -0.15 1.50 -0.44 -0.08 -9.5 -0.0 0.1 -0.0 0.0 -11.58 -0.00 1.27483 -0.00121 0.00037 -0.00041 -0.00041 0.52804 0.01042 0.03452 -0.01244 0.00519 3.9328 -0.1140 -0.5898 0.1705 -0.1056 288.9 -0.6 0.0 0.2 -0.3 -3.49 0.33 0.33 0.09 -0.42 1.29 0.36 0.11 0.12 0.09 12.98 1.55 10.56 -6.62 2.80 0.24 0.52 0.67 -0.13 0.52 + -7.5 -97.5 101283 -55 -167 32 -1 296.6 -0.3 2.4 -0.2 -0.0 13.24 0.01 2.12 -0.53 0.15 -9.6 0.0 0.2 -0.1 0.0 -12.64 -0.00 1.27556 -0.00069 0.00061 -0.00022 -0.00027 0.54635 0.01176 0.03078 -0.01190 0.00711 3.6021 -0.1981 -0.5674 0.1424 -0.1561 287.7 -0.5 0.8 0.2 -0.2 -1.23 0.23 0.25 0.06 -0.38 0.10 0.30 0.22 0.15 -0.02 20.76 2.74 8.49 -4.93 1.88 4.09 1.54 0.46 0.17 0.25 + -7.5 -82.5 101252 -103 -171 27 -18 293.7 1.8 3.6 -0.2 0.5 12.50 1.25 2.54 -0.31 0.34 -8.9 0.1 0.0 -0.2 -0.1 -0.12 0.00 1.27491 -0.00045 0.00075 -0.00018 -0.00016 0.61714 0.01421 0.00802 -0.00970 -0.00125 2.5011 -0.0617 0.0263 0.1017 -0.0390 285.5 -0.1 1.1 0.1 0.2 0.35 -0.01 0.06 0.07 -0.17 2.12 0.05 -0.04 0.09 -0.01 35.84 -9.99 -3.82 0.15 -3.17 24.05 2.65 -2.34 -1.69 -2.87 + -7.5 -67.5 100058 -178 17 46 37 298.0 0.2 -0.6 -0.4 0.2 17.16 0.86 0.26 -0.24 -0.20 -3.8 -1.2 -0.8 0.8 0.6 18.88 87.67 1.27368 -0.00013 -0.00046 -0.00031 -0.00012 0.63124 0.02853 0.01713 -0.00548 -0.00719 2.4574 -0.2470 -0.2149 0.0541 0.0927 286.7 -0.8 -0.9 -0.1 0.2 -3.86 0.14 -0.23 0.16 -0.11 -6.00 0.13 0.02 -0.02 -0.03 10.58 -13.21 0.68 2.73 -1.29 -1.80 3.72 0.22 -1.38 -0.79 + -7.5 -52.5 97741 -114 32 38 27 298.2 -1.6 -1.3 0.2 0.7 15.69 1.91 0.98 -0.78 -1.16 -3.9 -2.1 -0.2 1.0 0.2 -19.35 295.12 1.27541 -0.00153 -0.00080 0.00004 0.00019 0.60486 0.03901 0.02313 -0.00698 -0.00554 2.5669 -0.2412 -0.1822 0.0205 -0.0468 286.7 -1.7 -1.3 0.0 0.2 -3.75 0.25 0.04 0.09 -0.09 -0.45 -0.33 -0.04 -0.02 0.09 2.82 -12.26 9.93 3.68 1.45 -6.87 -0.98 1.38 1.12 -0.75 + -7.5 -37.5 94916 -185 -74 35 62 297.5 1.5 0.5 -0.3 -0.5 12.54 0.02 1.25 -0.04 -0.12 -8.4 0.1 0.3 -0.1 -0.2 -6.75 564.23 1.27537 -0.00007 0.00018 -0.00064 -0.00067 0.54704 0.01444 0.03681 0.00157 -0.00505 3.0133 -0.2199 -0.4840 -0.0525 0.1131 286.3 0.2 -0.9 -0.3 -0.2 0.28 0.64 0.30 -0.07 -0.20 3.45 -0.20 -0.12 -0.13 0.02 8.63 -4.16 8.51 -0.38 0.87 -12.56 -12.79 -1.60 3.02 0.71 + -7.5 -22.5 101327 -138 -147 39 52 298.7 -0.1 1.4 -0.1 -0.1 14.86 0.37 1.24 -0.22 -0.22 -9.5 0.0 0.0 -0.0 0.0 0.21 -0.00 1.27592 -0.00052 0.00102 -0.00059 -0.00042 0.54709 0.01103 0.02058 -0.00406 -0.00295 3.5950 -0.0868 -0.2647 0.0122 0.0085 289.1 -0.1 0.4 0.0 -0.1 1.31 0.10 0.23 0.05 -0.36 4.21 0.28 -0.03 0.09 -0.05 18.81 0.69 12.02 -4.41 -1.79 -1.49 3.14 -2.15 -1.08 0.54 + -7.5 -7.5 101353 -166 -165 57 37 297.4 0.1 2.2 -0.2 0.0 14.13 0.74 1.47 -0.45 -0.23 -9.5 0.1 -0.0 -0.1 -0.0 15.71 -0.00 1.27620 -0.00008 0.00119 -0.00045 -0.00032 0.55873 0.03073 0.01229 -0.00903 0.00118 3.3838 -0.4673 -0.1091 0.1407 0.0116 288.1 -0.5 0.9 0.0 -0.3 0.90 -0.05 0.22 0.00 -0.35 -0.46 0.17 -0.07 0.09 -0.13 20.94 3.15 7.83 -2.39 -2.42 4.44 4.28 -0.65 -1.45 -0.09 + -22.5 7.5 101723 -258 -186 44 21 291.7 1.5 2.4 0.1 0.2 10.54 0.82 1.59 -0.07 -0.01 -8.1 -1.2 -0.2 0.3 -0.4 24.73 -0.00 1.27011 0.00420 0.00248 -0.00028 0.00085 0.52368 0.03067 0.00212 -0.01315 -0.00157 5.3035 -1.0572 -0.5046 0.4116 -0.1899 285.7 0.8 1.8 0.2 0.1 6.01 -4.45 -2.42 -0.26 -0.90 -2.59 -0.50 -0.28 -0.27 0.11 8.11 10.03 3.45 -0.75 3.76 3.85 5.72 2.57 -0.56 2.61 + -22.5 22.5 89798 -407 3 56 18 296.3 4.3 -0.4 -1.6 -0.4 8.00 3.74 1.85 0.26 0.09 -4.5 -2.7 -0.8 0.9 0.3 21.15 1055.42 1.26892 0.00595 0.00040 -0.00106 0.00093 0.52733 0.04909 0.00112 -0.01520 -0.00060 2.6589 -0.4999 0.0627 0.3142 0.0609 283.8 1.6 0.0 -0.3 0.0 3.52 -4.78 -1.80 0.14 -1.18 -0.95 -0.16 -0.27 0.07 -0.09 4.46 5.27 0.06 1.21 0.05 -1.01 0.95 -1.53 1.43 -0.13 + -22.5 37.5 101593 -511 -195 28 -13 297.8 2.3 1.6 0.1 0.0 14.14 2.61 0.91 -0.35 0.23 -9.1 -0.0 -0.6 -0.3 0.1 0.40 -0.00 1.26968 0.00487 0.00154 -0.00029 0.00106 0.54239 0.04667 0.01353 -0.00129 -0.00665 4.0991 -0.7727 -0.5970 -0.2232 0.2251 288.1 1.0 0.5 -0.3 0.4 -0.58 -3.92 -0.63 0.28 -1.46 -4.03 -0.34 -0.71 0.40 -0.12 6.83 7.70 3.58 4.30 1.67 -3.84 0.33 3.57 1.17 3.68 + -22.5 52.5 101732 -458 -330 8 -10 297.4 2.0 1.8 -0.1 -0.1 13.59 2.37 1.90 -0.18 0.14 -9.3 0.2 0.1 -0.0 0.0 -3.96 -0.00 1.26961 0.00398 0.00301 -0.00002 0.00100 0.56030 0.04757 0.03106 0.00166 -0.00048 3.4040 -0.4938 -0.4095 -0.0723 0.0239 286.6 0.7 0.7 -0.2 0.0 0.49 -3.46 -1.31 0.34 -1.86 1.76 0.34 0.25 -0.30 -0.07 7.43 3.58 5.12 1.57 1.90 -5.84 -3.11 1.16 -0.97 0.68 + -22.5 67.5 101825 -332 -309 -2 9 296.3 1.9 2.1 -0.1 0.0 12.61 2.10 1.88 -0.24 0.17 -9.4 0.2 0.0 -0.1 -0.0 -7.62 -0.00 1.26924 0.00305 0.00285 -0.00026 0.00068 0.54159 0.03459 0.02994 -0.00210 0.00141 3.6557 -0.3427 -0.3954 -0.0111 -0.0635 286.3 1.0 1.0 -0.1 0.0 -0.48 -4.23 -1.61 0.61 -1.92 -2.78 0.60 0.24 -0.20 0.05 6.98 -0.64 1.93 -0.82 0.37 -3.43 -2.51 -1.72 -0.87 -0.56 + -22.5 82.5 101841 -230 -268 -13 11 295.3 1.6 2.1 -0.0 -0.0 11.69 1.53 1.73 -0.20 0.12 -9.4 0.1 0.1 -0.1 0.0 -34.67 -0.00 1.26846 0.00285 0.00268 -0.00009 0.00088 0.53316 0.02181 0.02404 -0.00337 0.00162 3.7414 -0.1740 -0.3271 0.0095 -0.0549 285.7 1.2 1.2 0.0 0.0 -0.32 -4.97 -1.81 0.59 -1.88 -3.85 0.64 0.32 0.12 0.23 11.14 1.38 3.65 -0.40 1.27 -0.95 -0.71 -0.20 -0.76 0.16 + -22.5 97.5 101779 -198 -222 -44 0 294.3 1.0 1.6 -0.0 -0.1 10.89 0.88 1.56 -0.09 0.06 -9.5 -0.1 0.2 -0.0 0.0 -45.71 -0.00 1.26817 0.00328 0.00297 0.00045 0.00141 0.53232 0.01466 0.02252 -0.00091 0.00215 3.8040 -0.0811 -0.2601 -0.0288 -0.0579 285.2 1.1 1.2 0.1 0.0 7.04 -5.54 -2.87 0.12 -2.13 -1.54 -0.09 0.71 0.02 0.09 12.29 -2.30 1.78 0.29 -0.64 -0.25 -2.88 0.61 0.62 -0.33 + -22.5 112.5 101352 -465 -216 -35 -41 296.0 0.8 2.6 -0.3 -0.0 12.54 2.88 2.17 -0.68 0.30 -6.3 2.5 0.3 -0.7 0.1 -23.52 -0.00 1.27174 0.00612 0.00338 0.00036 0.00176 0.55257 0.02087 0.02827 0.00509 0.00067 4.6036 0.3582 -0.7320 -0.3614 -0.2461 287.2 2.5 1.4 -0.3 0.2 11.88 -5.48 -4.55 -0.23 -2.54 8.45 -0.22 0.21 -0.13 -0.27 13.04 0.63 2.82 0.92 -0.23 7.19 11.99 4.86 -0.93 3.46 + -22.5 127.5 96591 -556 -61 0 -44 299.5 7.5 0.7 -1.6 -0.2 6.31 2.51 1.35 0.53 0.48 -4.3 -1.9 -0.1 -0.4 -0.0 9.18 411.47 1.27282 0.00801 0.00203 -0.00044 0.00190 0.58656 0.03208 0.01389 -0.00360 -0.00424 2.0538 -0.3921 0.0039 0.2345 0.0491 284.5 3.7 0.4 -0.5 0.4 18.33 -7.21 -4.92 -0.07 -2.36 4.74 -0.03 -0.42 -0.37 -0.07 10.28 5.48 0.34 1.18 0.92 -0.66 -1.03 -3.57 0.02 -0.17 + -22.5 142.5 98918 -553 -102 -15 -33 299.1 6.4 0.7 -1.5 -0.4 7.49 2.91 1.73 0.63 0.71 -5.9 -1.7 -0.3 0.4 -0.1 39.16 209.13 1.27231 0.00732 0.00187 -0.00045 0.00156 0.57399 0.03328 0.00477 0.00467 -0.00307 2.3469 -0.3822 0.0547 0.1696 0.0859 285.4 3.3 0.8 -0.8 0.3 18.83 -6.96 -3.59 0.61 -1.95 0.38 0.19 -0.01 0.26 0.07 11.27 6.61 4.43 2.52 2.33 3.81 0.30 0.03 0.40 -0.15 + -22.5 157.5 101449 -365 -223 -40 -59 296.4 2.1 1.7 -0.0 0.0 12.30 2.60 1.64 -0.13 0.29 -9.5 0.1 0.0 -0.0 -0.0 50.23 -0.00 1.26995 0.00472 0.00307 0.00005 0.00154 0.55720 0.02204 0.02641 0.00759 0.00444 3.5003 -0.1530 -0.4269 -0.1784 -0.0719 285.7 1.9 0.7 -0.4 0.1 18.23 -7.55 -3.27 0.71 -1.34 -0.72 -0.18 0.49 0.13 -0.45 11.58 1.95 9.97 -0.92 3.01 0.64 -2.54 2.85 -1.46 0.49 + -22.5 172.5 101362 -290 -248 -23 -59 296.5 1.8 2.1 -0.1 0.1 12.96 2.08 2.12 -0.32 0.35 -9.3 0.1 0.0 -0.1 0.0 55.17 -0.00 1.26993 0.00393 0.00384 0.00007 0.00116 0.57863 0.01338 0.02553 0.00084 0.00264 3.1768 -0.0669 -0.3250 -0.0331 -0.0573 285.2 1.7 1.3 -0.2 0.2 25.13 -7.53 -4.02 0.51 -0.78 0.55 -0.18 0.48 0.02 -0.08 22.01 1.29 2.83 -0.63 -3.34 3.46 -0.82 -0.75 0.21 -0.96 + -22.5 -172.5 101377 -256 -216 -33 -45 296.6 1.6 2.0 -0.2 0.1 13.34 1.81 1.94 -0.33 0.38 -9.2 0.1 -0.0 -0.1 0.0 31.06 -0.00 1.27032 0.00320 0.00372 0.00010 0.00073 0.59546 0.01144 0.01746 -0.00345 0.00109 2.9416 -0.0840 -0.1857 0.0162 -0.0609 285.0 1.4 1.4 0.0 0.2 12.35 -6.79 -4.59 0.88 -0.95 -0.91 -0.26 -0.25 -0.26 -0.05 20.76 5.76 -0.13 0.41 -4.63 1.95 0.29 -1.01 0.87 -0.74 + -22.5 -157.5 101445 -203 -186 -52 1 296.8 1.4 1.9 -0.1 0.2 13.56 1.75 1.71 -0.15 0.26 -9.2 0.1 -0.1 0.0 0.0 6.21 -0.00 1.26958 0.00243 0.00320 0.00003 0.00050 0.59793 0.01715 0.01452 -0.00173 -0.00234 2.9085 -0.1764 -0.1705 -0.0024 0.0132 285.0 1.0 1.2 0.0 0.2 13.82 -6.76 -4.49 1.23 -1.06 -3.33 0.09 -0.04 0.07 0.39 14.81 6.67 -1.09 -0.16 -5.19 -1.08 -0.00 0.04 0.08 -1.46 + -22.5 -142.5 101540 -124 -142 -34 37 297.1 1.2 1.7 -0.1 0.0 13.78 1.53 1.53 -0.02 -0.01 -9.2 0.2 -0.1 0.0 -0.1 -8.34 -0.00 1.26804 0.00134 0.00279 -0.00010 0.00014 0.59180 0.02203 0.01188 0.00026 -0.00452 2.9653 -0.2302 -0.1368 -0.0220 0.0544 285.4 0.4 1.2 0.0 0.2 10.44 -6.75 -4.75 0.69 -1.14 -0.55 -0.07 0.11 0.20 -0.34 7.30 6.90 -2.27 0.29 -2.51 -1.78 0.21 -2.17 0.33 -1.72 + -22.5 -127.5 101704 -54 -123 2 41 296.8 1.1 1.6 -0.1 0.0 13.23 1.17 1.29 -0.16 0.03 -9.3 0.1 -0.1 -0.0 -0.0 -10.18 -0.00 1.26749 0.00041 0.00228 -0.00048 -0.00016 0.57341 0.01654 0.00760 0.00148 -0.00693 3.1746 -0.1869 -0.0880 -0.0223 0.0768 285.6 0.4 1.2 -0.2 0.2 7.80 -6.60 -4.39 0.27 -0.99 1.34 0.04 -0.64 -0.03 0.20 -1.66 2.28 0.64 -0.45 -1.51 -5.03 -1.66 -0.75 -0.78 0.56 + -22.5 -112.5 101878 -27 -147 9 29 295.8 1.0 1.6 -0.2 0.2 12.04 0.85 1.26 -0.19 0.11 -9.4 0.0 -0.0 -0.0 -0.0 -3.07 -0.00 1.26570 0.00035 0.00186 -0.00066 0.00045 0.54551 0.00808 0.01022 0.00057 -0.00893 3.5311 -0.0957 -0.1422 -0.0075 0.1274 285.6 0.7 1.1 -0.2 0.5 8.04 -6.28 -3.40 0.31 -1.04 0.06 0.29 -0.54 -0.09 0.50 -6.32 -0.73 -0.18 -0.29 -1.02 -4.56 -1.93 -0.71 -0.21 0.02 + -22.5 -97.5 101996 -66 -199 -10 4 293.7 0.8 2.0 -0.0 0.2 10.48 0.69 1.37 -0.01 0.13 -9.5 0.0 0.0 0.0 -0.0 -1.55 -0.00 1.26628 0.00175 0.00245 -0.00026 0.00101 0.52397 0.00246 0.00757 -0.00078 -0.00641 3.9543 0.0472 -0.1417 0.0114 0.0997 284.8 0.8 1.5 0.0 0.5 5.13 -5.36 -2.30 0.32 -1.21 0.31 0.67 0.25 0.45 -0.27 -6.34 0.78 0.13 -1.36 1.55 -4.58 -0.63 -0.93 0.13 0.37 + -22.5 -82.5 101872 -149 -197 -0 -18 291.1 0.9 2.2 0.0 0.3 9.11 0.62 1.19 0.07 0.14 -9.6 0.0 0.0 0.1 -0.0 6.73 0.00 1.26657 0.00387 0.00322 -0.00000 0.00097 0.50784 0.00254 0.00961 -0.00017 -0.00218 4.7447 0.0527 -0.3215 -0.0022 0.0598 284.0 1.0 1.5 0.1 0.3 7.28 -5.09 -2.32 -0.47 -1.08 0.73 0.34 0.52 -0.09 -0.38 -1.96 3.25 2.06 -0.37 1.51 -2.76 0.48 -0.24 0.64 0.58 + -22.5 -67.5 58419 39 53 -23 13 274.8 2.6 0.7 -0.4 -0.1 2.39 1.58 1.08 0.14 0.62 -6.9 0.1 0.3 0.2 -0.0 43.26 4628.35 1.26155 0.00676 0.00291 -0.00043 0.00067 0.41751 -0.01935 -0.01591 0.00355 -0.00512 4.2696 0.4113 0.3054 -0.0286 -0.1619 263.5 3.7 2.0 -0.3 0.4 12.81 -6.11 -2.87 -0.06 -1.01 2.09 0.30 0.00 -0.44 0.01 3.38 3.44 1.18 0.35 0.91 4.01 4.81 1.34 0.48 0.85 + -22.5 -52.5 97412 -367 -2 51 40 297.6 2.4 -0.0 -1.1 0.1 12.46 3.11 1.31 -0.16 -0.03 -4.5 -3.1 -0.1 1.0 0.5 -2.16 338.68 1.26927 0.00423 0.00110 -0.00109 0.00038 0.56669 0.02860 0.00158 -0.00339 0.00473 2.7343 -0.2611 0.0690 0.0695 -0.0302 286.4 1.0 0.2 -0.5 0.0 7.05 -4.14 -2.10 0.39 -1.44 -2.07 -1.05 -0.71 0.44 0.30 9.95 9.55 -1.66 1.07 -1.36 -0.48 3.70 0.46 1.64 0.64 + -22.5 -37.5 101684 -352 -176 48 92 297.4 1.4 1.9 -0.1 0.1 13.61 1.97 1.33 -0.20 0.07 -9.2 0.3 -0.2 -0.0 -0.1 -9.16 -0.00 1.26763 0.00385 0.00263 -0.00046 0.00012 0.57539 0.03953 0.00619 -0.00522 -0.00749 3.1725 -0.3986 -0.0985 0.0661 0.0945 286.1 0.3 1.3 0.0 0.4 8.94 -4.11 -2.39 0.25 -2.21 2.49 1.46 0.33 0.51 0.31 3.33 -0.85 2.20 -4.43 -4.78 -6.04 -4.14 3.43 -2.44 1.43 + -22.5 -22.5 101933 -220 -227 62 89 296.2 1.5 2.2 -0.1 0.0 12.25 1.43 1.70 -0.31 -0.14 -9.4 0.1 -0.0 -0.1 -0.1 -1.54 -0.00 1.26697 0.00337 0.00330 -0.00040 0.00016 0.54602 0.02079 0.00718 -0.01184 -0.01438 3.5521 -0.1961 -0.1236 0.1269 0.1716 286.0 1.0 1.8 0.3 0.5 4.45 -5.87 -2.92 -0.42 -1.81 3.14 0.37 0.05 -0.01 -0.55 -7.41 -6.64 -0.06 -0.01 -0.18 -5.93 -2.88 -0.83 1.02 0.60 + -22.5 -7.5 102015 -212 -211 43 37 294.1 1.2 2.2 -0.0 0.2 10.71 1.22 1.55 -0.19 0.08 -9.5 0.1 0.0 -0.0 -0.0 13.39 -0.00 1.26761 0.00381 0.00316 -0.00020 0.00045 0.52199 0.01523 0.00020 -0.00839 -0.01059 4.0597 -0.0800 -0.0622 0.0623 0.0729 285.4 1.2 2.0 0.1 0.4 5.70 -5.61 -3.04 -0.26 -0.65 0.22 0.13 -0.47 -0.07 0.04 -3.71 0.01 -0.72 0.36 0.33 -3.83 -0.35 -0.40 0.50 0.72 + -37.5 7.5 101956 -22 -95 -47 21 286.7 1.4 2.1 0.2 0.1 7.35 0.72 1.05 0.08 0.00 -8.9 0.2 0.1 -0.0 -0.1 22.83 0.00 1.24498 0.00865 0.00626 -0.00037 0.00074 0.55821 0.01400 0.00498 -0.00190 -0.00261 3.4038 -0.0779 -0.0684 0.0308 -0.0269 276.7 1.9 2.2 0.2 0.2 37.59 -3.80 -2.47 1.13 1.45 2.20 -2.11 -0.17 -0.32 -0.09 0.55 -0.28 0.30 0.35 -0.85 -1.98 -1.06 -0.57 0.11 0.02 + -37.5 22.5 101680 -164 -85 -15 20 290.5 1.6 1.9 -0.0 0.1 8.90 0.86 1.25 -0.12 0.08 -9.3 -0.3 0.2 -0.0 -0.1 29.58 0.00 1.24708 0.00814 0.00563 -0.00089 0.00117 0.54054 0.00982 0.00663 -0.00100 -0.00437 3.7604 -0.1856 -0.0455 0.0073 0.0367 279.9 1.9 2.0 -0.1 0.4 35.06 -3.34 -4.28 1.04 0.92 2.36 -1.81 -0.29 -0.39 0.68 0.89 1.48 -0.01 -1.61 -0.86 2.04 0.25 1.11 -0.86 -0.20 + -37.5 37.5 101824 -251 -69 -33 37 290.0 1.2 1.9 -0.0 0.3 9.03 0.93 1.20 -0.03 0.30 -8.8 0.3 0.1 0.1 0.1 30.61 0.00 1.24916 0.00802 0.00510 -0.00100 0.00192 0.55169 0.01875 0.00467 -0.00424 -0.00101 3.4387 -0.2306 -0.0367 0.0586 0.0172 279.7 1.4 2.0 0.1 0.6 29.76 -2.10 -5.70 1.63 -0.55 3.41 -0.69 -0.31 -0.02 1.05 4.31 3.95 -0.49 -0.84 1.25 -0.12 0.27 -0.36 0.10 0.81 + -37.5 52.5 102011 -260 -75 -40 74 288.7 1.5 1.9 -0.1 0.4 8.54 1.03 1.08 -0.13 0.35 -8.5 0.4 0.0 -0.0 0.2 36.90 0.00 1.24894 0.00835 0.00521 -0.00125 0.00228 0.55351 0.01978 0.00413 -0.00647 -0.00087 3.3900 -0.1788 -0.0125 0.0770 0.0260 278.8 1.7 2.0 0.0 0.7 30.91 -3.42 -6.02 1.99 -1.45 -3.72 1.79 0.27 -0.82 0.66 3.60 2.77 0.86 -0.61 -0.20 -0.68 0.54 -0.70 0.35 0.10 + -37.5 67.5 102078 -109 -82 -87 130 287.9 1.9 2.1 -0.1 0.4 8.09 1.22 1.06 -0.04 0.27 -8.5 0.5 -0.1 0.0 -0.0 22.77 0.00 1.24657 0.00917 0.00542 -0.00146 0.00201 0.55285 0.02359 0.00718 -0.00448 -0.00307 3.4010 -0.2099 -0.0740 0.0481 0.0754 278.0 2.0 2.0 0.0 0.6 31.22 -5.81 -5.88 1.89 -1.95 -4.65 3.67 -0.06 -0.26 0.16 2.05 3.49 2.38 -0.11 0.40 -1.14 -0.06 0.45 0.65 -0.23 + -37.5 82.5 102021 98 -78 -109 126 286.9 2.0 2.1 0.1 0.2 7.60 1.15 1.00 0.11 0.06 -8.6 0.4 -0.1 0.1 -0.2 4.78 0.00 1.24408 0.00949 0.00513 -0.00101 0.00151 0.54999 0.02365 0.00921 -0.00201 -0.00373 3.4238 -0.2078 -0.1237 0.0139 0.0672 277.1 2.2 1.9 0.1 0.4 31.43 -7.45 -4.88 1.15 -1.19 -8.20 2.05 0.08 0.66 -1.42 1.24 2.85 2.42 0.57 0.64 -1.34 -0.40 0.25 -0.04 0.16 + -37.5 97.5 101903 178 -21 -88 37 286.1 1.6 2.0 0.2 0.1 7.16 0.80 0.89 0.13 0.01 -8.8 0.2 0.0 0.1 -0.1 -21.84 0.00 1.24169 0.00911 0.00531 -0.00061 0.00124 0.54571 0.02126 0.00965 -0.00168 -0.00437 3.4969 -0.1697 -0.1414 0.0017 0.0492 276.4 1.9 2.0 0.3 0.3 31.11 -6.65 -3.86 0.96 1.25 -7.48 -0.90 1.93 0.61 -1.39 -0.79 -0.49 1.21 -0.44 -0.18 -1.30 -0.93 -0.62 -0.59 -0.53 + -37.5 112.5 101729 60 63 -56 -30 286.8 1.0 1.9 0.1 0.0 7.23 0.55 0.91 0.00 -0.04 -9.1 0.1 0.2 -0.1 -0.1 -36.65 0.00 1.24191 0.00908 0.00594 -0.00054 0.00124 0.54000 0.01878 0.00985 -0.00142 -0.00417 3.5953 -0.1255 -0.0683 0.0117 0.0187 276.9 1.7 2.1 0.2 0.2 29.51 -5.13 -3.83 0.45 3.20 -0.92 -1.48 1.42 -0.07 -0.35 0.01 -1.13 -0.26 -0.60 -0.50 0.64 -0.19 -0.09 -0.65 0.16 + -37.5 127.5 101732 -76 137 -61 -43 287.1 1.0 2.0 0.0 0.1 7.54 0.68 1.03 -0.10 -0.01 -8.2 0.3 -0.0 -0.4 -0.3 -30.09 0.00 1.24293 0.01007 0.00590 -0.00078 0.00167 0.54742 0.02319 0.01171 -0.00120 -0.00246 3.6361 -0.2054 -0.1187 -0.0097 -0.0041 277.4 2.0 2.1 0.0 0.3 29.72 -2.16 -4.56 0.85 3.02 4.70 -0.33 0.20 -0.57 0.38 3.29 3.36 1.35 0.42 1.60 -0.15 0.03 -0.21 0.16 -0.20 + -37.5 142.5 98971 -298 129 -69 -71 287.2 4.6 2.0 -0.2 0.3 6.50 0.65 0.58 0.01 0.04 -6.4 -0.5 0.4 0.2 -0.1 1.50 230.34 1.24466 0.01259 0.00574 -0.00094 0.00195 0.53902 0.02663 0.00779 0.00152 -0.00092 3.3889 -0.3557 -0.0868 -0.0314 0.0146 277.2 3.5 2.2 -0.1 0.4 31.41 -1.32 -5.22 0.66 2.13 5.10 -0.47 -1.15 0.15 -0.22 1.16 3.38 -0.08 0.57 0.33 2.41 2.52 0.13 0.01 -0.01 + -37.5 157.5 101647 -118 102 -81 -32 289.6 2.1 2.1 -0.0 0.3 8.49 1.51 1.04 -0.03 0.18 -9.1 0.4 -0.3 -0.1 -0.1 10.31 0.00 1.24432 0.01125 0.00491 -0.00088 0.00167 0.54209 0.02217 0.00560 -0.00010 -0.00126 3.5474 -0.1520 -0.0927 -0.0201 0.0244 278.7 2.6 2.0 0.0 0.3 28.16 -0.67 -4.21 0.28 0.70 -0.88 1.77 -0.54 -0.31 0.48 4.17 1.71 0.74 0.26 0.01 -0.04 -1.64 -0.05 -0.09 -0.33 + -37.5 172.5 101582 -1 64 -121 -7 288.7 1.9 2.2 -0.1 0.3 8.22 1.28 1.06 -0.00 0.27 -8.8 0.5 -0.1 0.1 0.0 26.93 0.00 1.24412 0.01036 0.00546 -0.00096 0.00179 0.54620 0.02160 0.00583 0.00325 -0.00090 3.4755 -0.1379 -0.0520 -0.0805 0.0394 278.0 2.4 2.2 -0.2 0.4 28.92 -1.24 -3.33 0.60 -0.97 2.38 -0.02 0.71 -0.27 0.49 3.28 0.17 0.54 1.02 -0.31 1.08 1.43 -0.18 1.63 0.90 + -37.5 -172.5 101578 56 46 -147 29 288.3 2.3 2.3 0.0 0.3 8.21 1.37 1.09 0.08 0.19 -8.8 0.3 -0.2 0.0 -0.1 15.04 0.00 1.24391 0.01028 0.00580 -0.00076 0.00166 0.54858 0.01937 0.00822 0.00343 0.00079 3.4419 -0.1017 -0.0887 -0.0664 -0.0156 277.6 2.6 2.2 -0.1 0.3 28.54 -3.85 -2.37 0.37 -1.22 -2.65 0.88 -0.62 -0.43 -0.10 5.01 0.55 2.13 0.41 1.11 0.74 0.21 -0.13 -0.02 0.50 + -37.5 -157.5 101628 137 -2 -159 11 288.0 2.2 2.5 0.1 0.3 8.22 1.36 1.23 0.10 0.23 -8.7 0.4 -0.1 0.1 -0.1 1.79 0.00 1.24395 0.00999 0.00629 -0.00083 0.00152 0.55396 0.01890 0.00984 0.00050 0.00312 3.3608 -0.1246 -0.1030 -0.0552 -0.0587 277.3 2.6 2.3 0.0 0.2 24.59 -4.75 -2.36 0.17 -0.38 -1.40 1.00 0.39 0.15 -0.42 5.27 -0.08 2.49 -0.35 1.41 0.64 -0.22 0.44 0.12 -0.08 + -37.5 -142.5 101720 226 -15 -128 -28 287.7 2.0 2.5 0.1 0.3 8.10 1.20 1.29 0.13 0.20 -8.7 0.4 -0.0 0.2 -0.0 -9.70 0.00 1.24351 0.00953 0.00666 -0.00055 0.00136 0.56135 0.01778 0.00955 0.00072 0.00203 3.2672 -0.1031 -0.0957 -0.0260 -0.0387 277.0 2.4 2.4 0.0 0.2 26.79 -5.13 -2.08 0.15 0.54 0.57 0.13 0.16 1.08 -0.64 8.26 1.22 2.77 1.29 1.41 0.25 -0.34 0.11 -0.24 -0.37 + -37.5 -127.5 101814 242 -12 -72 -60 287.6 1.9 2.4 0.2 0.3 8.10 1.25 1.28 0.18 0.22 -8.5 0.5 0.1 0.1 -0.0 -11.49 0.00 1.24363 0.00917 0.00680 -0.00050 0.00104 0.56567 0.01879 0.01301 -0.00219 0.00231 3.2032 -0.1394 -0.1394 0.0150 -0.0522 276.9 2.4 2.3 0.2 0.2 28.69 -6.07 -2.14 -0.67 1.83 1.89 -0.23 -0.17 0.77 -0.32 9.73 0.93 3.83 -0.66 1.22 0.57 -0.30 0.95 -0.46 0.29 + -37.5 -112.5 101918 276 -19 -57 -56 287.6 2.1 2.7 0.2 0.5 8.12 1.27 1.33 0.17 0.32 -8.5 0.4 -0.1 0.1 -0.1 -10.11 0.00 1.24346 0.00887 0.00675 -0.00024 0.00129 0.56856 0.01471 0.00939 -0.00131 0.00179 3.1403 -0.0967 -0.0852 0.0078 -0.0395 276.8 2.5 2.4 0.2 0.4 30.37 -5.74 -2.78 -0.32 2.38 1.71 -0.23 -0.63 0.20 0.95 8.72 -1.02 1.61 -0.18 -1.04 0.16 -1.18 -1.11 0.34 -0.30 + -37.5 -97.5 102018 260 -55 -65 -2 287.4 2.0 2.6 0.2 0.6 7.97 1.14 1.26 0.18 0.31 -8.6 0.2 -0.1 0.1 -0.1 -4.29 0.00 1.24315 0.00854 0.00673 0.00011 0.00165 0.56556 0.00804 0.00542 -0.00185 -0.00212 3.1957 -0.0060 -0.0646 0.0199 0.0423 276.7 2.5 2.5 0.3 0.6 31.05 -5.06 -4.13 -0.57 0.83 1.26 -1.40 -0.05 0.19 0.56 5.03 -1.65 -0.78 0.29 -0.75 -1.52 -0.71 0.21 0.25 -0.23 + -37.5 -82.5 101996 152 -86 -70 1 286.8 1.5 2.5 0.0 0.4 7.50 0.80 1.12 0.03 0.24 -8.9 -0.0 -0.1 0.1 0.1 4.27 0.00 1.24260 0.00887 0.00706 0.00019 0.00184 0.55463 0.00294 0.00329 0.00090 -0.00176 3.4200 0.0597 -0.0446 -0.0120 0.0459 276.6 2.4 2.6 0.1 0.6 29.64 -4.84 -4.03 1.24 -1.06 1.08 -1.84 -0.84 -0.02 -0.24 0.70 -2.81 -1.61 1.13 -1.33 -2.59 -1.32 -0.94 0.14 -0.39 + -37.5 -67.5 96973 -301 -66 -65 19 290.3 6.8 1.9 0.1 0.5 5.39 1.39 1.12 -0.12 0.35 -5.8 -2.1 0.4 0.2 0.0 19.01 383.48 1.24627 0.01213 0.00659 -0.00025 0.00205 0.55952 0.00136 -0.00227 0.00538 -0.00149 2.4683 -0.1911 0.1217 -0.0295 -0.0184 277.6 5.1 2.7 -0.2 0.6 29.56 -3.86 -2.76 1.34 -1.56 -2.53 -0.84 -0.29 -0.85 0.79 14.49 13.38 2.24 -0.14 1.38 5.47 3.22 -0.14 -0.08 0.13 + -37.5 -52.5 101572 -239 -98 -53 36 289.3 3.0 2.7 -0.1 0.4 8.63 1.86 1.27 -0.03 0.43 -8.3 0.6 -0.7 -0.0 0.1 2.63 0.00 1.24603 0.01011 0.00570 -0.00100 0.00215 0.56017 0.00532 0.00081 -0.00038 0.00092 3.6546 0.1163 -0.0517 0.0084 -0.1097 278.6 3.4 2.4 -0.2 0.6 33.32 -5.23 -1.89 1.36 -1.73 -7.16 0.04 -0.82 -0.42 1.11 7.86 0.61 1.35 -0.36 0.50 1.99 -2.21 0.20 0.32 -0.41 + -37.5 -37.5 101647 -110 -128 -61 118 288.3 1.9 2.4 -0.0 0.4 8.25 1.25 1.19 -0.01 0.36 -8.7 0.4 -0.1 0.0 0.2 -5.08 0.00 1.24517 0.00938 0.00569 -0.00104 0.00239 0.56265 0.00962 0.00215 -0.00101 0.00189 3.3207 0.0141 -0.0270 -0.0127 -0.0207 277.6 2.5 2.3 -0.1 0.6 33.20 -6.11 -1.59 1.70 -0.58 2.16 1.51 0.36 0.44 0.68 8.01 1.21 0.29 -0.22 1.55 0.73 0.67 0.48 0.17 0.32 + -37.5 -22.5 101768 -4 -131 -46 143 287.5 1.9 2.4 0.1 0.3 8.04 1.23 1.18 0.05 0.20 -8.3 0.6 0.0 0.1 0.0 17.38 0.00 1.24415 0.00974 0.00632 -0.00048 0.00170 0.56774 0.01490 0.00565 0.00031 0.00119 3.1861 -0.0824 -0.0202 -0.0137 -0.0078 276.9 2.4 2.3 0.1 0.4 36.43 -5.48 -0.97 2.02 -0.43 4.24 1.65 0.52 0.86 -1.10 8.75 4.03 1.44 -0.43 1.40 0.22 0.54 0.38 -0.34 -0.93 + -37.5 -7.5 101915 50 -124 -30 84 286.9 1.8 2.3 0.1 0.1 7.75 1.07 1.14 0.08 0.07 -8.1 0.5 0.1 0.0 -0.1 20.27 0.00 1.24445 0.00949 0.00658 -0.00022 0.00096 0.56795 0.01501 0.00535 -0.00024 -0.00375 3.2199 -0.0599 -0.0339 0.0085 0.0477 276.6 2.3 2.3 0.2 0.3 36.34 -4.64 -1.30 1.74 0.33 0.22 -0.99 0.21 -0.07 -1.78 5.44 1.02 0.56 -0.49 -1.00 -1.34 -0.72 -0.76 -0.33 -0.41 + -52.5 7.5 99757 -6 -149 -54 -103 273.5 1.2 1.6 0.1 -0.3 3.30 0.36 0.41 0.02 -0.03 -7.5 0.7 0.3 0.1 -0.2 26.86 0.00 1.20464 0.01282 0.00906 0.00060 -0.00134 0.54913 0.01607 0.00716 -0.00136 -0.00188 2.8398 -0.3092 -0.1342 0.0366 0.0522 263.9 1.6 1.4 0.1 -0.4 57.66 -2.65 -2.11 -2.10 1.76 0.11 -3.84 3.06 0.14 -1.11 5.49 0.67 0.76 -0.43 -0.18 -0.02 -0.41 0.14 -0.10 0.04 + -52.5 22.5 99784 -74 -17 -64 -111 273.4 0.9 1.6 0.1 -0.0 3.28 0.30 0.42 0.04 0.02 -7.1 0.4 0.4 0.1 -0.1 37.05 0.00 1.20590 0.01108 0.00975 0.00042 -0.00112 0.55256 0.01227 0.00986 -0.00232 -0.00243 2.7561 -0.2308 -0.1505 0.0283 0.0388 263.8 1.2 1.6 0.2 -0.2 62.77 -3.47 -3.10 -2.81 2.56 4.13 -2.50 3.29 -0.34 1.56 5.93 0.32 0.79 -0.30 0.10 0.16 -0.32 0.29 -0.26 0.34 + -52.5 37.5 99804 -85 95 -74 -27 274.4 0.9 1.5 0.1 0.2 3.48 0.30 0.43 0.03 0.07 -7.6 0.2 0.4 -0.0 0.0 44.84 0.00 1.20763 0.01046 0.01024 -0.00007 -0.00011 0.55192 0.01209 0.01401 -0.00249 -0.00002 2.8213 -0.1977 -0.2127 0.0202 0.0094 264.5 1.0 1.7 0.1 0.1 65.90 -3.97 -3.54 -3.87 3.02 6.67 0.75 2.01 -0.86 3.68 6.08 0.87 1.00 -0.55 0.09 0.69 0.23 0.16 -0.06 0.55 + -52.5 52.5 99814 2 158 -97 105 274.6 1.0 1.5 -0.0 0.2 3.52 0.33 0.45 0.02 0.09 -7.7 0.2 0.3 0.0 0.1 43.75 0.00 1.20822 0.01119 0.01007 -0.00067 0.00102 0.55387 0.01432 0.01447 -0.00310 0.00218 2.8021 -0.2167 -0.2290 0.0042 -0.0125 264.6 1.3 1.8 0.0 0.4 66.63 -3.71 -3.38 -4.00 2.90 -1.69 4.02 0.44 -0.76 2.85 6.11 0.80 1.33 -0.70 0.48 0.04 0.38 -0.06 -0.01 0.32 + -52.5 67.5 99704 194 188 -109 159 274.9 1.2 1.5 -0.1 0.2 3.58 0.40 0.45 0.01 0.06 -7.8 0.4 0.3 0.0 0.2 35.46 0.00 1.20818 0.01230 0.00916 -0.00096 0.00134 0.55197 0.01743 0.01538 -0.00226 0.00425 2.8722 -0.2454 -0.2285 -0.0066 -0.0459 264.7 1.6 1.7 0.0 0.4 66.13 -1.87 -3.11 -4.15 2.67 -3.81 4.90 -1.07 0.25 -0.29 6.09 1.24 1.49 -0.50 0.78 -0.24 0.36 -0.18 -0.04 -0.08 + -52.5 82.5 99548 348 173 -86 102 274.2 1.4 1.4 -0.1 0.1 3.47 0.44 0.41 0.00 0.05 -7.7 0.5 0.3 0.0 0.0 24.20 0.00 1.20762 0.01305 0.00792 -0.00107 0.00074 0.55009 0.01829 0.01389 -0.00200 0.00352 2.8892 -0.2586 -0.1995 -0.0089 -0.0512 264.2 1.8 1.5 0.1 0.2 63.67 -0.61 -2.34 -4.01 2.78 -8.99 1.72 -1.58 0.72 -2.95 5.78 1.27 1.35 -0.33 0.53 -0.32 -0.04 -0.25 0.24 -0.05 + -52.5 97.5 99384 375 169 -51 -25 275.3 1.3 1.3 0.1 -0.1 3.72 0.43 0.42 0.06 -0.02 -8.0 0.3 0.3 0.0 -0.1 3.72 0.00 1.20772 0.01222 0.00689 -0.00096 -0.00057 0.54325 0.01587 0.01360 0.00034 0.00137 3.0010 -0.2088 -0.1914 -0.0322 -0.0140 264.9 1.6 1.3 0.2 -0.2 58.44 -0.86 -1.71 -3.64 2.77 -3.67 -2.85 -0.15 1.28 -4.29 5.38 0.52 1.19 -0.32 0.48 0.02 -0.69 0.05 -0.10 -0.28 + -52.5 112.5 99499 246 204 12 -166 275.5 0.9 1.5 0.2 -0.1 3.84 0.32 0.47 0.07 -0.01 -7.4 0.1 0.3 0.1 -0.3 -15.35 0.00 1.20953 0.01092 0.00673 -0.00103 -0.00150 0.54404 0.01414 0.01523 -0.00003 -0.00064 2.9722 -0.1567 -0.2216 -0.0124 0.0092 265.5 1.1 1.4 0.3 -0.3 55.93 -1.51 -0.39 -3.70 1.67 3.56 -4.64 1.56 0.49 -2.19 4.67 0.19 1.01 -0.22 0.07 0.73 -0.26 0.49 -0.08 -0.24 + -52.5 127.5 99748 84 295 33 -193 277.6 0.7 1.3 0.1 0.0 4.39 0.27 0.47 0.06 0.01 -8.0 -0.0 0.3 0.1 -0.1 -23.03 0.00 1.21355 0.00991 0.00648 -0.00143 -0.00160 0.54427 0.01307 0.01605 -0.00136 -0.00290 3.0584 -0.1489 -0.2245 0.0096 0.0183 267.2 0.7 1.4 0.2 -0.2 55.51 -0.77 0.77 -4.63 1.25 11.52 -3.08 1.04 -0.72 0.91 5.14 0.57 0.82 -0.74 0.23 1.41 0.08 -0.01 0.14 0.19 + -52.5 142.5 100110 -58 321 17 -127 278.3 0.9 1.3 0.1 0.2 4.67 0.35 0.46 0.06 0.08 -7.3 -0.0 0.2 0.1 -0.2 -18.50 0.00 1.21662 0.01004 0.00612 -0.00163 -0.00099 0.54728 0.01521 0.01581 -0.00199 -0.00251 3.0725 -0.1596 -0.1944 0.0107 0.0415 268.3 0.7 1.4 0.2 0.1 54.24 -0.28 1.35 -4.32 0.42 10.35 -0.72 -0.60 -0.15 2.75 4.92 0.75 0.50 -0.72 0.16 0.43 0.55 -0.22 0.14 0.10 + -52.5 157.5 100352 -109 269 34 -13 279.6 1.1 1.2 -0.0 0.3 4.97 0.44 0.47 0.03 0.14 -7.9 0.2 0.3 0.1 0.1 -18.96 0.00 1.21814 0.01091 0.00568 -0.00186 0.00005 0.54452 0.01877 0.01459 -0.00283 0.00133 3.1872 -0.2634 -0.1938 0.0756 -0.0096 269.3 1.0 1.4 0.1 0.4 53.46 0.45 0.47 -3.23 -0.37 5.93 1.58 -1.85 -0.06 3.66 4.12 1.28 -0.13 -0.15 0.24 -0.00 0.70 -0.33 -0.21 0.46 + -52.5 172.5 100435 -69 204 21 121 280.1 1.3 1.1 -0.1 0.4 5.11 0.53 0.47 -0.01 0.14 -7.8 0.4 0.3 -0.0 0.0 -19.23 0.00 1.21938 0.01182 0.00527 -0.00181 0.00058 0.54268 0.02048 0.01190 -0.00323 0.00098 3.2221 -0.2973 -0.1705 0.0834 0.0116 269.8 1.3 1.4 0.0 0.5 56.40 1.09 0.64 -3.39 -0.77 -2.45 2.05 -0.97 -0.44 1.82 4.02 2.26 -0.07 0.19 0.57 -0.11 0.65 -0.04 0.30 0.36 + -52.5 -172.5 100407 -5 155 10 191 280.4 1.5 1.4 -0.1 0.3 5.19 0.63 0.54 0.00 0.11 -8.1 0.4 0.1 0.1 0.1 -26.53 0.00 1.21879 0.01255 0.00536 -0.00149 0.00023 0.54087 0.02247 0.01075 -0.00170 0.00170 3.2480 -0.3247 -0.1189 0.0288 0.0132 269.9 1.5 1.5 0.0 0.4 51.51 2.15 0.86 -2.93 -0.92 0.12 1.72 -0.98 0.87 -0.47 4.40 1.19 -0.29 0.31 0.40 0.25 0.08 -0.24 0.34 -0.12 + -52.5 -157.5 100390 36 105 40 152 279.8 1.6 1.4 -0.1 0.1 5.07 0.61 0.54 -0.03 0.03 -7.8 0.4 0.2 -0.0 -0.2 -21.84 0.00 1.21833 0.01257 0.00567 -0.00102 -0.00056 0.54168 0.02248 0.01124 -0.00085 0.00007 3.1977 -0.3114 -0.1417 0.0500 0.0410 269.6 1.6 1.4 0.0 0.1 48.17 3.32 1.04 -3.81 -0.51 -1.17 -0.46 -0.37 0.89 -2.48 4.85 1.63 0.41 -0.40 0.04 -0.12 0.22 0.31 -0.51 -0.39 + -52.5 -142.5 100363 17 63 64 44 279.2 1.5 1.2 0.1 -0.0 4.92 0.59 0.47 0.04 -0.02 -8.0 0.4 0.3 0.0 -0.3 -18.05 0.00 1.21745 0.01182 0.00581 -0.00001 -0.00140 0.54337 0.02054 0.01297 0.00082 -0.00224 3.1539 -0.3197 -0.1595 0.0017 0.0580 269.1 1.5 1.2 0.2 0.0 47.32 5.44 0.83 -4.16 -0.11 -2.17 -1.59 -1.26 1.52 -2.84 5.11 1.67 0.63 0.44 -0.11 -0.47 -0.31 0.25 0.49 -0.00 + -52.5 -127.5 100299 -22 24 101 -65 279.0 1.3 1.1 0.2 -0.1 4.82 0.52 0.42 0.09 -0.03 -8.0 0.3 0.3 0.1 -0.2 -14.70 0.00 1.21657 0.01084 0.00607 0.00067 -0.00189 0.54343 0.02120 0.01340 0.00182 -0.00290 3.1738 -0.3208 -0.1863 -0.0055 0.0530 268.8 1.1 1.1 0.3 -0.1 48.61 7.50 0.05 -3.91 -0.41 -0.18 -2.10 -0.28 0.88 -1.45 4.49 2.09 1.07 0.09 0.35 -0.55 -0.57 0.21 -0.36 0.11 + -52.5 -112.5 100232 -88 1 111 -93 278.9 1.2 1.2 0.2 0.1 4.75 0.48 0.43 0.07 0.02 -8.1 0.2 0.2 0.1 -0.1 -10.98 0.00 1.21543 0.01012 0.00659 0.00110 -0.00149 0.54269 0.01835 0.01222 0.00114 -0.00106 3.1786 -0.2863 -0.1651 0.0224 0.0054 268.5 1.0 1.1 0.3 -0.1 49.65 9.20 -0.18 -4.01 -0.10 -2.64 -1.70 0.78 -0.20 1.45 4.49 2.05 1.05 -0.10 0.46 -0.17 -0.34 -0.24 -0.06 0.48 + -52.5 -97.5 100196 -151 20 67 -23 278.9 1.2 1.2 0.1 0.2 4.77 0.48 0.45 0.05 0.08 -8.0 0.1 0.1 0.1 -0.0 -8.06 0.00 1.21450 0.01002 0.00749 0.00114 -0.00047 0.54279 0.01536 0.01289 0.00087 0.00095 3.1734 -0.2345 -0.1685 0.0014 -0.0218 268.5 1.0 1.2 0.2 0.2 49.88 9.01 0.27 -3.26 0.61 0.61 -0.88 0.71 -1.41 3.10 4.82 2.22 1.42 0.31 1.24 0.27 0.33 0.13 -0.27 0.32 + -52.5 -82.5 100257 -170 -16 -5 76 279.0 1.3 1.4 -0.1 0.4 4.82 0.55 0.49 -0.00 0.15 -8.0 0.2 0.1 0.1 0.1 1.37 0.00 1.21343 0.01053 0.00806 0.00064 0.00063 0.54143 0.01600 0.01268 -0.00032 0.00418 3.2095 -0.2757 -0.1747 0.0172 -0.0481 268.5 1.1 1.3 0.0 0.4 50.55 8.57 0.44 -2.69 0.79 5.59 0.93 -0.95 -2.05 2.71 4.60 1.42 1.33 0.31 0.75 0.31 0.16 -0.16 -0.09 -0.06 + -52.5 -67.5 100298 -204 -92 -74 146 280.4 2.9 1.6 -0.1 0.3 4.74 0.71 0.57 0.02 0.17 -6.7 0.3 -0.5 -0.4 0.2 11.93 0.00 1.21327 0.01232 0.00815 0.00007 0.00126 0.54470 0.01457 0.01001 0.00038 0.00376 3.2699 -0.2834 -0.1156 -0.0117 -0.0353 269.2 2.0 1.3 -0.2 0.4 45.93 4.89 -0.10 -1.72 0.02 -1.27 2.22 -2.03 -2.00 1.08 3.58 1.49 1.13 0.70 0.55 -1.61 -0.29 0.34 0.26 0.23 + -52.5 -52.5 100238 -152 -198 -150 158 278.1 2.2 1.5 -0.1 0.2 4.48 0.74 0.52 -0.02 0.11 -6.5 1.2 0.4 -0.1 0.1 7.31 0.00 1.21036 0.01382 0.00813 -0.00042 0.00150 0.54222 0.01666 0.00809 -0.00097 0.00499 3.2970 -0.2417 -0.1674 -0.0218 -0.0699 268.1 2.3 1.3 -0.3 0.4 46.07 2.21 -0.65 -1.21 0.70 -5.97 2.56 -1.51 -1.21 -0.81 3.49 1.14 1.36 0.18 1.07 -0.99 -0.12 -0.32 -0.33 -0.26 + -52.5 -37.5 100142 -44 -252 -153 118 275.2 2.0 1.7 -0.1 0.2 3.78 0.61 0.48 -0.02 0.10 -6.2 1.3 0.1 -0.1 -0.1 9.74 0.00 1.20787 0.01470 0.00785 -0.00048 0.00112 0.54720 0.01755 0.00638 -0.00081 0.00419 2.9770 -0.2653 -0.1248 0.0126 -0.0818 266.1 2.4 1.3 -0.2 0.3 45.46 1.08 -0.28 -0.88 1.43 -1.52 2.35 -1.22 0.78 -1.42 4.36 0.83 1.06 0.07 0.71 -0.49 0.25 -0.41 0.22 -0.09 + -52.5 -22.5 99965 46 -254 -133 58 274.5 1.8 1.5 -0.0 0.1 3.56 0.55 0.41 0.01 0.05 -7.4 1.0 0.1 0.2 -0.0 20.02 0.00 1.20604 0.01501 0.00766 -0.00005 0.00027 0.54766 0.01866 0.00419 -0.00100 0.00223 2.9161 -0.3394 -0.1018 -0.0103 -0.0272 265.1 2.3 1.2 -0.1 0.1 50.05 -0.44 -0.18 -0.98 2.00 -3.70 1.33 0.29 1.30 -2.86 4.32 0.14 1.11 -0.06 0.23 -0.17 -0.13 0.06 0.40 -0.65 + -52.5 -7.5 99832 77 -230 -83 -30 273.6 1.4 1.5 0.0 -0.1 3.34 0.44 0.39 0.01 -0.00 -7.1 0.9 0.4 0.1 -0.1 27.44 0.00 1.20472 0.01444 0.00820 0.00039 -0.00085 0.54805 0.01794 0.00581 -0.00089 -0.00092 2.8204 -0.3200 -0.1305 0.0057 0.0391 264.3 2.1 1.2 0.0 -0.2 55.82 -1.58 -1.42 -1.42 2.03 -2.22 -2.10 1.76 1.09 -2.74 4.85 0.69 1.21 0.04 0.11 -0.28 -0.41 0.26 -0.34 -0.44 + -67.5 7.5 98336 94 21 123 -67 264.2 7.2 4.7 0.6 -0.8 1.77 0.91 0.54 0.18 -0.01 -5.1 -3.5 -2.6 0.6 0.7 14.67 -0.00 1.16781 0.02505 0.01831 0.00390 -0.00071 0.53440 -0.00679 -0.00649 0.00092 0.00303 2.9096 0.5280 0.2857 -0.0341 -0.1482 255.6 4.7 3.0 0.8 -0.5 11.08 -5.90 -4.32 -4.20 -2.13 -2.82 -0.01 3.00 2.77 -2.13 2.63 0.47 0.10 -0.29 -0.15 -0.18 -0.26 0.25 0.21 0.10 + -67.5 22.5 98234 112 37 194 -70 264.2 7.0 5.4 0.9 -1.0 1.81 0.88 0.65 0.17 -0.00 -4.7 -3.9 -3.0 0.4 0.7 15.86 -0.00 1.16694 0.02470 0.01945 0.00455 -0.00083 0.53006 -0.00627 -0.00863 0.00138 0.00347 2.9088 0.5696 0.3945 -0.0307 -0.2037 255.7 4.5 3.6 1.0 -0.6 9.91 -7.48 -3.35 -4.46 -1.84 0.60 -1.39 2.54 2.30 0.57 2.60 0.49 0.07 -0.07 -0.25 0.18 -0.23 0.38 -0.15 0.17 + -67.5 37.5 98403 56 81 197 -56 264.4 6.6 5.2 0.9 -1.1 1.76 0.81 0.64 0.14 0.01 -5.2 -2.9 -2.6 0.5 0.8 24.44 -0.00 1.16783 0.02432 0.01982 0.00450 -0.00063 0.53568 -0.00932 -0.00443 0.00226 0.00290 2.8000 0.5289 0.2976 0.0460 -0.2217 255.4 4.5 3.6 1.0 -0.6 6.06 -6.83 -1.49 -4.38 -0.54 8.95 -1.83 1.13 -0.80 1.24 3.17 0.34 0.75 0.05 -0.24 -0.19 0.02 0.03 -0.32 0.17 + -67.5 52.5 82438 272 318 203 -56 254.7 5.9 2.1 2.1 -0.2 0.61 0.44 0.09 0.19 0.02 -3.0 -2.3 0.3 -0.8 0.1 38.75 1379.92 1.16464 0.02586 0.01973 0.00466 0.00018 0.51543 -0.01042 0.01308 -0.00406 0.00124 1.7743 0.8157 -0.1845 0.3189 -0.1140 246.6 4.3 1.9 1.5 -0.1 9.24 -7.82 -0.16 -5.82 0.92 4.57 0.63 -1.51 0.25 1.40 1.90 0.03 0.76 -0.38 0.15 -0.52 0.46 -0.34 0.36 -0.03 + -67.5 67.5 95697 -9 253 118 -33 260.4 7.9 1.8 2.6 -0.1 1.16 0.82 0.12 0.38 0.03 -5.5 -2.0 0.1 -0.0 -0.1 25.44 239.59 1.16588 0.02634 0.01690 0.00491 -0.00006 0.55302 -0.02082 0.01146 -0.00954 0.00179 2.5302 0.5906 -0.1537 0.1987 -0.0462 251.8 5.8 1.5 2.2 -0.1 7.26 -6.34 -0.09 -6.76 1.66 -6.65 3.32 -2.18 0.25 0.00 3.34 0.17 0.94 -0.82 0.34 0.85 0.02 0.01 -0.06 -0.23 + -67.5 82.5 92509 200 243 187 -56 261.1 6.3 1.8 2.1 -0.2 1.04 0.68 0.12 0.30 0.02 -3.8 -2.2 -0.0 -0.6 0.2 15.35 489.24 1.16803 0.02615 0.01619 0.00394 -0.00027 0.53654 -0.00821 0.00767 -0.00507 0.00140 2.1398 0.6406 -0.1292 0.2673 -0.0476 252.1 4.8 1.6 1.6 0.0 1.75 -3.27 -1.28 -5.65 0.83 3.16 0.09 -1.42 -0.03 -1.31 2.93 0.25 0.66 -0.51 -0.01 -0.40 -0.12 -0.09 0.16 -0.03 + -67.5 97.5 78758 384 278 258 -82 252.9 5.8 1.3 2.3 0.2 0.62 0.34 0.07 0.19 0.03 -1.8 -2.8 0.5 -1.2 -0.1 1.05 1728.66 1.16616 0.02713 0.01656 0.00340 -0.00052 0.49539 0.00018 0.01287 -0.00460 -0.00110 2.1980 0.4181 -0.2091 0.2834 -0.0009 245.6 4.1 1.4 1.6 0.2 1.84 -3.42 -1.85 -5.17 -1.17 -3.15 -2.80 -1.64 0.70 -0.33 1.51 0.09 0.40 -0.24 -0.11 -0.07 -0.20 -0.05 0.04 0.06 + -67.5 112.5 85333 241 248 274 -70 256.6 5.9 1.3 2.1 0.5 0.87 0.51 0.11 0.26 0.09 -2.0 -2.8 0.4 -0.7 -0.3 -17.43 1121.25 1.16849 0.02562 0.01460 0.00311 -0.00068 0.51135 -0.00538 0.00711 -0.00438 -0.00110 2.1772 0.6366 -0.1046 0.2089 0.0227 249.5 4.0 1.4 1.7 0.3 3.40 -3.49 -1.45 -4.39 -2.63 -0.66 -3.22 -2.01 0.54 1.33 2.42 -0.17 0.48 -0.18 -0.21 0.55 0.06 0.09 0.06 0.19 + -67.5 127.5 84669 189 240 290 -10 257.0 5.6 1.3 2.2 0.6 0.77 0.45 0.11 0.23 0.09 -2.3 -2.7 0.1 -1.0 -0.2 -34.13 1194.19 1.16929 0.02500 0.01351 0.00282 -0.00079 0.51754 -0.00560 0.00889 -0.00295 -0.00116 1.9858 0.5412 -0.1689 0.2167 -0.0188 248.8 3.9 1.3 1.7 0.5 9.98 -7.02 -2.14 -4.49 -2.99 -2.91 -0.83 -0.68 0.21 1.79 3.08 0.07 0.79 -0.24 0.08 -0.06 -0.05 0.10 -0.07 0.16 + -67.5 142.5 87704 124 242 275 37 256.5 6.7 1.1 2.7 0.5 0.67 0.53 0.07 0.29 0.08 -2.9 -2.8 -0.3 -0.8 0.2 -43.58 921.49 1.17238 0.02481 0.01257 0.00277 -0.00060 0.54863 -0.01318 0.01238 -0.00745 0.00132 1.4983 0.8734 -0.2595 0.3870 -0.1272 249.5 4.3 1.2 1.9 0.6 17.81 -8.70 -2.10 -5.14 -2.53 -10.68 2.44 -0.49 -0.18 1.84 4.00 0.57 0.92 -0.12 0.35 0.53 0.09 0.17 -0.15 0.25 + -67.5 157.5 98547 -8 179 262 13 262.3 8.8 1.9 1.7 1.1 1.60 1.08 0.27 0.33 0.22 -1.7 -6.1 0.2 0.5 -0.6 -50.28 -0.00 1.17695 0.02390 0.01163 0.00220 0.00011 0.55458 -0.01621 0.01015 0.00000 0.00131 2.3924 0.7659 -0.0892 0.0776 -0.0396 255.5 5.2 1.5 1.5 0.9 31.76 -10.18 -2.76 -5.26 -0.74 -0.90 1.90 -0.36 -1.31 1.30 5.94 0.81 1.03 0.01 0.35 1.28 0.18 0.26 -0.41 0.01 + -67.5 172.5 98466 64 165 267 64 264.3 7.6 2.6 0.7 1.0 1.90 1.00 0.35 0.19 0.21 -4.7 -3.4 -0.9 1.3 -0.2 -55.77 -0.00 1.17902 0.02336 0.01141 0.00152 0.00017 0.54584 -0.00644 0.00736 0.00175 0.00118 2.5228 0.4929 0.0045 -0.0376 -0.0234 256.6 4.8 1.6 1.0 0.9 36.30 -10.45 -4.31 -4.71 1.35 -2.66 0.62 -0.15 -1.93 1.51 4.61 0.30 0.78 -0.18 0.32 0.83 -0.07 0.08 0.03 0.01 + -67.5 -172.5 98308 135 168 235 138 265.6 6.5 2.5 0.7 0.8 2.08 0.87 0.34 0.17 0.17 -5.6 -2.1 -0.5 0.7 -0.4 -57.72 0.00 1.18090 0.02267 0.01151 0.00156 -0.00024 0.54061 -0.00010 0.00778 0.00166 0.00319 2.6202 0.3317 -0.0479 -0.0512 -0.0476 257.5 4.2 1.7 0.8 0.6 44.54 -10.90 -5.78 -4.93 1.13 1.71 -0.04 0.61 0.03 0.49 3.79 0.31 0.81 -0.68 0.14 0.53 -0.01 0.15 0.19 -0.11 + -67.5 -157.5 98237 151 164 252 165 266.3 5.6 2.7 0.7 0.3 2.18 0.78 0.37 0.15 0.10 -5.8 -1.9 -0.9 0.5 -0.0 -54.39 0.00 1.18136 0.02206 0.01215 0.00203 -0.00091 0.53850 0.00306 0.00800 0.00169 0.00463 2.6829 0.2617 -0.0371 -0.0355 -0.0773 258.0 3.7 1.8 0.8 0.3 43.07 -11.46 -6.62 -4.45 -0.11 1.93 -2.84 1.02 1.52 -1.61 3.30 0.69 0.88 -0.32 0.44 0.33 -0.24 0.20 -0.18 -0.44 + -67.5 -142.5 98221 82 183 315 156 267.1 4.7 3.3 0.7 -0.5 2.29 0.68 0.43 0.15 -0.00 -6.3 -1.4 -1.4 0.4 0.5 -47.81 0.00 1.18142 0.02110 0.01311 0.00248 -0.00175 0.53522 0.00312 0.00490 0.00210 0.00508 2.7728 0.1586 0.0617 0.0034 -0.1296 258.6 3.2 2.0 0.8 -0.1 41.30 -12.50 -7.02 -3.47 -0.84 3.94 -5.07 1.83 1.86 -2.12 2.81 0.41 0.42 -0.10 0.12 0.29 -0.36 0.00 -0.15 -0.21 + -67.5 -127.5 98224 -2 186 379 105 267.9 4.0 3.3 0.6 -0.5 2.39 0.59 0.45 0.14 0.01 -6.5 -1.1 -1.1 0.2 0.5 -38.27 0.00 1.18148 0.01996 0.01404 0.00277 -0.00191 0.53296 0.00245 0.00478 0.00127 0.00399 2.8259 0.1353 0.0284 0.0358 -0.0977 259.1 2.6 2.1 0.7 -0.2 35.38 -12.95 -8.15 -2.97 -1.36 5.51 -5.44 0.64 0.75 -1.42 2.42 0.08 0.58 -0.24 -0.07 0.11 -0.49 0.17 -0.02 0.17 + -67.5 -112.5 98245 -107 190 388 70 268.8 3.2 3.0 0.5 -0.3 2.52 0.49 0.44 0.10 0.03 -7.0 -0.9 -0.8 0.3 0.4 -30.87 0.00 1.18154 0.01883 0.01463 0.00298 -0.00154 0.52950 0.00267 0.00421 0.00075 0.00322 2.9505 0.0994 0.0183 0.0463 -0.0794 259.8 2.1 2.0 0.6 -0.1 32.28 -13.18 -8.15 -2.41 -1.49 4.62 -5.07 0.18 -0.59 0.68 2.31 -0.23 0.71 -0.03 0.00 0.36 -0.41 -0.37 0.06 0.22 + -67.5 -97.5 98280 -205 184 338 77 269.6 2.6 2.7 0.3 -0.1 2.62 0.43 0.43 0.07 0.08 -7.3 -0.7 -0.5 0.2 0.4 -23.42 0.00 1.18149 0.01801 0.01489 0.00291 -0.00090 0.52737 0.00201 0.00444 0.00227 0.00126 3.0429 0.0610 0.0011 0.0193 -0.0464 260.3 1.7 1.9 0.4 0.1 30.13 -12.93 -7.53 -1.68 -0.62 5.23 -3.37 -0.68 -2.79 1.28 2.40 0.07 0.42 0.13 0.10 -0.10 -0.03 0.13 -0.12 -0.11 + -67.5 -82.5 98399 -273 164 263 110 269.3 3.4 2.6 -0.3 -0.2 2.63 0.56 0.41 0.00 0.07 -6.5 -1.5 -0.8 0.8 0.4 -10.63 0.00 1.18119 0.01814 0.01504 0.00249 -0.00027 0.52753 0.00003 0.00433 0.00241 0.00190 3.0218 0.0940 0.0329 -0.0319 -0.0792 260.4 2.1 1.7 0.1 0.1 26.97 -11.99 -6.47 -1.81 0.00 8.49 1.87 -0.98 -3.38 1.63 2.10 -0.27 0.50 -0.26 0.03 0.16 0.91 -0.24 -0.15 0.04 + -67.5 -67.5 95784 -214 146 152 102 266.5 5.0 1.8 0.4 -0.0 2.09 0.78 0.26 0.07 0.08 -5.3 -1.5 -0.4 -0.0 0.5 8.64 241.84 1.17987 0.01993 0.01520 0.00205 0.00044 0.52936 -0.00409 0.00796 -0.00089 0.00226 2.5932 0.4049 -0.0756 0.0639 -0.0777 258.3 3.0 1.4 0.1 0.2 25.48 -9.27 -5.87 -2.41 0.89 12.12 4.65 -0.29 -3.82 0.02 2.22 -0.18 0.55 -0.80 0.10 -0.48 1.60 -0.21 0.57 0.18 + -67.5 -52.5 98840 -244 206 83 82 264.0 8.1 0.9 0.7 0.4 1.86 1.10 0.15 0.20 0.11 0.6 -8.2 0.2 -0.6 0.2 7.64 0.00 1.17774 0.02137 0.01397 0.00179 0.00103 0.54733 -0.01034 0.01060 -0.00105 0.00216 2.1965 0.8925 -0.1275 0.1047 -0.0468 258.2 4.0 0.7 0.1 0.3 28.67 -7.30 -6.74 -3.83 0.67 -8.05 5.42 -1.16 -1.04 0.39 1.73 0.21 0.29 -0.33 0.51 -0.62 0.21 -0.52 0.44 -0.10 + -67.5 -37.5 98745 -108 158 98 88 264.1 8.1 1.5 0.2 0.2 1.88 1.03 0.18 0.13 0.07 -2.5 -6.5 -0.9 0.6 -0.1 8.22 0.00 1.17587 0.02269 0.01421 0.00192 0.00099 0.54411 -0.00790 0.00830 0.00098 0.00356 2.4342 0.7223 -0.0208 -0.0350 -0.0575 257.5 4.2 0.7 0.1 0.2 26.31 -5.94 -7.34 -4.64 -0.50 -8.25 5.03 -0.82 1.67 -0.75 1.89 0.47 0.20 -0.10 0.37 -0.59 -0.06 -0.04 -0.05 -0.11 + -67.5 -22.5 98599 -8 101 140 63 264.1 8.0 2.8 -0.2 -0.1 1.84 0.98 0.32 0.07 0.02 -3.9 -5.6 -1.6 1.4 0.1 6.14 0.00 1.17283 0.02377 0.01507 0.00232 0.00075 0.54031 -0.00711 0.00297 0.00219 0.00569 2.5895 0.6479 0.1696 -0.0750 -0.0986 256.8 4.4 1.3 0.1 0.0 21.51 -4.81 -7.01 -4.48 -1.43 -9.55 3.69 0.51 1.17 -2.25 1.88 0.37 0.15 -0.27 0.27 -0.69 0.03 0.12 0.16 -0.27 + -67.5 -7.5 98456 65 48 123 -1 264.2 7.7 4.1 0.2 -0.7 1.80 0.95 0.45 0.11 -0.03 -4.5 -4.8 -2.5 1.2 1.0 8.62 -0.00 1.16973 0.02475 0.01656 0.00295 0.00004 0.53647 -0.00591 -0.00465 0.00088 0.00502 2.7498 0.5396 0.3419 -0.0852 -0.1531 256.1 4.7 2.2 0.4 -0.3 14.12 -4.92 -5.04 -4.43 -1.23 -5.36 2.20 2.15 1.47 -3.94 2.38 0.87 0.27 -0.13 0.16 -0.78 -0.21 0.51 0.17 -0.18 + -82.5 7.5 66654 520 240 259 -65 241.5 6.5 1.5 2.5 -0.4 0.27 0.18 0.04 0.09 0.00 -3.4 -1.1 -0.1 -0.8 0.1 -9.93 2968.14 1.14042 0.03652 0.02389 0.00660 0.00176 0.45896 -0.01531 0.00857 -0.00587 0.00069 2.6844 0.5204 -0.1667 0.3661 -0.0985 235.0 5.0 1.6 1.5 -0.3 7.62 -0.64 1.38 0.81 -0.17 5.88 1.53 0.90 3.69 -1.48 0.13 0.08 0.05 0.04 0.01 -0.03 0.06 -0.02 0.08 -0.01 + -82.5 22.5 64022 547 249 288 -78 239.7 6.6 1.6 2.6 -0.6 0.24 0.16 0.04 0.08 -0.00 -4.4 -0.7 0.1 -0.5 0.1 -5.53 3270.73 1.13917 0.03766 0.02471 0.00698 0.00186 0.45418 -0.01737 0.00986 -0.00593 -0.00047 2.6564 0.4851 -0.1500 0.3192 -0.1150 232.7 5.4 1.7 1.7 -0.3 11.07 -0.46 1.65 1.42 -0.61 10.50 1.27 0.29 3.34 -0.90 0.11 0.09 0.05 0.04 0.01 0.03 0.09 0.00 0.06 0.01 + -82.5 37.5 61354 572 260 316 -86 235.9 7.3 1.2 3.1 -0.5 0.19 0.15 0.02 0.09 -0.00 -2.3 -1.8 0.1 -1.3 0.4 -0.84 3589.26 1.13829 0.03884 0.02561 0.00723 0.00191 0.44911 -0.01983 0.00988 -0.00555 0.00022 2.2933 0.6667 -0.2978 0.4696 -0.1334 230.1 5.7 1.7 1.9 -0.2 10.34 -0.41 1.53 1.88 -0.68 4.07 0.73 -0.46 2.85 -0.25 0.07 0.06 0.02 0.04 0.00 -0.01 0.04 -0.03 0.05 -0.00 + -82.5 52.5 60042 574 256 331 -84 231.5 9.4 1.5 4.4 -1.0 0.15 0.15 0.01 0.09 -0.00 -1.4 -1.5 2.6 -0.9 0.2 -2.07 3756.25 1.13568 0.04038 0.02577 0.00784 0.00180 0.46754 -0.03768 0.00939 -0.01297 0.00353 1.6828 1.0571 -0.1799 0.6281 -0.2748 228.0 6.2 1.7 2.3 -0.3 11.24 -0.77 1.36 2.04 -0.54 5.46 -0.06 -0.63 1.84 0.02 0.03 0.07 0.01 0.06 0.01 -0.04 0.04 -0.02 0.05 0.01 + -82.5 67.5 57927 621 267 366 -84 232.1 7.4 1.1 3.4 -0.5 0.13 0.13 0.01 0.08 -0.00 -3.3 -1.1 0.1 -0.9 0.3 -0.16 4008.06 1.13684 0.04049 0.02614 0.00746 0.00202 0.47598 -0.04646 0.01459 -0.01747 0.00118 1.6977 0.9421 -0.4075 0.6263 -0.1470 226.9 5.8 1.8 2.2 -0.1 10.42 -0.65 0.65 2.44 -0.28 1.62 -0.05 -1.22 1.07 0.27 0.03 0.05 0.01 0.05 0.01 -0.01 0.00 -0.01 -0.00 0.00 + -82.5 82.5 60415 562 253 346 -76 230.9 9.5 0.9 4.7 -0.7 0.12 0.15 0.00 0.10 -0.00 1.7 -3.7 3.2 -2.4 -0.5 -4.59 3719.25 1.13551 0.04058 0.02489 0.00786 0.00170 0.48867 -0.04979 0.01394 -0.02075 0.00341 1.0829 1.3057 -0.2416 0.7816 -0.2672 227.4 6.3 1.6 2.6 -0.2 13.57 -1.10 0.06 2.37 -0.15 -6.18 -0.48 -1.35 -0.08 0.54 0.02 0.07 -0.01 0.07 0.01 0.02 -0.05 0.01 -0.05 0.00 + -82.5 97.5 63132 520 250 350 -64 232.9 9.6 1.1 4.3 -0.8 0.14 0.16 0.00 0.10 -0.01 1.2 -4.3 -0.7 -2.1 1.1 -15.13 3403.22 1.13605 0.03976 0.02413 0.00751 0.00132 0.47779 -0.03284 0.01127 -0.01408 0.00204 1.1855 1.2080 -0.2881 0.6807 -0.1987 229.0 6.4 1.5 2.6 -0.3 12.61 -2.57 -0.96 1.70 0.30 -15.52 1.39 -0.99 0.04 0.24 0.02 0.05 -0.01 0.06 0.00 -0.02 -0.10 0.01 -0.09 0.01 + -82.5 112.5 65578 480 234 340 -51 231.5 11.9 0.5 5.3 -1.1 0.15 0.19 0.00 0.12 -0.01 7.9 -8.8 3.9 -5.0 -0.3 -25.83 3121.46 1.13773 0.03859 0.02291 0.00692 0.00118 0.48027 -0.02870 0.01345 -0.01113 0.00158 1.0447 1.4158 -0.2622 0.8225 -0.2588 230.6 6.4 1.2 2.5 -0.3 7.75 -2.54 -1.58 1.78 0.46 -19.53 1.36 -0.86 -0.80 0.27 -0.01 0.02 -0.02 0.05 -0.00 -0.02 -0.09 0.02 -0.10 0.01 + -82.5 127.5 68457 490 239 357 -38 235.3 10.8 0.5 3.9 -1.1 0.18 0.21 0.00 0.13 -0.01 1.4 -4.2 -0.5 -1.7 0.9 -37.46 2787.16 1.13823 0.03808 0.02167 0.00657 0.00083 0.49194 -0.03388 0.01537 -0.01253 0.00437 1.2646 1.3270 -0.3091 0.7305 -0.2737 233.2 6.1 1.2 2.1 -0.4 1.62 -2.54 -2.04 1.60 0.63 -17.92 2.69 -0.27 -1.55 0.07 -0.08 -0.02 -0.01 0.03 0.00 -0.02 -0.08 0.01 -0.11 0.01 + -82.5 142.5 71861 518 238 352 -26 239.5 10.3 1.2 3.3 -1.3 0.24 0.24 0.01 0.13 -0.01 6.0 -8.6 -0.7 -2.7 1.4 -41.74 2404.42 1.14120 0.03694 0.02083 0.00621 0.00043 0.48799 -0.02492 0.00994 -0.00781 0.00525 1.5404 1.2168 -0.2101 0.6457 -0.2941 236.2 5.9 1.1 1.9 -0.5 -5.46 -1.01 -2.39 1.46 0.50 -15.81 4.24 0.40 -1.60 -0.27 -0.12 -0.09 -0.00 -0.04 0.00 0.13 -0.08 0.06 -0.12 0.01 + -82.5 157.5 77401 521 265 320 -20 248.2 7.5 1.2 2.3 -1.0 0.35 0.27 0.03 0.14 -0.01 -2.4 -3.2 -0.3 -0.7 0.5 -37.62 1838.98 1.14601 0.03443 0.01939 0.00582 0.00023 0.49568 -0.01304 0.00885 -0.00417 0.00377 1.9140 0.8232 -0.1394 0.4391 -0.1932 240.4 5.4 0.9 1.8 -0.6 -6.18 -0.29 -1.94 0.69 0.42 -6.28 1.87 1.44 -1.30 0.06 -0.01 -0.01 -0.06 -0.00 -0.07 0.60 0.11 0.05 0.07 -0.10 + -82.5 172.5 98927 38 251 202 73 256.2 9.9 1.1 3.3 -1.4 0.56 0.60 0.03 0.35 -0.02 6.1 -6.7 -2.0 -2.5 0.9 -44.99 0.00 1.14963 0.03251 0.01643 0.00624 -0.00048 0.58893 -0.02526 0.00407 -0.01088 0.00324 1.2154 1.2866 -0.1102 0.7035 -0.2212 248.2 6.8 1.0 2.4 -0.8 -11.54 1.47 -0.59 1.46 1.79 -11.37 1.74 2.43 0.31 1.74 0.35 -0.07 -0.05 0.04 -0.18 1.12 -0.18 0.25 -0.02 0.10 + -82.5 -172.5 98735 85 268 217 95 252.8 11.0 1.3 3.9 -1.4 0.64 0.68 0.03 0.39 -0.03 12.1 -9.6 -0.3 -4.2 2.5 -47.03 0.00 1.15039 0.03211 0.01661 0.00624 -0.00042 0.57638 -0.02513 0.00715 -0.01038 0.00677 1.3494 1.4195 -0.1432 0.7523 -0.2618 248.6 6.6 1.1 2.4 -0.8 -13.44 2.56 0.53 1.44 2.64 -7.67 0.94 2.90 -0.38 1.26 -0.06 -0.10 -0.05 0.10 0.01 0.54 -0.20 0.20 -0.04 0.09 + -82.5 -157.5 98571 126 277 225 106 251.8 11.5 1.3 4.3 -1.5 0.65 0.72 0.03 0.41 -0.03 13.9 -10.9 1.7 -4.6 2.5 -46.69 -0.00 1.15202 0.03141 0.01693 0.00609 -0.00044 0.57102 -0.02438 0.00714 -0.00907 0.00755 1.1981 1.5175 -0.1337 0.8019 -0.2589 249.4 6.3 1.2 2.2 -0.9 -8.98 2.22 1.66 1.11 2.56 4.40 -1.71 2.65 -1.57 0.08 0.38 0.12 0.25 0.18 0.16 1.06 0.17 0.28 0.08 0.16 + -82.5 -142.5 93097 298 293 282 63 253.2 9.3 1.6 3.4 -1.3 0.73 0.63 0.07 0.33 -0.02 9.0 -8.3 2.4 -4.0 0.9 -43.63 427.00 1.15334 0.03067 0.01780 0.00552 -0.00014 0.54313 -0.01441 0.00610 -0.00613 0.00593 1.6989 1.2755 -0.1022 0.6579 -0.2541 249.0 5.4 1.4 1.7 -0.7 -3.32 0.98 2.80 0.95 2.01 15.80 -4.66 1.87 -2.61 -0.53 0.73 0.31 0.39 0.19 0.23 1.09 0.42 0.21 -0.05 0.04 + -82.5 -127.5 89159 329 300 280 20 253.7 8.2 1.5 2.8 -1.0 0.72 0.56 0.08 0.28 -0.01 4.4 -6.8 0.9 -3.4 1.0 -40.52 767.24 1.15345 0.03046 0.01847 0.00525 0.00021 0.52823 -0.01127 0.00628 -0.00618 0.00334 1.8091 1.0902 -0.1014 0.5723 -0.1988 247.9 5.1 1.5 1.5 -0.5 2.86 -0.81 3.06 0.72 1.58 21.93 -6.15 0.18 -3.55 -0.50 0.81 0.38 0.34 0.20 0.16 0.93 0.28 0.10 -0.01 -0.04 + -82.5 -112.5 80355 429 312 268 -23 250.4 6.7 1.6 2.1 -0.6 0.58 0.37 0.08 0.17 0.01 -0.1 -4.5 -0.0 -2.2 0.5 -33.87 1569.57 1.15178 0.03096 0.01970 0.00511 0.00079 0.49675 -0.00575 0.00740 -0.00529 0.00185 1.9426 0.8013 -0.0468 0.4603 -0.0950 244.1 4.4 1.5 1.1 -0.3 8.06 -2.77 2.74 0.12 1.16 18.98 -5.79 -1.21 -3.46 0.07 0.63 0.17 0.27 0.05 0.09 -0.05 0.08 -0.12 0.04 -0.07 + -82.5 -97.5 74298 453 300 236 -42 245.5 6.4 1.6 2.1 -0.3 0.43 0.27 0.06 0.13 0.01 -1.4 -1.6 -0.4 -0.7 0.4 -29.19 2164.18 1.15071 0.03123 0.02082 0.00491 0.00128 0.47506 0.00040 0.00777 -0.00199 0.00155 1.8536 0.7998 -0.0267 0.5033 -0.0833 240.8 4.0 1.5 0.9 -0.1 11.43 -4.48 2.14 -0.73 0.99 6.64 -4.87 -2.07 -3.26 0.65 0.46 0.05 0.14 -0.00 0.03 -0.30 -0.22 -0.10 -0.15 -0.01 + -82.5 -82.5 86490 168 238 150 -33 248.9 8.7 1.2 3.1 -0.6 0.43 0.42 0.02 0.23 -0.01 -1.2 -3.5 0.8 -1.4 0.4 -28.77 1049.66 1.14971 0.03056 0.01893 0.00528 0.00118 0.55054 -0.02486 0.01035 -0.01264 0.00520 1.1929 1.2729 -0.1717 0.6642 -0.1663 244.1 5.2 1.4 1.5 -0.3 10.17 -5.32 1.58 -1.37 1.22 -8.74 -0.86 -3.02 -1.72 0.67 0.91 -0.13 0.25 -0.17 0.12 -0.57 -0.67 -0.15 -0.56 0.01 + -82.5 -67.5 96005 -59 176 91 -31 251.8 10.1 1.0 3.5 -0.5 0.55 0.60 0.02 0.33 -0.01 3.0 -5.5 1.1 -2.7 0.8 -25.52 256.20 1.15053 0.02994 0.01777 0.00528 0.00134 0.58733 -0.03584 0.00791 -0.01802 0.00262 1.2278 1.4570 -0.1941 0.7441 -0.1804 247.5 5.9 1.3 1.7 -0.2 6.22 -4.89 1.13 -1.83 1.48 -5.26 1.79 -2.22 -0.83 1.04 1.29 -0.37 0.33 -0.37 0.23 -0.09 0.37 0.01 0.09 0.34 + -82.5 -52.5 88361 187 187 156 -31 252.8 7.3 1.4 2.2 -0.2 0.52 0.42 0.06 0.20 0.02 -0.6 -3.5 0.4 -1.4 0.3 -21.98 867.46 1.15010 0.03023 0.01877 0.00484 0.00174 0.54022 -0.01592 0.00523 -0.00964 0.00016 1.6486 0.9553 -0.0551 0.5259 -0.0920 246.2 5.0 1.3 1.2 0.0 4.20 -3.87 -0.02 -1.86 2.02 3.27 2.31 -1.22 0.49 0.54 0.97 -0.07 0.21 -0.18 0.28 -0.30 0.50 -0.02 0.42 0.03 + -82.5 -37.5 84477 262 191 169 -23 250.2 8.0 1.3 2.6 -0.3 0.46 0.41 0.04 0.21 0.00 1.4 -5.3 0.1 -2.5 0.4 -18.26 1207.48 1.14770 0.03140 0.01952 0.00513 0.00176 0.52434 -0.01771 0.00733 -0.01052 0.00277 1.6939 1.0651 -0.1251 0.5782 -0.1755 244.4 5.1 1.2 1.3 -0.1 1.63 -1.83 -0.20 -1.04 1.99 3.96 2.10 0.02 1.81 -0.77 0.72 0.02 0.13 -0.11 0.21 -0.54 0.29 -0.08 0.40 -0.12 + -82.5 -22.5 75291 429 210 211 -33 247.6 6.0 1.5 1.9 -0.3 0.40 0.26 0.06 0.12 0.01 -1.9 -1.6 0.6 -1.3 -0.2 -13.77 2061.01 1.14631 0.03294 0.02128 0.00541 0.00182 0.48463 -0.00888 0.01001 -0.00568 0.00102 2.4558 0.5672 -0.0598 0.4039 -0.1384 240.8 4.5 1.3 1.1 -0.1 2.76 -1.54 0.41 -0.87 1.48 1.67 2.53 0.59 3.18 -1.95 0.28 0.10 0.06 0.03 0.03 -0.26 0.02 -0.03 0.14 -0.07 + -82.5 -7.5 68257 516 232 241 -49 244.5 5.3 1.6 1.7 -0.3 0.32 0.17 0.05 0.08 0.00 -4.3 -0.5 0.1 -0.4 0.0 -10.43 2782.40 1.14309 0.03491 0.02318 0.00585 0.00186 0.45516 -0.00614 0.00924 -0.00293 -0.00082 2.9026 0.2761 -0.0948 0.2403 -0.0823 236.9 4.6 1.4 1.1 -0.2 3.79 -1.07 0.90 -0.13 0.58 1.06 2.04 1.01 3.78 -1.96 0.14 0.07 0.03 0.04 -0.01 -0.08 0.04 -0.01 0.10 -0.05 diff --git a/src/test/resources/gpt2-grid/corrupted-bad-data-gpt2_5.grd b/src/test/resources/gpt2-grid/corrupted-bad-data-gpt2_5.grd deleted file mode 100644 index 4620e71bb7..0000000000 --- a/src/test/resources/gpt2-grid/corrupted-bad-data-gpt2_5.grd +++ /dev/null @@ -1,10 +0,0 @@ -% lat lon p:a0 A1 B1 A2 B2 T:a0 A1 B1 A2 B2 Q:a0 A1 B1 A2 B2 dT:a0 A1 B1 A2 B2 undu Hs h:a0 A1 B1 A2 B2 w:a0 A1 B1 A2 B2 - 87.5 2.5 101421 21 409 -217 -122 259.2 -13.2 -6.1 2.6 0.3 1.64 -1.62 -0.67 0.51 0.35 1.5 7.1 3.0 -0.8 1.7 19.36 -0.00 1.1879 -0.0345 -0.0129 0.0054 0.0035 0.5792 0.0082 0.0081 -0.0004 0.0050 - 87.5 7.5 101416 21 411 -213 -120 259.3 -13.1 -6.1 2.6 0.3 1.65 -1.61 -0.67 0.51 0.35 1.4 7.0 3.1 -0.8 1.8 19.35 -0.00 1.1879 -0.0345 -0.0129 0.0054 0.0035 0.5788 0.0078 0.0081 -0.0005 0.0050 - 87.5 12.5 101411 22 413 -209 -118 259.3 -13.1 -6.1 2.6 0.3 1.65 -1.61 -0.68 0.51 0.35 1.3 7.0 3.1 -0.8 1.8 19.25 -0.00 1.1879 -0.0345 -0.0129 0.0054 0.0035 0.5785 0.0074 0.0081 -0.0005 0.0050 - 87.5 17.5 101407 23 415 -205 -116 259.4 -13.0 -6.1 2.6 0.3 1.66 -1.61 -0.68 0.50 0.35 1.2 7.0 3.2 -0.8 1.8 19.08 -0.00 1.1879 -0.0344 -0.0129 0.0053 0.0035 0.5782 0.0070 0.0081 -0.0005 0.0050 - 87.5 22.5 dummy_value 26 417 -202 -115 259.5 -12.9 -6.1 2.5 0.3 1.66 -1.60 -0.69 0.50 0.35 1.1 6.9 3.2 -0.8 1.8 18.96 -0.00 1.1879 -0.0344 -0.0129 0.0053 0.0035 0.5780 0.0066 0.0082 -0.0006 0.0051 - 87.5 27.5 101401 30 420 -198 -113 259.5 -12.9 -6.1 2.5 0.3 1.66 -1.60 -0.69 0.50 0.35 1.1 6.9 3.3 -0.8 1.8 18.90 -0.00 1.1879 -0.0344 -0.0129 0.0053 0.0035 0.5777 0.0063 0.0083 -0.0005 0.0051 - 87.5 32.5 101398 35 422 -195 -112 259.5 -12.8 -6.2 2.5 0.3 1.67 -1.60 -0.69 0.50 0.35 1.0 6.8 3.3 -0.8 1.8 18.80 -0.00 1.1879 -0.0344 -0.0129 0.0053 0.0035 0.5775 0.0061 0.0084 -0.0005 0.0051 - 87.5 37.5 101397 41 425 -192 -111 259.6 -12.8 -6.2 2.5 0.3 1.67 -1.60 -0.70 0.49 0.35 1.0 6.8 3.3 -0.7 1.9 18.58 -0.00 1.1879 -0.0344 -0.0129 0.0053 0.0035 0.5774 0.0059 0.0086 -0.0004 0.0051 - \ No newline at end of file diff --git a/src/test/resources/gpt2-grid/corrupted-irregular-grid-gpt2_5.grd b/src/test/resources/gpt2-grid/corrupted-irregular-grid-gpt2_5.grd deleted file mode 100644 index f1026d323d..0000000000 --- a/src/test/resources/gpt2-grid/corrupted-irregular-grid-gpt2_5.grd +++ /dev/null @@ -1,6 +0,0 @@ -% This is not an original GPT2 grid, it has been edited for test purposes and should not be used except for test -% lat lon p:a0 A1 B1 A2 B2 T:a0 A1 B1 A2 B2 Q:a0 A1 B1 A2 B2 dT:a0 A1 B1 A2 B2 undu Hs h:a0 A1 B1 A2 B2 w:a0 A1 B1 A2 B2 - 17.5 67.5 100913 30 -28 9 -14 283.4 -9.1 -2.9 -0.0 0.3 6.02 -2.61 -1.33 0.29 0.32 -4.6 1.0 -0.3 -0.1 -0.0 41.28 52.71 1.2352 -0.0211 -0.0108 0.0008 0.0009 0.5583 -0.0126 -0.0125 0.0040 0.0012 - 17.5 72.5 100434 46 -48 -8 -16 282.7 -10.0 -3.0 0.0 0.2 5.88 -2.85 -1.38 0.39 0.36 -4.3 1.1 -0.2 -0.2 0.1 34.11 93.18 1.2344 -0.0225 -0.0111 0.0010 0.0007 0.5578 -0.0120 -0.0115 0.0049 0.0017 - 12.5 67.4999 89942 -161 -172 56 16 281.0 -8.4 -3.1 0.1 0.1 5.13 -2.91 -1.20 0.34 0.34 -6.5 0.6 -0.2 0.2 -0.1 47.30 1024.64 1.2395 -0.0209 -0.0110 0.0010 0.0008 0.5243 -0.0055 -0.0101 0.0041 0.0011 - 12.5 72.5 99762 183 -97 41 -6 284.8 -10.5 -2.7 -0.3 0.1 6.21 -3.28 -1.34 0.41 0.22 -5.4 1.6 -0.0 0.0 0.1 44.86 160.75 1.2406 -0.0219 -0.0106 0.0008 0.0005 0.5649 -0.0088 -0.0096 0.0044 0.0026 diff --git a/src/test/resources/orbit-determination/GNSS/dsst_od_test_GPS07.in b/src/test/resources/orbit-determination/GNSS/dsst_od_test_GPS07.in index 99ff35fedf..d69c139e04 100644 --- a/src/test/resources/orbit-determination/GNSS/dsst_od_test_GPS07.in +++ b/src/test/resources/orbit-determination/GNSS/dsst_od_test_GPS07.in @@ -97,7 +97,7 @@ general.relativity = false ## Attitude mode, from the following predefined list [NADIR_WITH_YAW_COMPENSATION] ## NADIR_POINTING_WITH_YAW_COMPENSATION, CENTER_POINTING_WITH_YAW_STEERING, ## LOF_ALIGNED_LVLH, LOF_ALIGNED_QSW, LOF_ALIGNED_TNW, LOF_ALIGNED_VNC, LOF_ALIGNED_VVLH -attitude.mode = CENTER_POINTING_WITH_YAW_STEERING +attitude.mode = DEFAULT_LAW ## Use range and range-rate measurements use.range.measurements = true diff --git a/src/test/resources/orbit-determination/Lageos2/dsst_od_test_Lageos2.in b/src/test/resources/orbit-determination/Lageos2/dsst_od_test_Lageos2.in index 91a8e71f86..5cb0118800 100644 --- a/src/test/resources/orbit-determination/Lageos2/dsst_od_test_Lageos2.in +++ b/src/test/resources/orbit-determination/Lageos2/dsst_od_test_Lageos2.in @@ -24,16 +24,16 @@ ### Orbit definition ## date of the orbital parameters (UTC) -orbit.date = 2016-02-12T00:43:20.000 +orbit.date = 2016-02-13T16:00:00.000 -## Position & velocity along X, y, z in inertial frame (m) -orbit.cartesian.px = -2551060.0 -orbit.cartesian.py = 9748630.0 -orbit.cartesian.pz = -6528040.0 -orbit.cartesian.vx = -4595.0 -orbit.cartesian.vy = 1029.0 -orbit.cartesian.vz = 3382.0 +## Position & velocity along X, y, z in inertial frame (m) +orbit.cartesian.px = 7526990.0 +orbit.cartesian.py = -9646310.0 +orbit.cartesian.pz = 1464110.0 +orbit.cartesian.vx = 3033.0 +orbit.cartesian.vy = 1715.0 +orbit.cartesian.vz = -4447.0 ## Spacecraft mass (kg) [1000.] mass = 405.380 @@ -45,9 +45,9 @@ iers.conventions = 2010 inertial.frame = EME2000 # Propagator min step (s), max step (s), position error (m) and normalization scale (m) [0.001, 300, 10.0, 1000.0] -propagator.min.step = 3600 +propagator.min.step = 43200.0 propagator.max.step = 86400 -propagator.position.error = 10.0 +propagator.position.error = 0.001 # body (default is a WGS-84 ellipsoid with IERS-2010 conventions and simple EOP frame) body.frame = CIO/2010-based ITRF simple EOP @@ -84,7 +84,7 @@ drag.cd.estimated = false drag.area = 0.28270 ## Solar Radiation Pressure (true/false) [false] -solar.radiation.pressure = false +solar.radiation.pressure = true ## SRP coefficient solar.radiation.pressure.cr = 1.134 ## Estimation flag for SRP coefficient (true/false) [false] @@ -331,7 +331,7 @@ PV.measurements.velocity.sigma = 0.01 # parameter x and sx is the associated scale factor # scaling factor for orbital parameters normalization (m) # if not specified, the value set for propagator.position.error will be copied -estimator.orbital.parameters.position.scale = 100.0 +estimator.orbital.parameters.position.scale = 10.0 # we can use either a Levenberg-Marquardt or a Gauss-Newton # optimization engine. Default is Levenberg-Marquardt diff --git a/src/test/resources/orbit-determination/Lageos2/unscented_kalman_od_test_Lageos2.in b/src/test/resources/orbit-determination/Lageos2/unscented_kalman_od_test_Lageos2.in deleted file mode 100644 index eba974d74c..0000000000 --- a/src/test/resources/orbit-determination/Lageos2/unscented_kalman_od_test_Lageos2.in +++ /dev/null @@ -1,378 +0,0 @@ -####################################### -# LAGEOS-2 -# -# -# Initial orbit extracted from the following TLE -# -# 1 22195U 92070B 16045.51027931 -.00000009 00000-0 00000+0 0 9990 -# 2 22195 52.6508 132.9147 0137738 336.2706 1.6348 6.47294052551192 -# -# -####################################### -## -## Input file for OrbitDetermination - -## The input file syntax is a set of key=value lines. -## Blank lines and lines starting with '#' (after whitespace trimming) are -## silently ignored. -## The equal sign may be surrounded by space characters. -## Keys must correspond to the ParameterKey enumerate constants, given that -## matching is not case sensitive and that '_' characters may appear as '.' -## characters in the file. - -## This file must contain one orbit defined as keplerian, equinoctial, circular -## or cartesian. - -## Some parameters are optional, default values are shown below between []. - -## All dates are treated in UTC timescale. -## The inertial frame for orbit definition and propagation is EME2000. -## Physical data are read from the src/tutorial/resources/tutorial-orekit-data -## directory. - -### Orbit definition -## date of the orbital parameters (UTC) -orbit.date = 2016-02-14T12:14:48.132 - - -## Position & velocity along X, y, z in inertial frame (m) -orbit.cartesian.px = -5532232.334281051 -orbit.cartesian.py = 1.0025636857493076E7 -orbit.cartesian.pz = -3578941.1097531132 -orbit.cartesian.vx = -3871.2032545876173 -orbit.cartesian.vy = -607.9056504788068 -orbit.cartesian.vz = 4281.008504966887 -#orbit.tle.line_1 = 1 22195U 92070B 16045.51027931 -.00000009 00000-0 00000+0 0 9990 -#orbit.tle.line_2 = 2 22195 52.6508 132.9147 0137738 336.2706 1.6348 6.47294052551192 -#orbit.tle.line_1 =1 22195U 92070B 16061.61468796 -.00000009 +00000-0 +00000-0 0 9998 -#orbit.tle.line_2 = 2 22195 052.6496 122.7359 0137661 343.3246 089.0633 06.47294129552242 - -## Spacecraft mass (kg) [1000.] -mass = 405.380 - -# IERS conventions [2010] -iers.conventions = 2010 - -# Inertial frame [EME2000] -inertial.frame = EME2000 - -# Propagator min step (s), max step (s), position error (m) and normalization scale (m) [0.001, 300, 10.0, 1000.0] -propagator.min.step = 0.001 -propagator.max.step = 300 -propagator.position.error = 10.0 - -# body (default is a WGS-84 ellipsoid with IERS-2010 conventions and simple EOP frame) -body.frame = CIO/2010-based ITRF simple EOP -body.equatorial.radius = 6378137.0 -body.inverse.flattening = 298.257223563 - -### Force models - -## Central body gravity potential degree -central.body.degree = 8 -## Central body gravity potential order -central.body.order = 8 - -## 3rd body Sun (true/false) [false] -third.body.sun = true -## 3rd body Moon (true/false) [false] -third.body.moon = true - -## ocean tides (negative degree and order by default to disable ocean tides) -ocean.tides.degree = -1 -ocean.tides.order = -1 - -## solid tides (true/false) [false] -solid.tides.sun = false; -solid.tides.moon = false - -## Atmospheric drag (true/false) [false] -drag = false -## Drag coefficient -drag.cd = 2.0 -## Estimation flag for drag coefficient (true/false) [false] -drag.cd.estimated = false -## Drag area (m^2) -drag.area = 0.28270 - -## Solar Radiation Pressure (true/false) [false] -solar.radiation.pressure = false -## SRP coefficient -solar.radiation.pressure.cr = 1.134 -## Estimation flag for SRP coefficient (true/false) [false] -solar.radiation.pressure.cr.estimated = true -## SRP area (m^2) -solar.radiation.pressure.area = 0.28270 - -## Earth's Albedo and Infrared (true/false) [false] -## It reuses the configuration of the solar radiation pressure for the reflection coefficient -earth.albedo.infrared = false -## Angular resolution -albedo.infrared.angular.resolution = 15.0 - -# Post-Newtonian correction force due to general relativity (true/false) [false] -general.relativity = false - -## Use range and range-rate measurements -use.range.measurements = true -use.range.rate.measurements = false - -## On-board range bias (m) [0.0] -onboard.range.bias = 0.0 -onboard.range.bias.min = 0.0 -onboard.range.bias.max = 0.0 - -### Estimation flag for on-board range (true/false) [false] -onboard.range.bias.estimated = false - -## On-board antenna phase center in spacecraft frame (m) [0.0, 0.0, 0.0] -on.board.antenna.phase.center.x = 0.0 -on.board.antenna.phase.center.y = 0.0 -on.board.antenna.phase.center.z = 0.0 - -## On-board clock offset (s) [0.0] -on.board.clock.offset = 0.0 -on.board.clock.offset.min = -0.01 -on.board.clock.offset.max = +0.01 -on.board.clock.offset.estimated = false - -## correction of ground stations displacements (true/false) [false, false, false] -## if remove.permanent.deformation if true, the station coordinates are -## considered *mean tide* and already include the permanent deformation, hence -## it should be removed from the displacement to avoid considering it twice; -## if false, the station coordinates are considered *conventional tide free* -## so the permanent deformation must be included in the displacement -solid.tides.displacement.correction = false -solid.tides.displacement.remove.permanent.deformation = false -ocean.loading.correction = false - -## Use a time span tropospheric model or not -use.time.span.tropospheric.model = true - -## Ground stations (angles in degrees, altitude and range bias in meters) -ground.station.name [0] = YARL -ground.station.latitude [0] = -29.046495 -ground.station.longitude [0] = 115.346744 -ground.station.altitude [0] = 245.088103 -ground.station.clock.offset [0] = 0.0 -ground.station.clock.offset.min [0] = -0.001 -ground.station.clock.offset.max [0] = +0.001 -ground.station.clock.offset.estimated [0] = false -ground.station.position.estimated [0] = true -ground.station.range.sigma [0] = 1.0 -ground.station.range.bias [0] = 0.0 -ground.station.range.bias.min [0] = -1000.0 -ground.station.range.bias.max [0] = +1000.0 -ground.station.range.bias.estimated [0] = true -ground.station.range.rate.sigma [0] = 0.001 -ground.station.range.rate.bias [0] = 0.0 -ground.station.range.rate.bias.min [0] = -1.0 -ground.station.range.rate.bias.max [0] = +1.0 -ground.station.range.rate.bias.estimated [0] = true -ground.station.azimuth.sigma [0] = 0.02 -ground.station.azimuth.bias [0] = 0.01 -ground.station.azimuth.bias.min [0] = -0.50 -ground.station.azimuth.bias.max [0] = +0.50 -ground.station.elevation.sigma [0] = 0.02 -ground.station.elevation.bias [0] = 0.01 -ground.station.elevation.bias.min [0] = -0.50 -ground.station.elevation.bias.max [0] = +0.50 -ground.station.az.el.biases.estimated [0] = true -ground.station.elevation.refraction.correction [0] = true -ground.station.tropospheric.model.estimated [0] = false -ground.station.tropospheric.zenith.delay [0] = 2.0 -ground.station.tropospheric.delay.estimated [0] = false -ground.station.global.mapping.function [0] = false -ground.station.niell.mapping.function [0] = false -ground.station.range.tropospheric.correction [0] = true -ground.station.weather.estimated [0] = false -ground.station.range.ionospheric.correction [0] = false -ground.station.ionospheric.model.estimated [0] = false -ground.station.ionospheric.vtec.estimated [0] = false -ground.station.ionospheric.vtec.value [0] = 10.0 -ground.station.ionospheric.hion.value [0] = 350e3 - -ground.station.name [1] = GODL -ground.station.latitude [1] = 39.020605 -ground.station.longitude [1] = 283.172305 -ground.station.altitude [1] = 19.882672 -ground.station.clock.offset [1] = 0.0 -ground.station.clock.offset.min [1] = -0.001 -ground.station.clock.offset.max [1] = +0.001 -ground.station.clock.offset.estimated [1] = false -ground.station.position.estimated [1] = false -ground.station.range.sigma [1] = 1.0 -ground.station.range.bias [1] = 0.0 -ground.station.range.bias.min [1] = -1000.0 -ground.station.range.bias.max [1] = +1000.0 -ground.station.range.bias.estimated [1] = false -ground.station.range.rate.sigma [1] = 0.001 -ground.station.range.rate.bias [1] = 0.0 -ground.station.range.rate.bias.min [1] = -1.0 -ground.station.range.rate.bias.max [1] = +1.0 -ground.station.range.rate.bias.estimated [1] = false -ground.station.azimuth.sigma [1] = 0.02 -ground.station.azimuth.bias [1] = 0.01 -ground.station.azimuth.bias.min [1] = -0.50 -ground.station.azimuth.bias.max [1] = +0.50 -ground.station.elevation.sigma [1] = 0.02 -ground.station.elevation.bias [1] = 0.01 -ground.station.elevation.bias.min [1] = -0.50 -ground.station.elevation.bias.max [1] = +0.50 -ground.station.az.el.biases.estimated [1] = false -ground.station.elevation.refraction.correction [1] = true -ground.station.tropospheric.model.estimated [1] = false -ground.station.tropospheric.zenith.delay [1] = 2.0 -ground.station.tropospheric.delay.estimated [1] = false -ground.station.global.mapping.function [1] = false -ground.station.niell.mapping.function [1] = false -ground.station.range.tropospheric.correction [1] = true -ground.station.weather.estimated [1] = false -ground.station.range.ionospheric.correction [1] = false -ground.station.ionospheric.model.estimated [1] = false -ground.station.ionospheric.vtec.estimated [1] = false -ground.station.ionospheric.vtec.value [1] = 10.0 -ground.station.ionospheric.hion.value [1] = 350e3 - -ground.station.name [2] = HA4T -ground.station.latitude [2] = 20.706489 -ground.station.longitude [2] = 203.743079 -ground.station.altitude [2] = 3056.971459 -ground.station.clock.offset [2] = 0.0 -ground.station.clock.offset.min [2] = -0.001 -ground.station.clock.offset.max [2] = +0.001 -ground.station.clock.offset.estimated [2] = false -ground.station.position.estimated [2] = false -ground.station.range.sigma [2] = 1.0 -ground.station.range.bias [2] = 0.0 -ground.station.range.bias.min [2] = -1000.0 -ground.station.range.bias.max [2] = +1000.0 -ground.station.range.bias.estimated [2] = false -ground.station.range.rate.sigma [2] = 0.001 -ground.station.range.rate.bias [2] = 0.0 -ground.station.range.rate.bias.min [2] = -1.0 -ground.station.range.rate.bias.max [2] = +1.0 -ground.station.range.rate.bias.estimated [2] = false -ground.station.azimuth.sigma [2] = 0.02 -ground.station.azimuth.bias [2] = 0.01 -ground.station.azimuth.bias.min [2] = -0.50 -ground.station.azimuth.bias.max [2] = +0.50 -ground.station.elevation.sigma [2] = 0.02 -ground.station.elevation.bias [2] = 0.01 -ground.station.elevation.bias.min [2] = -0.50 -ground.station.elevation.bias.max [2] = +0.50 -ground.station.az.el.biases.estimated [2] = false -ground.station.elevation.refraction.correction [2] = true -ground.station.tropospheric.model.estimated [2] = false -ground.station.tropospheric.zenith.delay [2] = 2.0 -ground.station.tropospheric.delay.estimated [2] = false -ground.station.global.mapping.function [2] = false -ground.station.niell.mapping.function [2] = false -ground.station.range.tropospheric.correction [2] = true -ground.station.weather.estimated [2] = false -ground.station.range.ionospheric.correction [2] = false -ground.station.ionospheric.model.estimated [2] = false -ground.station.ionospheric.vtec.estimated [2] = false -ground.station.ionospheric.vtec.value [2] = 10.0 -ground.station.ionospheric.hion.value [2] = 350e3 - -ground.station.name [3] = SISL -ground.station.latitude [3] = 33.577694 -ground.station.longitude [3] = 135.937039 -ground.station.altitude [3] = 102.317142 -ground.station.clock.offset [3] = 0.0 -ground.station.clock.offset.min [3] = -0.001 -ground.station.clock.offset.max [3] = +0.001 -ground.station.clock.offset.estimated [3] = false -ground.station.position.estimated [3] = false -ground.station.range.sigma [3] = 1.0 -ground.station.range.bias [3] = 0.0 -ground.station.range.bias.min [3] = -1000.0 -ground.station.range.bias.max [3] = +1000.0 -ground.station.range.bias.estimated [3] = false -ground.station.range.rate.sigma [3] = 0.001 -ground.station.range.rate.bias [3] = 0.0 -ground.station.range.rate.bias.min [3] = -1.0 -ground.station.range.rate.bias.max [3] = +1.0 -ground.station.range.rate.bias.estimated [3] = false -ground.station.azimuth.sigma [3] = 0.02 -ground.station.azimuth.bias [3] = 0.01 -ground.station.azimuth.bias.min [3] = -0.50 -ground.station.azimuth.bias.max [3] = +0.50 -ground.station.elevation.sigma [3] = 0.02 -ground.station.elevation.bias [3] = 0.01 -ground.station.elevation.bias.min [3] = -0.50 -ground.station.elevation.bias.max [3] = +0.50 -ground.station.az.el.biases.estimated [3] = false -ground.station.elevation.refraction.correction [3] = true -ground.station.tropospheric.model.estimated [3] = false -ground.station.tropospheric.zenith.delay [3] = 2.0 -ground.station.tropospheric.delay.estimated [3] = false -ground.station.global.mapping.function [3] = false -ground.station.niell.mapping.function [3] = false -ground.station.range.tropospheric.correction [3] = true -ground.station.weather.estimated [3] = false -ground.station.range.ionospheric.correction [3] = false -ground.station.ionospheric.model.estimated [3] = false -ground.station.ionospheric.vtec.estimated [3] = false -ground.station.ionospheric.vtec.value [3] = 10.0 -ground.station.ionospheric.hion.value [3] = 350e3 - - - -### Measurements parameters -range.outlier.rejection.multiplier = 6 -range.outlier.rejection.starting.iteration = 2 -range.rate.outlier.rejection.multiplier = 6 -range.rate.outlier.rejection.starting.iteration = 2 -az.el.outlier.rejection.multiplier = 6 -az.el.outlier.rejection.starting.iteration = 2 -PV.outlier.rejection.multiplier = 6 -PV.outlier.rejection.starting.iteration = 2 -range.measurements.base.weight = 1.0 -range.rate.measurements.base.weight = 1.0 -azimuth.measurements.base.weight = 1.0 -elevation.measurements.base.weight = 1.0 -PV.measurements.base.weight = 1.0 -PV.measurements.position.sigma = 1.0 -PV.measurements.velocity.sigma = 0.01 - -## Estimator - -# normalized parameters p are computed from physical parameters x -# as p = (x - x0) / sx where x0 is the reference value for physical -# parameter x and sx is the associated scale factor -# scaling factor for orbital parameters normalization (m) -# if not specified, the value set for propagator.position.error will be copied -estimator.orbital.parameters.position.scale = 10.0 - -# we can use either a Levenberg-Marquardt or a Gauss-Newton -# optimization engine. Default is Levenberg-Marquardt -estimator.optimization.engine = Gauss-Newton - -# the default initial step bound factor is 100 for Levenberg-Marquardt -# this is too small for normalized parameters when initial guess is very -# far. An order of magnitude is 100 times the distance error of the initial guess -# divided by estimator.orbital.parameters.position.scale. So if the initial guess -# is about 100km wrong and estimator.orbital.parameters.position.scale is set to 10.0, -# the initial step bound factor should be of the order of magnitude of 1.0e6 -estimator.Levenberg.Marquardt.initial.step.bound.factor = 1.0e6 - -# convergence is reached when max|p(k+1) - p(k)| < ε for each -# normalized estimated parameters p and iterations k and k+1 -# so the ε threshold (which corresponds to the key -# estimator.normalized.parameters.convergence.threshold) -# Normalized values are computed as (x - x0) / sx, -# so convergence is reached when the following condition holds for -# all estimated parameters: |x[i+1] - x[i]| <= ε * sx -# So the convergence threshold specified here can be considered as -# a multiplication factor applied to scale. Since for all parameters -# the scale is often small (typically about 1 m for orbital positions -# for example), then the threshold should not be too small. A value -# of 10⁻³ is often quite accurate. -estimator.normalized.parameters.convergence.threshold = 1.0e-3 -estimator.max.iterations = 25 -estimator.max.evaluations = 35 - -# comma-separated list of measurements files (in the same directory as this file) -measurements.files = Lageos2_test.aer \ No newline at end of file diff --git a/src/test/resources/rinex/ZIMM00CHE_R_20190320000_15M_30S_MO.crx.gz b/src/test/resources/rinex/ZIMM00CHE_R_20190320000_15M_30S_MO.crx.gz index 7563cffead..40f3df4929 100644 Binary files a/src/test/resources/rinex/ZIMM00CHE_R_20190320000_15M_30S_MO.crx.gz and b/src/test/resources/rinex/ZIMM00CHE_R_20190320000_15M_30S_MO.crx.gz differ diff --git a/src/test/resources/rinex/aiub0000.00o b/src/test/resources/rinex/aiub0000.00o index 9f2baf2e15..32132f25c1 100644 --- a/src/test/resources/rinex/aiub0000.00o +++ b/src/test/resources/rinex/aiub0000.00o @@ -11,7 +11,7 @@ X1234A123 XX ZZZ REC # / TYPE / VERS .9030 .0000 .0000 ANTENNA: DELTA H/E/N 1 1 WAVELENGTH FACT L1/2 1 2 6 G14 G15 G16 G17 G18 G19 WAVELENGTH FACT L1/2 - 0 RCV CLOCK OFFS APPL + 1 RCV CLOCK OFFS APPL 4 P1 L1 L2 P2 # / TYPES OF OBSERV 18.000 INTERVAL 2001 3 24 13 10 36.0000000 GPS TIME OF FIRST OBS diff --git a/src/test/resources/rinex/clckReset_U_20190320000_10M_10M_MO.crx b/src/test/resources/rinex/clckReset_U_20190320000_10M_10M_MO.crx index d530dd1dfc..6796c06deb 100644 --- a/src/test/resources/rinex/clckReset_U_20190320000_10M_10M_MO.crx +++ b/src/test/resources/rinex/clckReset_U_20190320000_10M_10M_MO.crx @@ -13,6 +13,7 @@ TRIMBLE NETR9 SWISSTOPO OBSERVER / AGENCY G 12 C1C L1C S1C C2W L2W S2W C2X L2X S2X C5X L5X S5X SYS / # / OBS TYPES 30.000 INTERVAL 2019 2 1 0 0 0.0000000 GPS TIME OF FIRST OBS + 1 RCV CLOCK OFFS APPL G L2X -0.25000 SYS / PHASE SHIFT R L1P 0.25000 SYS / PHASE SHIFT R L2C -0.25000 SYS / PHASE SHIFT diff --git a/src/test/resources/sinex/ITRF2020-psd-gnss.snx b/src/test/resources/sinex/ITRF2020-psd-gnss.snx new file mode 100644 index 0000000000..d5905c481b --- /dev/null +++ b/src/test/resources/sinex/ITRF2020-psd-gnss.snx @@ -0,0 +1,1373 @@ +%=SNX 2.02 IGN 21:231:52906 IGN 94:002:00000 21:001:00000 P 580 2 S +*------------------------------------------------------------------------------- ++SITE/ID +*CODE PT __DOMES__ T _STATION DESCRIPTION__ _LONGITUDE_ _LATITUDE__ HEIGHT_ + AB01 A 49237M001 P AtkaIslandAK2007, UNIT 185 47 42.9 52 12 34.2 25.0 + AB02 A 70204M001 P Nikolski__AK2007, ALAS 191 08 43.2 52 58 14.2 193.0 + AB07 A 40423M002 P Sandpoint, UNITED STAT 199 31 23.7 55 20 57.4 90.0 + AB48 A 70208M001 P PortAlexanAK2005, ALAS 225 21 10.8 56 14 42.2 5.0 + AB51 A 49233M001 P PetersburgAK2005, UNIT 227 05 11.2 56 47 51.4 76.0 + AC25 A 70206M001 P King_Cove_AK2005, ALAS 197 41 9.4 55 05 20.3 584.0 + AC40 A 70205M001 P 201 22 53.1 56 55 49.3 40.0 + AC60 A 49260M001 P Westeast__AK2008, UNIT 174 04 34.5 52 42 52.6 18.0 + AIS5 A 49998M001 P Annette Island - Alask 228 24 1.7 55 04 8.6 32.0 + ANTC A 41713S001 P Antuco, CHILE 288 28 4.6 -37 20 19.3 745.0 + AREG A 42202M008 P Arequipa, PERU 288 30 25.6 -16 27 55.5 2489.0 + AREQ A 42202M005 P Arequipa, PERU 288 30 24.5 -16 27 56.4 2491.0 + ASPA A 50503S006 P AMERICAN SAMOA, AMERIC 189 16 39.3 -14 19 34.0 54.0 + BLUF A 50234M001 P Bluff, NEW ZEALAND 168 17 31.5 -46 35 6.2 125.0 + BLYT A 40479M001 P 245 17 6.5 33 36 37.5 86.0 + CARI A 22316M001 P 92 43 10.4 11 36 51.1 -2.0 + CHAN A 21611M002 P Changchun, CHINA 125 26 39.1 43 47 26.5 273.0 + CHOF A 21788M001 P Chofu, JAPAN 139 31 51.8 35 40 28.3 94.0 + CHTI A 50242M001 P Chatham Island North, 183 22 58.4 -43 44 7.7 76.0 + CKSV A 23606M002 P Tainan, TAIWAN (PROVIN 120 13 12.0 22 59 55.9 60.0 + CLGO A 49935M001 P COLLEGE/ALASKA, UNITED 212 08 22.2 64 52 25.6 196.0 + COCO A 50127M001 P Coco Islands, AUSTRALI 96 50 2.3 -12 11 18.1 -35.0 + CONT A 41719M004 P Concepcion, CHILE 286 58 28.6 -36 50 34.2 173.0 + CONZ A 41719M002 P Concepcion, CHILE 286 58 28.3 -36 50 37.5 181.0 + COPO A 41714S001 P Copiapo, CHILE 289 39 42.3 -27 23 4.3 479.0 + CORD A 41511M001 P Cordoba GPS site, ARGE 295 31 47.8 -31 31 42.4 747.0 + COSO A 40469M001 P CHINA LAKE, UNITED STA 242 11 28.0 35 58 56.4 1455.0 + CUSV A 21904S001 P Bangkok, THAILAND 100 32 2.1 13 44 9.3 74.0 + CUUT A 21904S002 P Bangkok, THAILAND 100 32 2.2 13 44 9.6 74.0 + DAE2 A 23902M003 P Daejeon, SOUTH KOREA ( 127 22 28.1 36 23 57.9 117.0 + DAEJ A 23902M002 P Daejeon, SOUTH KOREA ( 127 22 28.1 36 23 57.9 117.0 + DGAR A 30802M001 P DIEGO GARCIA, BRITISH 72 22 12.9 -7 16 10.9 -65.0 + DHLG A 49904M001 P DURMID HILL, UNITED ST 244 12 43.1 33 23 23.3 -83.0 + DUND A 50212M003 P Dunedin, NEW ZEALAND 170 35 49.8 -45 53 1.2 387.0 + EIL1 A 49805S001 P Eielson AFB, UNITED ST 212 53 13.3 64 41 16.6 176.0 + EIL4 A 49805S002 P Eielson AFB, UNITED ST 212 53 13.1 64 41 16.4 177.0 + FAIR A 40408M001 P FAIRBANKS, UNITED STAT 212 30 2.7 64 58 40.8 319.0 + FAIV A 40408M005 P FAIRBANKS, UNITED STAT 212 30 5.3 64 58 41.3 324.0 + FALE A 50601S001 P FALEOLO AIRPORT/SAMOA, 188 00 1.7 -13 49 56.0 48.0 + GCGO A 40408M006 P FAIRBANKS, UNITED STAT 212 30 2.0 64 58 41.0 319.0 + GMSD A 21749S002 P Tanegashima Island - G 131 00 56.0 30 33 23.2 142.0 + GOL2 A 40405S031 P GOLDSTONE, UNITED STAT 243 06 38.7 35 25 30.6 987.0 + GOLD A 40405S031 P GOLDSTONE, UNITED STAT 243 06 38.7 35 25 30.6 987.0 + HAAS A 50237M001 P Haast, NEW ZEALAND 168 47 8.0 -44 04 23.5 1054.0 + HER2 A 40522M001 P Hermosillo, MEXICO 249 01 58.0 29 05 33.2 187.0 + HIKB A 50225M001 P Hicks bay, NEW ZEALAND 178 18 12.0 -37 33 39.7 107.0 + HOKI A 50211M001 P Hokitika, NEW ZEALAND 170 59 3.5 -42 42 46.5 54.0 + IQQE A 41708S002 P Iquique, CHILE 289 52 5.9 -20 16 24.7 39.0 + ISTA A 20807M001 P Istanbul, TURKEY 29 01 9.6 41 06 16.0 147.0 + JNU1 A 49519S001 P JUNEAU, ALASKA, UNITED 225 24 51.5 58 21 45.3 15.0 + KEN1 A 49995S001 P Kenai - Alaska, UNITED 208 38 59.4 60 40 30.3 56.0 + KEN5 A 49995M001 P Kenai - Alaska, UNITED 208 38 59.4 60 40 30.3 56.0 + KGNI A 21704S005 P KOGANEI, JAPAN 139 29 17.2 35 42 37.2 124.0 + KSMV A 21701S007 P KASHIMA, JAPAN 140 39 27.6 35 57 19.3 58.0 + LEXA A 50236M001 P Alexandra, NEW ZEALAND 169 18 29.7 -45 13 51.7 332.0 + LHCL A 41518S001 P Lihue Calel, ARGENTINA 294 24 17.1 -38 00 9.6 405.0 + LKTA A 50233M001 P Lake Taylor, NEW ZEALA 172 15 58.8 -42 47 0.1 713.0 + LPGS A 41510M001 P La Plata, ARGENTINA 302 04 3.7 -34 54 24.3 30.0 + LPIL A 92732S001 P WE-LIFOU, FRANCE 167 15 49.6 -20 55 4.8 73.0 + MAC1 A 50135M001 P Macquarie Island, AUST 158 56 9.0 -54 29 58.3 -7.0 + MAHO A 50229M001 P Mahoenui, NEW ZEALAND 174 51 14.7 -38 30 46.8 303.0 + MANA A 41201S001 P Managua, NICARAGUA 273 45 3.6 12 08 56.2 71.0 + MGUE A 41558M001 P Malargue, ARGENTINA 290 36 7.5 -35 46 38.5 1554.0 + MIZU A 21702M002 P MIZUSAWA, JAPAN 141 07 58.2 39 08 6.6 117.0 + MONP A 40497M004 P Monument Peak, UNITED 243 34 39.6 32 53 31.0 1843.0 + MPL2 A 41544M001 P Universidad de Mar del 302 25 43.3 -38 00 20.8 54.0 + MQZG A 50214M001 P Christchurch, NEW ZEAL 172 39 17.0 -43 42 9.9 155.0 + MRL1 A 50254S001 P Marlbourough, NEW ZEAL 173 44 26.7 -41 34 34.3 147.0 + MTKA A 21741S002 P MITAKA, JAPAN 139 33 41.0 35 40 46.3 109.0 + NCKU A 23606M001 P Tainan, TAIWAN (PROVIN 120 13 21.3 22 59 48.0 98.0 + NESA A 41546M001 P General Conesa, ARGENT 295 32 45.6 -40 06 19.4 83.0 + NIEB A 49269M001 P Niebla, UNITED STATES 286 35 56.0 -39 52 7.5 58.0 + NIUM A 50210M001 P Niue Island, NEW ZEALA 190 04 22.6 -19 04 35.5 90.0 + NTUS A 22601M001 P Nanyang Technological 103 40 47.9 1 20 44.9 75.0 + OAFA A 41508M001 P San Juan, ARGENTINA 291 22 36.2 -31 30 31.2 727.0 + OUS2 A 50212M002 P Dunedin, NEW ZEALAND 170 30 39.3 -45 52 10.1 26.0 + P101 A 21753S001 P P-Oshoro - Hokkaido, J 140 51 29.9 43 12 34.3 38.0 + P475 A 49886M003 P POINT LOMA, UNITED STA 242 45 21.9 32 39 59.0 -25.0 + PBR2 A 22308M002 P Port Blair, INDIA 92 42 43.7 11 38 16.0 -23.0 + PBRI A 22308M001 P Port Blair, INDIA 92 42 43.7 11 38 16.0 -23.0 + PIN1 A 40407M003 P PINYON FLATS, UNITED S 243 32 30.6 33 36 43.8 1256.0 + PLO5 A 49886M001 P POINT LOMA, UNITED STA 242 45 25.1 32 39 55.5 -23.0 + PLO6 A 49886M002 P POINT LOMA, UNITED STA 242 45 24.6 32 39 56.2 -22.0 + PRMI A 82002M001 P Magueyes Island, PUERT 292 57 16.7 17 58 13.4 -25.0 + PYGR A 50240M001 P Puysegur Point, NEW ZE 166 40 50.7 -46 09 58.2 253.0 + QUI3 A 42003S004 P Quito III, ECUADOR 281 31 57.8 0 08 22.5 2927.0 + RAUL A 50257M001 P Raoul Island, NEW ZEAL 182 04 15.8 -29 14 40.8 92.0 + REYK A 10202M001 P Reykjavik, ICELAND 338 02 40.3 64 08 19.6 93.0 + REYZ A 10202M003 P Reykjavik, ICELAND 338 02 40.3 64 08 19.6 93.0 + RIOP A 42006M001 P Rio Bamba, ECUADOR 281 20 56.0 -1 39 2.2 2817.0 + SAMO A 50603M001 P APIA, WESTERN SAMOA 188 15 41.7 -13 50 57.2 77.0 + SAMP A 23104M001 P Medan, INDONESIA 98 42 53.0 3 37 17.8 2.0 + SAN0 A 44466M001 P SanAndresCOL2007, COLO 278 17 3.4 12 34 49.6 10.0 + SANT A 41705M003 P Santiago, CHILE 289 19 53.2 -33 09 1.0 723.0 + SCIP A 49915M001 P SAN CLEMENTE ISLAND, U 241 30 43.5 32 54 51.9 453.0 + SEAT A 40457M002 P Seattle, UNITED STATES 237 41 25.9 47 39 14.3 44.0 + SIN1 A 22601M003 P Nanyang Technological 103 40 46.0 1 20 34.7 92.0 + SIO3 A 40460M004 P LA JOLLA/SCRIPPS, UNIT 242 44 58.6 32 51 52.9 35.0 + SIO5 A 40460M006 P LA JOLLA/SCRIPPS, UNIT 242 45 1.1 32 50 26.6 186.0 + SMST A 21726M001 P Simosato, JAPAN 135 56 13.0 33 34 40.3 98.0 + SNLR A 42021M001 P San Lorenzo, ECUADOR 281 09 10.8 1 17 33.1 23.0 + SOLO A 51202M001 P Solomon Islands, SOLOM 159 57 15.6 -9 26 5.7 123.0 + SPAN A 12628M001 P Spanochori, GREECE 20 40 25.1 38 46 52.7 451.0 + SPW2 A 40460M007 P LA JOLLA/SCRIPPS, UNIT 242 44 34.5 32 52 0.9 -21.0 + SSIA A 41401S001 P San Salvador, EL SALVA 270 53 0.2 13 41 49.5 627.0 + STK2 A 21731S004 P SHINTOTSUKAWA, JAPAN 141 50 41.3 43 31 43.1 119.0 + SUWN A 23903M001 P Suwon-Shi, SOUTH KOREA 127 03 15.3 37 16 31.9 82.0 + TONG A 50902M001 P Nuku Alofa, TONGA 184 49 14.8 -21 08 41.0 56.0 + TSEA A 49448S001 P The Surveyors Exchange 210 06 18.1 61 11 14.4 43.0 + TSK2 A 21730S010 P Tsukuba, JAPAN 140 05 13.6 36 06 20.1 70.0 + TSKB A 21730S005 P Tsukuba, JAPAN 140 05 15.0 36 06 20.4 67.0 + TUBI A 20806M001 P Gebze, TURKEY 29 27 2.4 40 47 12.2 220.0 + TUVA A 51101M001 P FUNAFUTI, TUVALU 179 11 47.6 -8 31 31.0 38.0 + USUD A 21729S007 P USUDA, JAPAN 138 21 43.4 36 07 59.2 1509.0 + VALP A 41712S001 P Valparaiso, CHILE 288 22 26.1 -33 01 38.1 31.0 + VBCA A 41512M001 P Bahia Blanca, ARGENTIN 297 43 50.8 -38 42 2.8 59.0 + WAIM A 50239M001 P Waimate, NEW ZEALAND 170 55 13.1 -44 39 20.5 1045.0 + WANG A 50228M001 P Wanganui, NEW ZEALAND 174 49 17.2 -39 47 12.8 290.0 + WEST A 50238M001 P 288 30 24.1 42 36 48.0 85.0 + WGTN A 50208M003 P Wellington, NEW ZEALAN 174 48 21.2 -41 19 24.5 26.0 + WGTT A 50208S004 P Wellington, NEW ZEALAN 174 46 53.8 -41 17 25.6 43.0 + WIDC A 49917M001 P WIDE CANYON, UNITED ST 243 36 29.6 33 56 5.1 445.0 + YSSK A 12329M003 P Yuzhno-Sakhalinsk, RUS 142 43 0.2 47 01 47.1 91.0 +-SITE/ID +*------------------------------------------------------------------------------- ++SOLUTION/ESTIMATE +*INDEX _TYPE_ CODE PT SOLN _REF_EPOCH__ UNIT S ___ESTIMATED_VALUE___ __STD_DEV__ + 1 AEXP_N AB01 A ---- 13:242:59103 m 2 -1.16779196624443e-02 8.12623e-04 + 2 TEXP_N AB01 A ---- 13:242:59103 m 2 5.02510982822891e-01 6.01958e-02 + 3 AEXP_N AB01 A ---- 16:072:65204 m 2 -1.31981162574364e-02 1.42146e-03 + 4 TEXP_N AB01 A ---- 16:072:65204 m 2 1.02131561331021e+00 1.47387e-01 + 5 AEXP_N AB02 A ---- 15:208:17386 m 2 -1.41009708499839e-02 6.91517e-04 + 6 TEXP_N AB02 A ---- 15:208:17386 m 2 1.30193895176138e+00 8.66602e-02 + 7 AEXP_E AB07 A ---- 20:204:22364 m 2 1.50471482248693e-02 9.48067e-04 + 8 TEXP_E AB07 A ---- 20:204:22364 m 2 4.94574258665434e-02 6.53701e-03 + 9 AEXP_N AB07 A ---- 20:204:22364 m 2 -2.28126993470868e-02 1.05259e-03 + 10 TEXP_N AB07 A ---- 20:204:22364 m 2 3.87312480106851e-02 3.60616e-03 + 11 ALOG_E AB48 A ---- 13:005:32298 m 2 -8.41718942162843e-03 2.19183e-04 + 12 TLOG_E AB48 A ---- 13:005:32298 m 2 8.97690478558904e-01 6.65769e-02 + 13 AEXP_N AB48 A ---- 13:005:32298 m 2 -6.91036154776120e-02 3.42009e-04 + 14 TEXP_N AB48 A ---- 13:005:32298 m 2 4.75872294590976e+00 7.81317e-02 + 15 AEXP_N AB48 A ---- 13:005:32298 m 2 -1.76100887366745e-02 4.36829e-04 + 16 TEXP_N AB48 A ---- 13:005:32298 m 2 2.95360881043234e-01 1.39299e-02 + 17 AEXP_U AB48 A ---- 13:005:32298 m 2 3.27061906213452e-02 6.51215e-04 + 18 TEXP_U AB48 A ---- 13:005:32298 m 2 2.90599109963509e+00 1.32069e-01 + 19 ALOG_N AB51 A ---- 13:005:32298 m 2 -6.95910593809211e-03 5.94304e-04 + 20 TLOG_N AB51 A ---- 13:005:32298 m 2 6.54241732712969e-01 9.82667e-02 + 21 AEXP_E AC25 A ---- 20:204:22364 m 2 1.35592319013603e-02 9.23807e-04 + 22 TEXP_E AC25 A ---- 20:204:22364 m 2 8.69427244353046e-02 1.71155e-02 + 23 AEXP_N AC25 A ---- 20:204:22364 m 2 -8.08303061745013e-03 1.49761e-03 + 24 TEXP_N AC25 A ---- 20:204:22364 m 2 1.14685739854586e-01 5.72800e-02 + 25 AEXP_N AC40 A ---- 20:204:22364 m 2 -1.00414288064987e-02 1.04323e-03 + 26 TEXP_N AC40 A ---- 20:204:22364 m 2 1.33923059688045e-01 3.43895e-02 + 27 AEXP_E AC60 A ---- 14:174:75189 m 2 -1.60960440694269e-02 2.34334e-03 + 28 TEXP_E AC60 A ---- 14:174:75189 m 2 1.38749235042499e+00 1.80832e-01 + 29 AEXP_E AIS5 A ---- 13:005:32298 m 2 5.74313550982342e-03 2.61418e-04 + 30 TEXP_E AIS5 A ---- 13:005:32298 m 2 1.92879876356458e-01 1.42807e-02 + 31 ALOG_N AIS5 A ---- 12:302:11048 m 2 -5.08976395960290e-03 1.97177e-04 + 32 TLOG_N AIS5 A ---- 12:302:11048 m 2 1.13081722790527e-01 2.15637e-02 + 33 ALOG_E ANTC A ---- 10:058:23656 m 2 -1.28699198121674e-01 5.41878e-04 + 34 TLOG_E ANTC A ---- 10:058:23656 m 2 8.08455225832410e-01 1.81199e-02 + 35 ALOG_E ANTC A ---- 10:058:23656 m 2 -3.56937459818481e-02 5.17885e-04 + 36 TLOG_E ANTC A ---- 10:058:23656 m 2 3.53677247694474e-03 3.68902e-04 + 37 ALOG_N ANTC A ---- 10:058:23656 m 2 8.76938710916818e-02 2.36697e-02 + 38 TLOG_N ANTC A ---- 10:058:23656 m 2 6.74719810025941e+00 1.67398e+00 + 39 ALOG_N ANTC A ---- 10:058:23656 m 2 1.23511869822841e-02 3.69955e-04 + 40 TLOG_N ANTC A ---- 10:058:23656 m 2 1.72868196121241e-02 2.63439e-03 + 41 ALOG_U ANTC A ---- 10:058:23656 m 2 5.50435552310340e-02 9.52067e-04 + 42 TLOG_U ANTC A ---- 10:058:23656 m 2 4.70992312239571e-01 1.92304e-02 + 43 ALOG_E AREG A ---- 01:174:73993 m 2 -1.63377151539946e-01 1.85315e-02 + 44 TLOG_E AREG A ---- 01:174:73993 m 2 8.15228736135023e+00 1.23120e+00 + 45 ALOG_E AREG A ---- 01:188:34724 m 2 -2.46405451514933e-02 1.07423e-03 + 46 TLOG_E AREG A ---- 01:188:34724 m 2 7.90000693982516e-02 9.87862e-03 + 47 AEXP_N AREG A ---- 01:174:73993 m 2 -7.86715766808174e-02 3.88833e-04 + 48 TEXP_N AREG A ---- 01:174:73993 m 2 3.30459757701857e+00 4.42993e-02 + 49 AEXP_N AREG A ---- 01:188:34724 m 2 -3.06953318132498e-02 3.74570e-04 + 50 TEXP_N AREG A ---- 01:188:34724 m 2 2.73260436107906e-01 6.54607e-03 + 51 ALOG_U AREG A ---- 01:174:73993 m 2 5.30786405941775e-02 5.14331e-03 + 52 TLOG_U AREG A ---- 01:174:73993 m 2 3.30433462394161e+00 3.37011e-01 + 53 ALOG_E AREQ A ---- 01:174:73993 m 2 -1.63377151539946e-01 1.85315e-02 + 54 TLOG_E AREQ A ---- 01:174:73993 m 2 8.15228736135023e+00 1.23120e+00 + 55 ALOG_E AREQ A ---- 01:188:34724 m 2 -2.46405451514933e-02 1.07423e-03 + 56 TLOG_E AREQ A ---- 01:188:34724 m 2 7.90000693982516e-02 9.87862e-03 + 57 AEXP_N AREQ A ---- 01:174:73993 m 2 -7.86715766808174e-02 3.88833e-04 + 58 TEXP_N AREQ A ---- 01:174:73993 m 2 3.30459757701857e+00 4.42993e-02 + 59 AEXP_N AREQ A ---- 01:188:34724 m 2 -3.06953318132498e-02 3.74570e-04 + 60 TEXP_N AREQ A ---- 01:188:34724 m 2 2.73260436107906e-01 6.54607e-03 + 61 ALOG_U AREQ A ---- 01:174:73993 m 2 5.30786405941775e-02 5.14331e-03 + 62 TLOG_U AREQ A ---- 01:174:73993 m 2 3.30433462394161e+00 3.37011e-01 + 63 AEXP_E ASPA A ---- 09:272:64091 m 2 4.26377729769802e-02 5.34954e-03 + 64 TEXP_E ASPA A ---- 09:272:64091 m 2 5.34854267417561e+00 5.26988e-01 + 65 AEXP_N ASPA A ---- 09:272:64091 m 2 1.41837183356163e-02 2.91310e-04 + 66 TEXP_N ASPA A ---- 09:272:64091 m 2 1.10601898610024e+00 5.20701e-02 + 67 ALOG_U ASPA A ---- 09:272:64091 m 2 -2.99046472861597e-02 1.24737e-03 + 68 TLOG_U ASPA A ---- 09:272:64091 m 2 2.80362744315544e-01 3.21738e-02 + 69 ALOG_E BLUF A ---- 09:196:33748 m 2 -8.14283171520257e-03 2.28942e-04 + 70 TLOG_E BLUF A ---- 09:196:33748 m 2 1.38835312893239e-01 1.14146e-02 + 71 AEXP_E BLYT A ---- 99:289:35203 m 2 6.30222583667247e-03 5.35258e-04 + 72 TEXP_E BLYT A ---- 99:289:35203 m 2 2.08718079861012e-01 2.53077e-02 + 73 AEXP_N BLYT A ---- 10:094:81643 m 2 -1.14854855239444e-02 1.09519e-04 + 74 TEXP_N BLYT A ---- 10:094:81643 m 2 1.74654121497535e+00 3.13943e-02 + 75 AEXP_E CARI A ---- 04:361:03529 m 2 -7.26346879025971e-02 6.61264e-03 + 76 TEXP_E CARI A ---- 04:361:03529 m 2 6.19317579031767e-02 5.72645e-03 + 77 ALOG_E CARI A ---- 04:361:03529 m 2 -1.31490633736411e-01 5.60012e-04 + 78 TLOG_E CARI A ---- 04:361:03529 m 2 1.31820577316724e-01 4.53474e-03 + 79 ALOG_N CARI A ---- 04:361:03529 m 2 -4.26475264671735e-02 3.00536e-04 + 80 TLOG_N CARI A ---- 04:361:03529 m 2 8.75490449846941e-02 3.45158e-03 + 81 AEXP_N CARI A ---- 12:102:31117 m 2 -6.22368004219217e-03 4.65315e-04 + 82 TEXP_N CARI A ---- 12:102:31117 m 2 3.45917517370473e-01 3.85523e-02 + 83 ALOG_U CARI A ---- 04:361:03529 m 2 8.08827565808557e-02 1.02206e-03 + 84 TLOG_U CARI A ---- 04:361:03529 m 2 1.59390727278267e-01 8.17566e-03 + 85 AEXP_E CHAN A ---- 11:070:20783 m 2 9.32133597351287e-03 1.90012e-04 + 86 TEXP_E CHAN A ---- 11:070:20783 m 2 6.54859284423687e-01 2.71375e-02 + 87 AEXP_N CHAN A ---- 11:070:20783 m 2 -3.38254602287907e-03 2.25255e-04 + 88 TEXP_N CHAN A ---- 11:070:20783 m 2 5.43323654414235e-01 7.13400e-02 + 89 AEXP_E CHOF A ---- 11:070:20783 m 2 1.65691745929846e-01 1.45094e-03 + 90 TEXP_E CHOF A ---- 11:070:20783 m 2 1.92108672151311e+00 3.01938e-02 + 91 AEXP_E CHTI A ---- 16:318:39779 m 2 -2.10524918726746e-03 2.01591e-04 + 92 TEXP_E CHTI A ---- 16:318:39779 m 2 3.91927309440284e-01 8.46402e-02 + 93 ALOG_E CKSV A ---- 16:036:71846 m 2 -6.87475117620020e-03 1.16181e-04 + 94 TLOG_E CKSV A ---- 16:036:71846 m 2 6.28974457411865e-02 5.96793e-03 + 95 AEXP_N CKSV A ---- 16:036:71846 m 2 -9.06550237069407e-03 3.08653e-04 + 96 TEXP_N CKSV A ---- 16:036:71846 m 2 1.68162816390947e+00 7.38004e-02 + 97 AEXP_U CKSV A ---- 16:036:71846 m 2 2.00027895593370e-02 7.93826e-04 + 98 TEXP_U CKSV A ---- 16:036:71846 m 2 3.70346798757153e-01 2.30799e-02 + 99 AEXP_E CLGO A ---- 02:307:79960 m 2 1.87948882705854e-02 1.07674e-03 + 100 TEXP_E CLGO A ---- 02:307:79960 m 2 8.41081281232693e+00 5.34547e-01 + 101 AEXP_N CLGO A ---- 02:307:79960 m 2 -2.39609301591528e-02 5.60637e-04 + 102 TEXP_N CLGO A ---- 02:307:79960 m 2 4.90698853509284e+00 2.64859e-01 + 103 AEXP_E COCO A ---- 00:170:53052 m 2 1.48964027214767e-02 7.30275e-04 + 104 TEXP_E COCO A ---- 00:170:53052 m 2 1.23337723429429e+00 8.49021e-02 + 105 ALOG_N COCO A ---- 07:255:40226 m 2 3.37739433950275e-03 2.57960e-04 + 106 TLOG_N COCO A ---- 07:255:40226 m 2 5.39472789286760e-01 9.20007e-02 + 107 AEXP_N COCO A ---- 12:102:31117 m 2 6.51441881991903e-03 1.76123e-04 + 108 TEXP_N COCO A ---- 12:102:31117 m 2 5.45445623436705e-01 2.70103e-02 + 109 ALOG_E CONT A ---- 10:058:23656 m 2 -6.10300623717498e-02 3.00548e-04 + 110 TLOG_E CONT A ---- 10:058:23656 m 2 3.56478668973935e-02 1.19429e-03 + 111 AEXP_E CONT A ---- 11:042:72331 m 2 -9.22465174518881e-03 2.88221e-04 + 112 TEXP_E CONT A ---- 11:042:72331 m 2 3.58184887232744e-01 1.55895e-02 + 113 AEXP_N CONT A ---- 10:058:23656 m 2 -1.13984482974666e-02 5.11389e-04 + 114 TEXP_N CONT A ---- 10:058:23656 m 2 9.13179087857422e-01 4.85619e-02 + 115 ALOG_E CONZ A ---- 10:058:23656 m 2 -6.10300623717498e-02 3.00548e-04 + 116 TLOG_E CONZ A ---- 10:058:23656 m 2 3.56478668973935e-02 1.19429e-03 + 117 AEXP_E CONZ A ---- 11:042:72331 m 2 -9.22465174518881e-03 2.88221e-04 + 118 TEXP_E CONZ A ---- 11:042:72331 m 2 3.58184887232744e-01 1.55895e-02 + 119 AEXP_N CONZ A ---- 10:058:23656 m 2 -1.13984482974666e-02 5.11389e-04 + 120 TEXP_N CONZ A ---- 10:058:23656 m 2 9.13179087857422e-01 4.85619e-02 + 121 ALOG_E COPO A ---- 06:120:69437 m 2 -1.83174479819687e-03 1.15694e-04 + 122 TLOG_E COPO A ---- 06:120:69437 m 2 8.42099270541371e-03 7.10115e-03 + 123 ALOG_E CORD A ---- 15:259:82472 m 2 -1.18122756349830e-02 4.53783e-04 + 124 TLOG_E CORD A ---- 15:259:82472 m 2 1.57993064424335e+00 1.13355e-01 + 125 AEXP_E COSO A ---- 01:198:43645 m 2 6.86133743444658e-03 1.80252e-04 + 126 TEXP_E COSO A ---- 01:198:43645 m 2 1.04389139211378e+00 5.98629e-02 + 127 AEXP_E COSO A ---- 19:187:11992 m 2 -1.03550233136461e-02 9.29795e-04 + 128 TEXP_E COSO A ---- 19:187:11992 m 2 4.48693426523531e-02 9.04730e-03 + 129 AEXP_N CUSV A ---- 12:102:31117 m 2 1.06645096938545e-02 8.04643e-04 + 130 TEXP_N CUSV A ---- 12:102:31117 m 2 1.12420099357108e+00 1.48883e-01 + 131 AEXP_N CUUT A ---- 12:102:31117 m 2 1.06645096938545e-02 8.04643e-04 + 132 TEXP_N CUUT A ---- 12:102:31117 m 2 1.12420099357108e+00 1.48883e-01 + 133 ALOG_E DAE2 A ---- 11:070:20783 m 2 5.82719379513263e-03 1.55960e-04 + 134 TLOG_E DAE2 A ---- 11:070:20783 m 2 2.31661299628201e-01 1.88431e-02 + 135 ALOG_E DAEJ A ---- 11:070:20783 m 2 5.82719379513263e-03 1.55960e-04 + 136 TLOG_E DAEJ A ---- 11:070:20783 m 2 2.31661299628201e-01 1.88431e-02 + 137 ALOG_E DGAR A ---- 04:361:03529 m 2 4.29628105449896e-03 3.77402e-04 + 138 TLOG_E DGAR A ---- 04:361:03529 m 2 8.05220193610294e-01 1.29314e-01 + 139 AEXP_N DHLG A ---- 10:094:81643 m 2 -1.70476063408427e-02 2.60882e-04 + 140 TEXP_N DHLG A ---- 10:094:81643 m 2 2.80193999645357e+00 6.05137e-02 + 141 AEXP_E DUND A ---- 09:196:33748 m 2 -1.39948108370084e-02 2.51320e-04 + 142 TEXP_E DUND A ---- 09:196:33748 m 2 1.97144147818045e+00 4.69680e-02 + 143 AEXP_E DUND A ---- 09:196:33748 m 2 -6.05458076217277e-03 4.96380e-04 + 144 TEXP_E DUND A ---- 09:196:33748 m 2 3.60184970242054e-02 4.99204e-03 + 145 AEXP_N DUND A ---- 16:318:39776 m 2 -4.27465099241506e-03 1.21671e-04 + 146 TEXP_N DUND A ---- 16:318:39776 m 2 1.06923207405804e+00 8.96452e-02 + 147 AEXP_E EIL1 A ---- 02:307:79960 m 2 1.45221361794283e-02 3.55130e-04 + 148 TEXP_E EIL1 A ---- 02:307:79960 m 2 4.33280181313848e-01 1.76047e-02 + 149 AEXP_N EIL1 A ---- 02:307:79960 m 2 -1.79151534076978e-02 3.13410e-04 + 150 TEXP_N EIL1 A ---- 02:307:79960 m 2 6.39780944748958e-01 2.06861e-02 + 151 AEXP_E EIL4 A ---- 02:307:79960 m 2 1.45221361794283e-02 3.55130e-04 + 152 TEXP_E EIL4 A ---- 02:307:79960 m 2 4.33280181313848e-01 1.76047e-02 + 153 AEXP_N EIL4 A ---- 02:307:79960 m 2 -1.79151534076978e-02 3.13410e-04 + 154 TEXP_N EIL4 A ---- 02:307:79960 m 2 6.39780944748958e-01 2.06861e-02 + 155 AEXP_E FAIR A ---- 02:307:79960 m 2 2.12503693744213e-02 3.88467e-04 + 156 TEXP_E FAIR A ---- 02:307:79960 m 2 4.51558523231202e+00 1.31928e-01 + 157 AEXP_E FAIR A ---- 02:307:79960 m 2 7.84312761941661e-03 5.77433e-04 + 158 TEXP_E FAIR A ---- 02:307:79960 m 2 1.09970165203688e-01 1.17786e-02 + 159 AEXP_N FAIR A ---- 02:307:79960 m 2 -3.12705450069609e-02 4.81388e-04 + 160 TEXP_N FAIR A ---- 02:307:79960 m 2 6.38702673406978e+00 1.48599e-01 + 161 AEXP_N FAIR A ---- 02:307:79960 m 2 -1.79114651078224e-02 2.71994e-04 + 162 TEXP_N FAIR A ---- 02:307:79960 m 2 4.96320024711895e-01 1.58954e-02 + 163 AEXP_E FAIV A ---- 02:307:79960 m 2 2.12503693744213e-02 3.88467e-04 + 164 TEXP_E FAIV A ---- 02:307:79960 m 2 4.51558523231202e+00 1.31928e-01 + 165 AEXP_E FAIV A ---- 02:307:79960 m 2 7.84312761941661e-03 5.77433e-04 + 166 TEXP_E FAIV A ---- 02:307:79960 m 2 1.09970165203688e-01 1.17786e-02 + 167 AEXP_N FAIV A ---- 02:307:79960 m 2 -3.12705450069609e-02 4.81388e-04 + 168 TEXP_N FAIV A ---- 02:307:79960 m 2 6.38702673406978e+00 1.48599e-01 + 169 AEXP_N FAIV A ---- 02:307:79960 m 2 -1.79114651078224e-02 2.71994e-04 + 170 TEXP_N FAIV A ---- 02:307:79960 m 2 4.96320024711895e-01 1.58954e-02 + 171 AEXP_E FALE A ---- 09:272:64091 m 2 -3.95141099654387e-02 2.79134e-03 + 172 TEXP_E FALE A ---- 09:272:64091 m 2 7.06614615485477e-01 3.63732e-02 + 173 ALOG_E FALE A ---- 09:272:64091 m 2 3.29909622831306e-02 1.75403e-03 + 174 TLOG_E FALE A ---- 09:272:64091 m 2 1.76012810735029e-01 1.59434e-02 + 175 ALOG_U FALE A ---- 09:272:64091 m 2 -1.31898206677412e-02 1.74143e-03 + 176 TLOG_U FALE A ---- 09:272:64091 m 2 1.95051863456445e-01 6.38362e-02 + 177 AEXP_E GCGO A ---- 02:307:79960 m 2 2.12503693744213e-02 3.88467e-04 + 178 TEXP_E GCGO A ---- 02:307:79960 m 2 4.51558523231202e+00 1.31928e-01 + 179 AEXP_E GCGO A ---- 02:307:79960 m 2 7.84312761941661e-03 5.77433e-04 + 180 TEXP_E GCGO A ---- 02:307:79960 m 2 1.09970165203688e-01 1.17786e-02 + 181 AEXP_N GCGO A ---- 02:307:79960 m 2 -3.12705450069609e-02 4.81388e-04 + 182 TEXP_N GCGO A ---- 02:307:79960 m 2 6.38702673406978e+00 1.48599e-01 + 183 AEXP_N GCGO A ---- 02:307:79960 m 2 -1.79114651078224e-02 2.71994e-04 + 184 TEXP_N GCGO A ---- 02:307:79960 m 2 4.96320024711895e-01 1.58954e-02 + 185 AEXP_E GMSD A ---- 04:361:03529 m 2 -1.22238510109119e-02 3.36691e-04 + 186 TEXP_E GMSD A ---- 04:361:03529 m 2 7.12517714994782e-01 4.79208e-02 + 187 AEXP_E GMSD A ---- 19:008:45570 m 2 8.09556003985641e-03 6.79653e-04 + 188 TEXP_E GMSD A ---- 19:008:45570 m 2 6.39552272993025e-02 8.78995e-03 + 189 AEXP_N GMSD A ---- 04:361:03529 m 2 1.02829260619976e-02 2.84970e-04 + 190 TEXP_N GMSD A ---- 04:361:03529 m 2 6.46535169705458e-01 4.19936e-02 + 191 AEXP_N GMSD A ---- 19:008:45570 m 2 -6.84765007505096e-03 5.66458e-04 + 192 TEXP_N GMSD A ---- 19:008:45570 m 2 5.89449780254912e-02 7.85938e-03 + 193 AEXP_E GOL2 A ---- 99:289:35203 m 2 -1.53946625205506e-02 2.90099e-04 + 194 TEXP_E GOL2 A ---- 99:289:35203 m 2 4.85170063973629e+00 2.11109e-01 + 195 AEXP_E GOL2 A ---- 19:187:11992 m 2 1.04314072843861e-02 1.65489e-04 + 196 TEXP_E GOL2 A ---- 19:187:11992 m 2 4.63864338352515e-01 2.18072e-02 + 197 AEXP_E GOLD A ---- 99:289:35203 m 2 -1.53946625205506e-02 2.90099e-04 + 198 TEXP_E GOLD A ---- 99:289:35203 m 2 4.85170063973629e+00 2.11109e-01 + 199 AEXP_E GOLD A ---- 19:187:11992 m 2 1.04314072843861e-02 1.65489e-04 + 200 TEXP_E GOLD A ---- 19:187:11992 m 2 4.63864338352515e-01 2.18072e-02 + 201 AEXP_E HAAS A ---- 09:196:33748 m 2 -1.00830430826542e-02 3.51263e-04 + 202 TEXP_E HAAS A ---- 09:196:33748 m 2 2.68196343415718e+00 1.43187e-01 + 203 AEXP_E HER2 A ---- 12:103:26148 m 2 6.99980016752539e-03 1.79563e-04 + 204 TEXP_E HER2 A ---- 12:103:26148 m 2 9.79819199063344e-01 6.29572e-02 + 205 ALOG_E HIKB A ---- 16:245:59877 m 2 2.17708273767376e-03 2.09954e-04 + 206 TLOG_E HIKB A ---- 16:245:59877 m 2 4.69807636703863e-03 7.03124e-03 + 207 AEXP_E HOKI A ---- 16:318:39776 m 2 2.16271397786833e-02 3.57362e-04 + 208 TEXP_E HOKI A ---- 16:318:39776 m 2 1.87207813679143e+00 7.29345e-02 + 209 AEXP_N HOKI A ---- 04:358:53944 m 2 4.57662529921057e-03 1.91766e-04 + 210 TEXP_N HOKI A ---- 04:358:53944 m 2 1.75861959077594e+00 2.08904e-01 + 211 AEXP_U HOKI A ---- 16:318:39776 m 2 9.37497240823839e-03 5.87416e-04 + 212 TEXP_U HOKI A ---- 16:318:39776 m 2 1.06976027427718e+00 2.04507e-01 + 213 AEXP_E IQQE A ---- 14:091:85607 m 2 -4.22583977039182e-02 1.18661e-03 + 214 TEXP_E IQQE A ---- 14:091:85607 m 2 2.51095319590381e-02 1.22186e-03 + 215 ALOG_E IQQE A ---- 14:091:85607 m 2 -3.89057163904281e-02 3.08767e-04 + 216 TLOG_E IQQE A ---- 14:091:85607 m 2 1.23452489823994e-01 4.94790e-03 + 217 ALOG_N IQQE A ---- 14:091:85607 m 2 -4.57559836330520e-03 1.03849e-04 + 218 TLOG_N IQQE A ---- 14:091:85607 m 2 4.46214742712238e-02 6.45470e-03 + 219 ALOG_U IQQE A ---- 14:091:85607 m 2 -4.76125704838974e-03 2.10116e-04 + 220 TLOG_U IQQE A ---- 14:091:85607 m 2 3.99639823544764e-03 4.97608e-03 + 221 ALOG_E ISTA A ---- 99:229:00098 m 2 1.64568220286954e-02 3.80779e-04 + 222 TLOG_E ISTA A ---- 99:229:00098 m 2 1.06747114607428e+00 5.82935e-02 + 223 ALOG_N ISTA A ---- 99:229:00098 m 2 -8.98469869585909e-03 2.72157e-04 + 224 TLOG_N ISTA A ---- 99:229:00098 m 2 4.12192110312979e-01 5.44826e-02 + 225 AEXP_N JNU1 A ---- 13:005:32298 m 2 -6.11836359612222e-03 3.23277e-04 + 226 TEXP_N JNU1 A ---- 13:005:32298 m 2 9.97855305503151e-01 1.04388e-01 + 227 AEXP_E KEN1 A ---- 02:307:79960 m 2 1.63854395804193e-02 1.37003e-02 + 228 TEXP_E KEN1 A ---- 02:307:79960 m 2 1.42122257370940e-01 7.18545e-02 + 229 AEXP_N KEN1 A ---- 02:307:79960 m 2 -1.69296489748164e-02 2.31652e-03 + 230 TEXP_N KEN1 A ---- 02:307:79960 m 2 6.55094408216612e-01 1.67484e-01 + 231 AEXP_E KEN5 A ---- 02:307:79960 m 2 1.63854395804193e-02 1.37003e-02 + 232 TEXP_E KEN5 A ---- 02:307:79960 m 2 1.42122257370940e-01 7.18545e-02 + 233 AEXP_N KEN5 A ---- 02:307:79960 m 2 -1.69296489748164e-02 2.31652e-03 + 234 TEXP_N KEN5 A ---- 02:307:79960 m 2 6.55094408216612e-01 1.67484e-01 + 235 AEXP_E KGNI A ---- 11:070:20783 m 2 -6.03640065245365e-02 1.40468e-03 + 236 TEXP_E KGNI A ---- 11:070:20783 m 2 2.81110451432773e-01 5.72751e-03 + 237 ALOG_E KGNI A ---- 11:070:20783 m 2 7.25063626044340e-02 2.70022e-04 + 238 TLOG_E KGNI A ---- 11:070:20783 m 2 4.48163492142826e-02 2.36732e-03 + 239 AEXP_N KGNI A ---- 11:070:20783 m 2 -1.44933690345401e-01 1.63677e-01 + 240 TEXP_N KGNI A ---- 11:070:20783 m 2 1.93743734321504e+01 1.24520e+01 + 241 AEXP_U KGNI A ---- 11:070:20783 m 2 2.25722531777154e-02 8.44908e-04 + 242 TEXP_U KGNI A ---- 11:070:20783 m 2 1.08576055040392e+00 9.50241e-02 + 243 ALOG_E KSMV A ---- 08:128:60320 m 2 7.73745075284647e-03 2.57638e-04 + 244 TLOG_E KSMV A ---- 08:128:60320 m 2 1.12487510786978e-01 1.71149e-02 + 245 ALOG_E KSMV A ---- 11:070:20783 m 2 3.40204748748642e-02 2.29016e-03 + 246 TLOG_E KSMV A ---- 11:070:20783 m 2 2.32033769632914e-01 5.15296e-02 + 247 ALOG_E KSMV A ---- 11:070:20783 m 2 5.47995903513174e-02 3.00932e-03 + 248 TLOG_E KSMV A ---- 11:070:20783 m 2 4.99018026328851e-03 9.45792e-04 + 249 ALOG_N KSMV A ---- 11:070:20783 m 2 -1.42276026357925e-01 5.83084e-02 + 250 TLOG_N KSMV A ---- 11:070:20783 m 2 6.21557580424269e+00 2.09047e+00 + 251 ALOG_N KSMV A ---- 11:070:20783 m 2 -2.77506083371272e-02 5.20319e-04 + 252 TLOG_N KSMV A ---- 11:070:20783 m 2 4.61547446621766e-03 7.76988e-04 + 253 ALOG_U KSMV A ---- 11:070:20783 m 2 2.23883186849131e-02 5.47254e-04 + 254 TLOG_U KSMV A ---- 11:070:20783 m 2 2.03597284966443e-02 4.29505e-03 + 255 ALOG_E LEXA A ---- 09:196:33748 m 2 -1.18688918574351e-02 3.78622e-04 + 256 TLOG_E LEXA A ---- 09:196:33748 m 2 6.45291429724167e-01 4.65727e-02 + 257 AEXP_E LHCL A ---- 10:058:23656 m 2 5.10236988417012e-02 8.26288e-04 + 258 TEXP_E LHCL A ---- 10:058:23656 m 2 1.27420971063633e+00 2.36301e-02 + 259 ALOG_E LHCL A ---- 10:058:23656 m 2 -4.30898984086126e-02 3.09802e-04 + 260 TLOG_E LHCL A ---- 10:058:23656 m 2 3.88713285133235e-01 1.11376e-02 + 261 ALOG_N LHCL A ---- 10:058:23656 m 2 2.13688661706858e-02 3.81490e-04 + 262 TLOG_N LHCL A ---- 10:058:23656 m 2 3.30605921031991e+00 1.29055e-01 + 263 ALOG_E LKTA A ---- 16:318:39776 m 2 8.00657570456689e-03 4.37859e-04 + 264 TLOG_E LKTA A ---- 16:318:39776 m 2 8.72253022681453e-02 1.63828e-02 + 265 ALOG_E LPGS A ---- 10:058:23656 m 2 -5.75920158843214e-03 6.14995e-04 + 266 TLOG_E LPGS A ---- 10:058:23656 m 2 8.19870719935022e-01 1.42248e-01 + 267 AEXP_E LPIL A ---- 07:084:02402 m 2 8.19127944982833e-03 4.41780e-04 + 268 TEXP_E LPIL A ---- 07:084:02402 m 2 5.04154957489666e-02 4.10058e-03 + 269 AEXP_E LPIL A ---- 08:100:45973 m 2 1.01508442684738e-02 5.70353e-04 + 270 TEXP_E LPIL A ---- 08:100:45973 m 2 6.57429137338623e-02 4.92302e-03 + 271 AEXP_E MAC1 A ---- 04:358:53944 m 2 -4.79663342017918e-03 1.65155e-04 + 272 TEXP_E MAC1 A ---- 04:358:53944 m 2 1.26537753481757e+00 8.69467e-02 + 273 AEXP_N MAC1 A ---- 04:358:53944 m 2 1.92441128856711e-02 6.13919e-04 + 274 TEXP_N MAC1 A ---- 04:358:53944 m 2 4.36456365580134e-01 1.48753e-02 + 275 ALOG_N MAC1 A ---- 04:358:53944 m 2 -1.28683005775746e-02 1.82064e-04 + 276 TLOG_N MAC1 A ---- 04:358:53944 m 2 7.64325269175626e-02 7.38976e-03 + 277 AEXP_N MAHO A ---- 16:318:39776 m 2 4.45298309731938e-03 5.64820e-04 + 278 TEXP_N MAHO A ---- 16:318:39776 m 2 6.25231432948617e-01 1.31735e-01 + 279 ALOG_N MANA A ---- 04:283:77214 m 2 -2.83843346266814e-02 2.73147e-03 + 280 TLOG_N MANA A ---- 04:283:77214 m 2 1.46832090796218e+00 1.66740e-01 + 281 ALOG_N MANA A ---- 12:249:52928 m 2 -8.73010450206124e-02 2.90474e-02 + 282 TLOG_N MANA A ---- 12:249:52928 m 2 6.05827698579105e+00 1.37164e+00 + 283 ALOG_E MGUE A ---- 10:058:23656 m 2 -1.26731919248510e-01 6.43056e-03 + 284 TLOG_E MGUE A ---- 10:058:23656 m 2 1.30078937307990e+00 1.72569e-01 + 285 ALOG_E MIZU A ---- 11:070:20783 m 2 2.52475558732161e-01 6.32416e-03 + 286 TLOG_E MIZU A ---- 11:070:20783 m 2 7.80721657250935e-01 5.35253e-02 + 287 ALOG_E MIZU A ---- 11:070:20783 m 2 1.02211504514878e-01 2.96321e-03 + 288 TLOG_E MIZU A ---- 11:070:20783 m 2 9.63928562677631e-03 1.35874e-03 + 289 ALOG_E MIZU A ---- 15:132:76378 m 2 1.46305411266070e-02 1.81133e-03 + 290 TLOG_E MIZU A ---- 15:132:76378 m 2 4.07370354362165e-01 7.79203e-02 + 291 AEXP_N MIZU A ---- 05:228:09988 m 2 -8.49100713996326e-03 4.08110e-04 + 292 TEXP_N MIZU A ---- 05:228:09988 m 2 4.29546276722907e-01 4.26986e-02 + 293 ALOG_N MIZU A ---- 11:070:20783 m 2 -1.85663122423875e-01 1.10710e-02 + 294 TLOG_N MIZU A ---- 11:070:20783 m 2 2.25770407976738e+00 1.92131e-01 + 295 ALOG_N MIZU A ---- 11:070:20783 m 2 -4.74665332962637e-02 1.43954e-03 + 296 TLOG_N MIZU A ---- 11:070:20783 m 2 1.89083492731115e-02 2.57829e-03 + 297 ALOG_E MONP A ---- 10:094:81643 m 2 -3.67432517536462e-03 1.21022e-04 + 298 TLOG_E MONP A ---- 10:094:81643 m 2 4.66973202291815e-02 8.69184e-03 + 299 AEXP_E MPL2 A ---- 10:058:23656 m 2 -4.44855614724335e-03 3.48882e-04 + 300 TEXP_E MPL2 A ---- 10:058:23656 m 2 4.79080468945617e-01 5.57130e-02 + 301 AEXP_N MQZG A ---- 16:318:39776 m 2 -4.13957625595618e-03 1.81766e-04 + 302 TEXP_N MQZG A ---- 16:318:39776 m 2 7.40023187907166e-01 7.58098e-02 + 303 AEXP_E MRL1 A ---- 16:318:39776 m 2 2.45034477274847e-02 1.06084e-03 + 304 TEXP_E MRL1 A ---- 16:318:39776 m 2 1.59325340393361e-02 1.27515e-03 + 305 ALOG_E MRL1 A ---- 16:318:39776 m 2 3.75241227474627e-02 3.17713e-04 + 306 TLOG_E MRL1 A ---- 16:318:39776 m 2 8.68860871691345e-02 3.43090e-03 + 307 AEXP_N MRL1 A ---- 16:318:39776 m 2 2.79023258185835e-02 2.28109e-04 + 308 TEXP_N MRL1 A ---- 16:318:39776 m 2 4.90573515506020e-01 8.57452e-03 + 309 AEXP_N MRL1 A ---- 16:318:39776 m 2 1.38654702166588e-02 7.23618e-04 + 310 TEXP_N MRL1 A ---- 16:318:39776 m 2 2.21545570101284e-02 1.98221e-03 + 311 AEXP_U MRL1 A ---- 16:318:39776 m 2 1.78578404293041e-02 6.32574e-04 + 312 TEXP_U MRL1 A ---- 16:318:39776 m 2 3.33688190667650e-01 2.32094e-02 + 313 ALOG_E MTKA A ---- 11:070:20783 m 2 6.19894580109116e-02 6.51848e-04 + 314 TLOG_E MTKA A ---- 11:070:20783 m 2 4.89487927832590e-01 2.91815e-02 + 315 ALOG_E MTKA A ---- 11:070:20783 m 2 2.10616728533138e-02 1.04676e-03 + 316 TLOG_E MTKA A ---- 11:070:20783 m 2 3.77343553902759e-03 1.00203e-03 + 317 AEXP_N MTKA A ---- 11:070:20783 m 2 -1.67487255745702e-02 6.52551e-04 + 318 TEXP_N MTKA A ---- 11:070:20783 m 2 2.25049434939950e+00 1.44858e-01 + 319 AEXP_U MTKA A ---- 11:070:20783 m 2 2.36368094191075e-02 7.18614e-04 + 320 TEXP_U MTKA A ---- 11:070:20783 m 2 6.26355443306600e-01 3.61153e-02 + 321 ALOG_E NCKU A ---- 16:036:71846 m 2 -6.87475117620020e-03 1.16181e-04 + 322 TLOG_E NCKU A ---- 16:036:71846 m 2 6.28974457411865e-02 5.96793e-03 + 323 AEXP_N NCKU A ---- 16:036:71846 m 2 -9.06550237069407e-03 3.08653e-04 + 324 TEXP_N NCKU A ---- 16:036:71846 m 2 1.68162816390947e+00 7.38004e-02 + 325 AEXP_U NCKU A ---- 16:036:71846 m 2 2.00027895593370e-02 7.93826e-04 + 326 TEXP_U NCKU A ---- 16:036:71846 m 2 3.70346798757153e-01 2.30799e-02 + 327 ALOG_E NESA A ---- 10:058:23656 m 2 -8.15114664812838e-03 1.59814e-04 + 328 TLOG_E NESA A ---- 10:058:23656 m 2 2.40111454633340e-01 1.54813e-02 + 329 ALOG_N NESA A ---- 10:058:23656 m 2 6.79845053501543e-02 1.98283e-02 + 330 TLOG_N NESA A ---- 10:058:23656 m 2 1.02585978411176e+01 2.11463e+00 + 331 AEXP_E NIEB A ---- 10:058:23656 m 2 -7.40137878786898e-03 4.04997e-04 + 332 TEXP_E NIEB A ---- 10:058:23656 m 2 1.11791365014948e+00 1.13986e-01 + 333 ALOG_N NIEB A ---- 10:058:23656 m 2 -1.20632035995242e-02 6.23086e-04 + 334 TLOG_N NIEB A ---- 10:058:23656 m 2 3.03939471191606e-01 3.53788e-02 + 335 ALOG_E NIUM A ---- 09:272:64091 m 2 -5.34758769610922e-03 2.13371e-04 + 336 TLOG_E NIUM A ---- 09:272:64091 m 2 1.93041300828904e-01 2.96604e-02 + 337 AEXP_N NIUM A ---- 09:272:64091 m 2 8.48336945969611e-03 1.96684e-04 + 338 TEXP_N NIUM A ---- 09:272:64091 m 2 7.19395899786150e-01 3.24156e-02 + 339 AEXP_E NTUS A ---- 05:087:58176 m 2 -9.03979149108178e-03 3.70266e-04 + 340 TEXP_E NTUS A ---- 05:087:58176 m 2 1.96296189487457e-01 1.47792e-02 + 341 ALOG_E NTUS A ---- 07:255:40226 m 2 -5.56904469630535e-03 3.59400e-04 + 342 TLOG_E NTUS A ---- 07:255:40226 m 2 2.27601885957635e-01 4.10072e-02 + 343 AEXP_N NTUS A ---- 07:255:40226 m 2 3.55945795303478e-02 1.03569e-03 + 344 TEXP_N NTUS A ---- 07:255:40226 m 2 9.45741639971929e-01 3.48355e-02 + 345 ALOG_N NTUS A ---- 07:255:40226 m 2 -2.78408132350581e-02 7.14724e-04 + 346 TLOG_N NTUS A ---- 07:255:40226 m 2 3.30214479818226e-01 2.46690e-02 + 347 AEXP_E OAFA A ---- 15:259:82472 m 2 1.91100548406048e-02 9.48608e-04 + 348 TEXP_E OAFA A ---- 15:259:82472 m 2 2.55037759451163e-01 1.31785e-02 + 349 ALOG_E OAFA A ---- 15:259:82472 m 2 -1.48531003516509e-02 2.50267e-04 + 350 TLOG_E OAFA A ---- 15:259:82472 m 2 5.22239681661933e-02 5.89610e-03 + 351 AEXP_E OUS2 A ---- 09:196:33748 m 2 -1.39948108370084e-02 2.51320e-04 + 352 TEXP_E OUS2 A ---- 09:196:33748 m 2 1.97144147818045e+00 4.69680e-02 + 353 AEXP_E OUS2 A ---- 09:196:33748 m 2 -6.05458076217277e-03 4.96380e-04 + 354 TEXP_E OUS2 A ---- 09:196:33748 m 2 3.60184970242054e-02 4.99204e-03 + 355 AEXP_N OUS2 A ---- 16:318:39776 m 2 -4.27465099241506e-03 1.21671e-04 + 356 TEXP_N OUS2 A ---- 16:318:39776 m 2 1.06923207405804e+00 8.96452e-02 + 357 ALOG_E P101 A ---- 03:268:71406 m 2 1.96649536823945e-02 4.24183e-04 + 358 TLOG_E P101 A ---- 03:268:71406 m 2 2.70902971654961e-01 1.75376e-02 + 359 AEXP_E P101 A ---- 11:070:20783 m 2 8.44692281610110e-03 5.30657e-04 + 360 TEXP_E P101 A ---- 11:070:20783 m 2 3.27571970945686e+00 2.93687e-01 + 361 ALOG_N P101 A ---- 03:268:71406 m 2 -1.63863528543170e-02 5.54437e-04 + 362 TLOG_N P101 A ---- 03:268:71406 m 2 3.69916152242146e-01 3.05718e-02 + 363 AEXP_N P101 A ---- 11:070:20783 m 2 3.26106417053919e-02 1.00518e-03 + 364 TEXP_N P101 A ---- 11:070:20783 m 2 8.89127011149502e-01 2.68996e-02 + 365 ALOG_N P101 A ---- 11:070:20783 m 2 -2.07847552732303e-02 5.02875e-04 + 366 TLOG_N P101 A ---- 11:070:20783 m 2 1.74140107876701e-01 1.38054e-02 + 367 ALOG_E P475 A ---- 10:094:81643 m 2 -1.52370738049251e-03 5.51011e-05 + 368 TLOG_E P475 A ---- 10:094:81643 m 2 2.03309014679534e-02 5.25869e-03 + 369 AEXP_E PBR2 A ---- 12:102:31117 m 2 -4.38998572143122e-02 2.68669e-03 + 370 TEXP_E PBR2 A ---- 12:102:31117 m 2 3.16962011669051e+00 1.46343e-01 + 371 ALOG_N PBR2 A ---- 12:102:31117 m 2 -2.69549966409943e-02 1.66874e-03 + 372 TLOG_N PBR2 A ---- 12:102:31117 m 2 1.86895685909179e+00 1.35464e-01 + 373 AEXP_U PBR2 A ---- 12:102:31117 m 2 1.95488335026399e-02 9.99502e-04 + 374 TEXP_U PBR2 A ---- 12:102:31117 m 2 1.16497887692074e+00 1.03194e-01 + 375 AEXP_E PBRI A ---- 12:102:31117 m 2 -4.38998572143122e-02 2.68669e-03 + 376 TEXP_E PBRI A ---- 12:102:31117 m 2 3.16962011669051e+00 1.46343e-01 + 377 ALOG_N PBRI A ---- 12:102:31117 m 2 -2.69549966409943e-02 1.66874e-03 + 378 TLOG_N PBRI A ---- 12:102:31117 m 2 1.86895685909179e+00 1.35464e-01 + 379 AEXP_U PBRI A ---- 12:102:31117 m 2 1.95488335026399e-02 9.99502e-04 + 380 TEXP_U PBRI A ---- 12:102:31117 m 2 1.16497887692074e+00 1.03194e-01 + 381 AEXP_N PIN1 A ---- 10:094:81643 m 2 -5.73782484262703e-03 1.84062e-04 + 382 TEXP_N PIN1 A ---- 10:094:81643 m 2 6.79026971003809e-01 4.46495e-02 + 383 ALOG_E PLO5 A ---- 10:094:81643 m 2 -1.52370738049251e-03 5.51011e-05 + 384 TLOG_E PLO5 A ---- 10:094:81643 m 2 2.03309014679534e-02 5.25869e-03 + 385 ALOG_E PLO6 A ---- 10:094:81643 m 2 -1.52370738049251e-03 5.51011e-05 + 386 TLOG_E PLO6 A ---- 10:094:81643 m 2 2.03309014679534e-02 5.25869e-03 + 387 AEXP_E PRMI A ---- 20:007:30265 m 2 -8.38780375891456e-03 8.65321e-04 + 388 TEXP_E PRMI A ---- 20:007:30265 m 2 4.71825084402872e-02 7.40591e-03 + 389 ALOG_N PRMI A ---- 20:007:30265 m 2 4.39477610749126e-03 3.33446e-04 + 390 TLOG_N PRMI A ---- 20:007:30265 m 2 1.15695942399885e-02 5.70290e-03 + 391 ALOG_E PYGR A ---- 09:196:33748 m 2 -3.46336734837210e-02 8.05567e-04 + 392 TLOG_E PYGR A ---- 09:196:33748 m 2 2.67559761929884e+00 1.25228e-01 + 393 ALOG_E PYGR A ---- 09:196:33748 m 2 -7.87927375930097e-03 1.82125e-04 + 394 TLOG_E PYGR A ---- 09:196:33748 m 2 2.31511367226231e-03 8.53811e-04 + 395 ALOG_N PYGR A ---- 09:196:33748 m 2 -1.56793067156189e-02 6.65045e-05 + 396 TLOG_N PYGR A ---- 09:196:33748 m 2 3.46029666929356e-03 4.57928e-04 + 397 AEXP_U PYGR A ---- 09:196:33748 m 2 4.14360456116188e-02 1.29391e-03 + 398 TEXP_U PYGR A ---- 09:196:33748 m 2 1.06602738601876e-01 6.56689e-03 + 399 ALOG_U PYGR A ---- 09:196:33748 m 2 1.65480041712024e-02 1.21968e-03 + 400 TLOG_U PYGR A ---- 09:196:33748 m 2 8.66799351444184e-01 1.61372e-01 + 401 ALOG_E QUI3 A ---- 16:107:86317 m 2 -9.19503296600215e-03 1.34640e-04 + 402 TLOG_E QUI3 A ---- 16:107:86317 m 2 8.34897012041909e-03 1.11781e-03 + 403 AEXP_N RAUL A ---- 14:174:69556 m 2 -6.50701643962465e-03 3.49296e-04 + 404 TEXP_N RAUL A ---- 14:174:69556 m 2 3.39746829604445e-01 4.64663e-02 + 405 AEXP_E REYK A ---- 00:173:03107 m 2 -8.85564799114899e-03 2.99153e-04 + 406 TEXP_E REYK A ---- 00:173:03107 m 2 2.11888718339363e+00 8.17631e-02 + 407 AEXP_N REYK A ---- 00:173:03107 m 2 2.97040485463477e-03 1.90252e-04 + 408 TEXP_N REYK A ---- 00:173:03107 m 2 2.18244693516239e-01 3.13057e-02 + 409 AEXP_E REYZ A ---- 00:173:03107 m 2 -8.85564799114899e-03 2.99153e-04 + 410 TEXP_E REYZ A ---- 00:173:03107 m 2 2.11888718339363e+00 8.17631e-02 + 411 AEXP_N REYZ A ---- 00:173:03107 m 2 2.97040485463477e-03 1.90252e-04 + 412 TEXP_N REYZ A ---- 00:173:03107 m 2 2.18244693516239e-01 3.13057e-02 + 413 AEXP_E RIOP A ---- 16:107:86317 m 2 -1.01534276178752e-02 4.22429e-04 + 414 TEXP_E RIOP A ---- 16:107:86317 m 2 1.48185368895965e-01 1.07351e-02 + 415 AEXP_N RIOP A ---- 16:107:86317 m 2 1.17128466392580e-02 2.58007e-03 + 416 TEXP_N RIOP A ---- 16:107:86317 m 2 1.90340458845199e+00 3.87970e-01 + 417 AEXP_E SAMO A ---- 09:272:64091 m 2 3.68007088138440e-02 5.32065e-04 + 418 TEXP_E SAMO A ---- 09:272:64091 m 2 1.35116213399710e+00 4.00084e-02 + 419 AEXP_N SAMO A ---- 09:272:64091 m 2 1.08502276076767e-02 1.04416e-03 + 420 TEXP_N SAMO A ---- 09:272:64091 m 2 2.34367490750117e+00 2.88747e-01 + 421 ALOG_U SAMO A ---- 09:272:64091 m 2 -1.20589799615208e-02 1.14345e-03 + 422 TLOG_U SAMO A ---- 09:272:64091 m 2 6.62590389408837e-02 3.48895e-02 + 423 AEXP_E SAMP A ---- 04:361:03529 m 2 -2.09041431935917e-01 1.81456e-02 + 424 TEXP_E SAMP A ---- 04:361:03529 m 2 1.22903672535605e+00 1.65710e-01 + 425 ALOG_N SAMP A ---- 05:087:58176 m 2 -2.67623537588271e-02 1.20744e-03 + 426 TLOG_N SAMP A ---- 05:087:58176 m 2 4.01797241515235e-02 8.23363e-03 + 427 ALOG_N SAN0 A ---- 12:249:52928 m 2 -3.28008011369959e-03 3.03265e-04 + 428 TLOG_N SAN0 A ---- 12:249:52928 m 2 4.99393013813994e-01 1.32293e-01 + 429 AEXP_E SANT A ---- 10:058:23656 m 2 -4.47794654886003e-02 2.83324e-04 + 430 TEXP_E SANT A ---- 10:058:23656 m 2 1.56827194864686e+00 2.38959e-02 + 431 AEXP_E SANT A ---- 10:058:23656 m 2 -1.56915625940346e-02 5.06783e-04 + 432 TEXP_E SANT A ---- 10:058:23656 m 2 9.46937865097107e-02 5.65254e-03 + 433 ALOG_N SANT A ---- 10:058:23656 m 2 -2.89548431997034e-03 1.12564e-04 + 434 TLOG_N SANT A ---- 10:058:23656 m 2 5.84317942729779e-02 1.37710e-02 + 435 AEXP_U SANT A ---- 10:058:23656 m 2 1.89347783235953e-02 7.11462e-04 + 436 TEXP_U SANT A ---- 10:058:23656 m 2 2.91251233083518e-01 1.89482e-02 + 437 ALOG_E SCIP A ---- 10:094:81643 m 2 -1.13153907408793e-03 5.06128e-05 + 438 TLOG_E SCIP A ---- 10:094:81643 m 2 3.25514057785605e-02 1.41069e-02 + 439 AEXP_N SEAT A ---- 01:059:68073 m 2 6.40049892733948e-03 5.52135e-04 + 440 TEXP_N SEAT A ---- 01:059:68073 m 2 4.79000051264269e+00 5.37503e-01 + 441 AEXP_E SIN1 A ---- 05:087:58176 m 2 -9.03979149108178e-03 3.70266e-04 + 442 TEXP_E SIN1 A ---- 05:087:58176 m 2 1.96296189487457e-01 1.47792e-02 + 443 ALOG_E SIN1 A ---- 07:255:40226 m 2 -5.56904469630535e-03 3.59400e-04 + 444 TLOG_E SIN1 A ---- 07:255:40226 m 2 2.27601885957635e-01 4.10072e-02 + 445 AEXP_N SIN1 A ---- 07:255:40226 m 2 3.55945795303478e-02 1.03569e-03 + 446 TEXP_N SIN1 A ---- 07:255:40226 m 2 9.45741639971929e-01 3.48355e-02 + 447 ALOG_N SIN1 A ---- 07:255:40226 m 2 -2.78408132350581e-02 7.14724e-04 + 448 TLOG_N SIN1 A ---- 07:255:40226 m 2 3.30214479818226e-01 2.46690e-02 + 449 ALOG_E SIO3 A ---- 10:094:81643 m 2 -3.33160986377613e-03 1.65029e-04 + 450 TLOG_E SIO3 A ---- 10:094:81643 m 2 6.28538104616530e-01 8.09284e-02 + 451 ALOG_E SIO5 A ---- 10:094:81643 m 2 -3.33160986377613e-03 1.65029e-04 + 452 TLOG_E SIO5 A ---- 10:094:81643 m 2 6.28538104616530e-01 8.09284e-02 + 453 ALOG_E SMST A ---- 11:070:20783 m 2 1.65801537669137e-02 6.71452e-04 + 454 TLOG_E SMST A ---- 11:070:20783 m 2 8.09279651151739e-01 5.82705e-02 + 455 AEXP_N SMST A ---- 04:249:36428 m 2 -8.32392318262547e-03 2.40182e-04 + 456 TEXP_N SMST A ---- 04:249:36428 m 2 4.67386891380009e-01 2.66436e-02 + 457 AEXP_N SMST A ---- 11:070:20783 m 2 4.64756738911992e-03 7.06915e-04 + 458 TEXP_N SMST A ---- 11:070:20783 m 2 4.56247336854589e-02 1.24222e-02 + 459 ALOG_N SMST A ---- 11:070:20783 m 2 1.95812889916696e-02 2.61691e-03 + 460 TLOG_N SMST A ---- 11:070:20783 m 2 2.04362949079399e+00 3.70211e-01 + 461 ALOG_E SNLR A ---- 16:107:86317 m 2 -5.63059886284502e-03 1.05308e-04 + 462 TLOG_E SNLR A ---- 16:107:86317 m 2 6.47874310565979e-02 8.54705e-03 + 463 AEXP_N SNLR A ---- 16:107:86317 m 2 -5.80189507969350e-03 3.59224e-04 + 464 TEXP_N SNLR A ---- 16:107:86317 m 2 2.34387587265797e-01 2.32885e-02 + 465 AEXP_E SOLO A ---- 12:207:40826 m 2 1.11852959809966e-02 7.90744e-04 + 466 TEXP_E SOLO A ---- 12:207:40826 m 2 3.96556350807132e-01 7.18972e-02 + 467 AEXP_E SOLO A ---- 16:343:63526 m 2 -8.04929811090408e-03 2.20785e-04 + 468 TEXP_E SOLO A ---- 16:343:63526 m 2 6.44970712795049e-01 3.63323e-02 + 469 AEXP_N SOLO A ---- 16:343:63526 m 2 7.00228637738587e-03 2.09708e-04 + 470 TEXP_N SOLO A ---- 16:343:63526 m 2 5.75680763150846e-01 3.55368e-02 + 471 ALOG_E SPAN A ---- 15:321:25807 m 2 -5.19025774935067e-03 1.27922e-04 + 472 TLOG_E SPAN A ---- 15:321:25807 m 2 1.28214850809435e-01 1.95986e-02 + 473 ALOG_N SPAN A ---- 15:321:25807 m 2 -5.32509199464391e-03 1.26611e-04 + 474 TLOG_N SPAN A ---- 15:321:25807 m 2 1.34576603442766e-01 1.92184e-02 + 475 ALOG_E SPW2 A ---- 10:094:81643 m 2 -3.33160986377613e-03 1.65029e-04 + 476 TLOG_E SPW2 A ---- 10:094:81643 m 2 6.28538104616530e-01 8.09284e-02 + 477 ALOG_E SSIA A ---- 01:013:63211 m 2 1.28320613957517e-02 4.96584e-03 + 478 TLOG_E SSIA A ---- 01:013:63211 m 2 1.44377856369362e+00 6.17099e-01 + 479 AEXP_N SSIA A ---- 01:013:63211 m 2 -6.83375604397587e-03 1.10648e-03 + 480 TEXP_N SSIA A ---- 01:013:63211 m 2 1.38564986859503e-01 2.26072e-02 + 481 AEXP_N SSIA A ---- 12:240:16639 m 2 -6.32358199455157e-03 4.67870e-04 + 482 TEXP_N SSIA A ---- 12:240:16639 m 2 1.28949298913492e-01 1.49842e-02 + 483 AEXP_E STK2 A ---- 03:268:71406 m 2 -3.12544483981059e-02 1.02761e-03 + 484 TEXP_E STK2 A ---- 03:268:71406 m 2 3.90382962596289e-01 1.49329e-02 + 485 ALOG_E STK2 A ---- 03:268:71406 m 2 2.74994599757755e-02 5.62833e-04 + 486 TLOG_E STK2 A ---- 03:268:71406 m 2 6.84349501571032e-02 5.00559e-03 + 487 AEXP_N STK2 A ---- 03:268:71406 m 2 -3.97046280518505e-02 3.82358e-04 + 488 TEXP_N STK2 A ---- 03:268:71406 m 2 1.38871759192151e+00 3.51975e-02 + 489 AEXP_N STK2 A ---- 03:268:71406 m 2 -1.44921478188329e-02 6.90079e-04 + 490 TEXP_N STK2 A ---- 03:268:71406 m 2 6.08619870701242e-02 5.19713e-03 + 491 ALOG_N STK2 A ---- 11:070:20783 m 2 -8.39729252769926e-03 6.34815e-04 + 492 TLOG_N STK2 A ---- 11:070:20783 m 2 8.15141993916550e-01 1.29464e-01 + 493 AEXP_E SUWN A ---- 11:070:20783 m 2 1.32650619450541e-02 3.08536e-04 + 494 TEXP_E SUWN A ---- 11:070:20783 m 2 1.23072292664592e+00 5.23632e-02 + 495 AEXP_E TONG A ---- 06:123:55600 m 2 2.50589003031582e-02 5.92410e-04 + 496 TEXP_E TONG A ---- 06:123:55600 m 2 1.51168963187595e-01 9.30364e-03 + 497 AEXP_E TSEA A ---- 02:307:79960 m 2 -4.60711093401196e-03 6.05849e-04 + 498 TEXP_E TSEA A ---- 02:307:79960 m 2 1.03414635482049e-01 2.36639e-02 + 499 AEXP_N TSEA A ---- 02:307:79960 m 2 -1.29007585218825e-02 6.39930e-04 + 500 TEXP_N TSEA A ---- 02:307:79960 m 2 3.84053429643352e-01 3.12276e-02 + 501 ALOG_E TSK2 A ---- 08:128:60320 m 2 5.22471832893359e-03 9.20036e-05 + 502 TLOG_E TSK2 A ---- 08:128:60320 m 2 1.46938693175004e-01 1.11048e-02 + 503 ALOG_E TSK2 A ---- 11:070:20783 m 2 5.94538821482231e-02 4.13719e-04 + 504 TLOG_E TSK2 A ---- 11:070:20783 m 2 3.56111805993900e-01 1.50813e-02 + 505 ALOG_E TSK2 A ---- 11:070:20783 m 2 3.83133174532408e-02 6.30813e-04 + 506 TLOG_E TSK2 A ---- 11:070:20783 m 2 3.49975286402748e-03 2.99828e-04 + 507 ALOG_N TSK2 A ---- 11:070:20783 m 2 -2.00198899064030e-02 1.29650e-04 + 508 TLOG_N TSK2 A ---- 11:070:20783 m 2 3.58021355312602e-02 1.23626e-03 + 509 ALOG_U TSK2 A ---- 11:070:20783 m 2 2.04788668532073e-02 5.84755e-03 + 510 TLOG_U TSK2 A ---- 11:070:20783 m 2 1.66542867694577e+00 4.82397e-01 + 511 ALOG_E TSKB A ---- 08:128:60320 m 2 5.22471832893359e-03 9.20036e-05 + 512 TLOG_E TSKB A ---- 08:128:60320 m 2 1.46938693175004e-01 1.11048e-02 + 513 ALOG_E TSKB A ---- 11:070:20783 m 2 5.94538821482231e-02 4.13719e-04 + 514 TLOG_E TSKB A ---- 11:070:20783 m 2 3.56111805993900e-01 1.50813e-02 + 515 ALOG_E TSKB A ---- 11:070:20783 m 2 3.83133174532408e-02 6.30813e-04 + 516 TLOG_E TSKB A ---- 11:070:20783 m 2 3.49975286402748e-03 2.99828e-04 + 517 ALOG_N TSKB A ---- 11:070:20783 m 2 -2.00198899064030e-02 1.29650e-04 + 518 TLOG_N TSKB A ---- 11:070:20783 m 2 3.58021355312602e-02 1.23626e-03 + 519 ALOG_U TSKB A ---- 11:070:20783 m 2 2.04788668532073e-02 5.84755e-03 + 520 TLOG_U TSKB A ---- 11:070:20783 m 2 1.66542867694577e+00 4.82397e-01 + 521 AEXP_E TUBI A ---- 99:229:00098 m 2 3.00847613802610e-02 8.84877e-04 + 522 TEXP_E TUBI A ---- 99:229:00098 m 2 2.22132370643969e-01 6.24494e-03 + 523 ALOG_E TUBI A ---- 99:229:00098 m 2 3.09033682394748e-02 5.79624e-04 + 524 TLOG_E TUBI A ---- 99:229:00098 m 2 1.77976546246410e+00 7.24951e-02 + 525 AEXP_N TUBI A ---- 99:229:00098 m 2 -2.34634717506956e-02 1.94494e-04 + 526 TEXP_N TUBI A ---- 99:229:00098 m 2 1.39974683020311e+00 1.76010e-02 + 527 AEXP_U TUBI A ---- 99:229:00098 m 2 -1.17220976912076e-02 5.46222e-04 + 528 TEXP_U TUBI A ---- 99:229:00098 m 2 1.62852372472698e+00 1.32445e-01 + 529 ALOG_N TUVA A ---- 09:280:80331 m 2 -1.29673208445191e-03 1.57031e-04 + 530 TLOG_N TUVA A ---- 09:280:80331 m 2 3.02245843846693e-02 2.50691e-02 + 531 ALOG_E USUD A ---- 11:070:20783 m 2 6.43929633149002e-02 1.47838e-03 + 532 TLOG_E USUD A ---- 11:070:20783 m 2 8.50198686244387e-01 7.28977e-02 + 533 ALOG_E USUD A ---- 11:070:20783 m 2 1.83670278816655e-02 9.43965e-04 + 534 TLOG_E USUD A ---- 11:070:20783 m 2 6.94213965179776e-03 1.55455e-03 + 535 AEXP_N USUD A ---- 04:249:53839 m 2 -1.08771694220235e-02 3.48526e-04 + 536 TEXP_N USUD A ---- 04:249:53839 m 2 4.40049097221585e-01 2.77322e-02 + 537 ALOG_N USUD A ---- 11:070:20783 m 2 6.48093382168595e-03 1.87646e-04 + 538 TLOG_N USUD A ---- 11:070:20783 m 2 3.67089849386313e-02 7.80848e-03 + 539 ALOG_U USUD A ---- 11:070:20783 m 2 3.87651035216392e-02 6.37074e-03 + 540 TLOG_U USUD A ---- 11:070:20783 m 2 1.30471832412352e+00 2.95267e-01 + 541 ALOG_E VALP A ---- 10:058:23656 m 2 -9.16467869083902e-03 3.61976e-04 + 542 TLOG_E VALP A ---- 10:058:23656 m 2 1.62275876141553e-01 1.70436e-02 + 543 ALOG_N VALP A ---- 10:058:23656 m 2 6.22505414649506e-03 1.27358e-04 + 544 TLOG_N VALP A ---- 10:058:23656 m 2 2.23973156439730e-02 3.34071e-03 + 545 AEXP_E VBCA A ---- 10:058:23656 m 2 -1.36948736938947e-02 1.82498e-04 + 546 TEXP_E VBCA A ---- 10:058:23656 m 2 9.16234198407747e-01 3.06172e-02 + 547 ALOG_E WAIM A ---- 09:196:33748 m 2 -1.54443896534085e-03 8.15670e-05 + 548 TLOG_E WAIM A ---- 09:196:33748 m 2 1.49767031594536e-03 1.44952e-03 + 549 ALOG_N WAIM A ---- 16:318:39776 m 2 -6.29912570778562e-04 1.55829e-04 + 550 TLOG_N WAIM A ---- 16:318:39776 m 2 1.28244996876357e-02 2.29666e-02 + 551 AEXP_N WANG A ---- 16:318:39776 m 2 1.07980647790036e-02 2.99711e-04 + 552 TEXP_N WANG A ---- 16:318:39776 m 2 5.74384313065965e-01 3.54370e-02 + 553 ALOG_E WEST A ---- 16:318:39776 m 2 1.89087066711369e-02 2.71426e-04 + 554 TLOG_E WEST A ---- 16:318:39776 m 2 3.30127623447567e-01 1.67666e-02 + 555 ALOG_N WEST A ---- 16:318:39776 m 2 -7.34343142362125e-03 2.60384e-04 + 556 TLOG_N WEST A ---- 16:318:39776 m 2 5.05700269622179e-01 5.24012e-02 + 557 AEXP_U WEST A ---- 16:318:39776 m 2 6.52135835019101e-03 7.13436e-04 + 558 TEXP_U WEST A ---- 16:318:39776 m 2 4.12627612835993e-01 7.34285e-02 + 559 AEXP_E WGTN A ---- 16:318:39776 m 2 3.25483238231841e-02 3.08094e-04 + 560 TEXP_E WGTN A ---- 16:318:39776 m 2 6.61111294734417e-01 1.33781e-02 + 561 AEXP_N WGTN A ---- 16:318:39776 m 2 1.02770454303946e-02 2.80074e-04 + 562 TEXP_N WGTN A ---- 16:318:39776 m 2 1.89223357057502e-01 8.78070e-03 + 563 AEXP_U WGTN A ---- 16:318:39776 m 2 2.58344283629362e-02 5.85202e-04 + 564 TEXP_U WGTN A ---- 16:318:39776 m 2 5.17467392480420e-01 2.62727e-02 + 565 AEXP_E WGTT A ---- 16:318:39776 m 2 3.25483238231841e-02 3.08094e-04 + 566 TEXP_E WGTT A ---- 16:318:39776 m 2 6.61111294734417e-01 1.33781e-02 + 567 AEXP_N WGTT A ---- 16:318:39776 m 2 1.02770454303946e-02 2.80074e-04 + 568 TEXP_N WGTT A ---- 16:318:39776 m 2 1.89223357057502e-01 8.78070e-03 + 569 AEXP_U WGTT A ---- 16:318:39776 m 2 2.58344283629362e-02 5.85202e-04 + 570 TEXP_U WGTT A ---- 16:318:39776 m 2 5.17467392480420e-01 2.62727e-02 + 571 AEXP_E WIDC A ---- 99:289:35203 m 2 5.65847381811268e-03 2.11420e-04 + 572 TEXP_E WIDC A ---- 99:289:35203 m 2 1.06042940483799e+00 8.10135e-02 + 573 AEXP_N WIDC A ---- 99:289:35203 m 2 1.90382222575423e-02 2.26822e-04 + 574 TEXP_N WIDC A ---- 99:289:35203 m 2 1.79253756139987e+00 4.70704e-02 + 575 AEXP_U WIDC A ---- 99:289:35203 m 2 1.64443798024266e-02 6.64387e-04 + 576 TEXP_U WIDC A ---- 99:289:35203 m 2 1.01590246466036e+00 8.37886e-02 + 577 ALOG_E YSSK A ---- 06:319:40458 m 2 2.15227089648403e-02 6.18388e-04 + 578 TLOG_E YSSK A ---- 06:319:40458 m 2 1.25148069115678e+00 6.76607e-02 + 579 AEXP_N YSSK A ---- 03:268:71406 m 2 -3.13310178966204e-02 3.22519e-03 + 580 TEXP_N YSSK A ---- 03:268:71406 m 2 7.34033500399195e+00 9.45262e-01 +-SOLUTION/ESTIMATE +*------------------------------------------------------------------------------- ++SOLUTION/MATRIX_ESTIMATE L COVA +*PARA1 PARA2 _______PARA2+0_______ _______PARA2+1_______ _______PARA2+2_______ + 1 1 6.60355780412110e-07 + 2 1 -3.23611103979246e-05 3.62353090855809e-03 + 3 1 1.03894203180700e-06 -6.85652272100501e-05 2.02053950883164e-06 + 4 1 -1.01835654922726e-04 6.82650729187649e-03 -1.91488699555833e-04 + 4 4 2.17229917036420e-02 + 5 5 4.78195740351243e-07 + 6 5 -5.56122743635090e-05 7.50998514426447e-03 + 7 7 8.98831597913470e-07 + 8 7 5.26800444251685e-07 4.27325527477697e-05 + 9 9 1.10794621511418e-06 + 10 9 6.36390996999676e-07 1.30044202094694e-05 + 11 11 4.80410883966519e-08 + 12 11 -1.36243570680237e-05 4.43247722244490e-03 + 13 13 1.16970417900393e-07 + 14 13 -1.75287547394606e-05 6.10456318355175e-03 + 15 13 2.65643783949337e-08 -9.28159473862588e-06 1.90819505335538e-07 + 16 13 -5.85986716477470e-07 6.95063660900210e-04 1.85958363312584e-06 + 16 16 1.94042802795935e-04 + 17 17 4.24080705397997e-07 + 18 17 3.78978148099488e-05 1.74423175455505e-02 + 19 19 3.53197835950121e-07 + 20 19 -5.68995374908077e-05 9.65634914027545e-03 + 21 21 8.53418773446928e-07 + 22 21 2.01093149028993e-06 2.92939853550401e-04 + 23 23 2.24284190685926e-06 + 24 23 -4.10952964188086e-05 3.28099639205238e-03 + 25 25 1.08832892692641e-06 + 26 25 -2.31677268791712e-05 1.18263789759423e-03 + 27 27 5.49126093050820e-06 + 28 27 -4.17678612437464e-04 3.27002875591221e-02 + 29 29 6.83392094915835e-08 + 30 29 -2.25495580436145e-06 2.03939722224424e-04 + 31 31 3.88787698684601e-08 + 32 31 -3.64489050980709e-06 4.64992807156252e-04 + 33 33 2.93631378620094e-07 + 34 33 -6.27980199645138e-06 3.28330091776588e-04 + 35 33 9.34571381209207e-08 -8.67550122182145e-06 2.68205011724182e-07 + 36 33 -2.24613231335238e-08 4.76736909082966e-06 -1.67043426394155e-07 + 36 36 1.36088365599347e-07 + 37 37 5.60256293574423e-04 + 38 37 3.94677436344743e-02 2.80219562381015e+00 + 39 37 6.08475769425742e-06 4.62711323086027e-04 1.36866772675355e-07 + 40 37 2.92863781109518e-05 2.31007842902131e-03 8.65979590824877e-07 + 40 40 6.93998754122192e-06 + 41 41 9.06431358207978e-07 + 42 41 1.58577608559751e-05 3.69809494537134e-04 + 43 43 3.43416390801453e-04 + 44 43 -2.23622442722071e-02 1.51585196168683e+00 + 45 43 1.40605475890176e-05 -1.09443169735092e-03 1.15396147745156e-06 + 46 43 -9.61706931581597e-05 7.98856517819695e-03 -9.92109553170249e-06 + 46 46 9.75870718553388e-05 + 47 47 1.51190923509992e-07 + 48 47 -1.37201559615031e-05 1.96242403730869e-03 + 49 47 4.94403702479941e-08 -9.69048719733527e-06 1.40302727126522e-07 + 50 47 -6.35155086835824e-07 1.77881641836719e-04 -1.92329122531900e-07 + 50 50 4.28509917842828e-05 + 51 51 2.64536134155794e-05 + 52 51 1.72020689530158e-03 1.13576538907502e-01 + 53 53 3.43416390801453e-04 + 54 53 -2.23622442722071e-02 1.51585196168683e+00 + 55 53 1.40605475890176e-05 -1.09443169735092e-03 1.15396147745156e-06 + 56 53 -9.61706931581597e-05 7.98856517819695e-03 -9.92109553170249e-06 + 56 56 9.75870718553388e-05 + 57 57 1.51190923509992e-07 + 58 57 -1.37201559615031e-05 1.96242403730869e-03 + 59 57 4.94403702479941e-08 -9.69048719733527e-06 1.40302727126522e-07 + 60 57 -6.35155086835824e-07 1.77881641836719e-04 -1.92329122531900e-07 + 60 60 4.28509917842828e-05 + 61 61 2.64536134155794e-05 + 62 61 1.72020689530158e-03 1.13576538907502e-01 + 63 63 2.86175466944445e-05 + 64 63 2.79091987338237e-03 2.77715893660872e-01 + 65 65 8.48615127317345e-08 + 66 65 4.90752278290522e-06 2.71129759772012e-03 + 67 67 1.55592955619370e-06 + 68 67 -3.79567829753533e-05 1.03515103783138e-03 + 69 69 5.24142150871658e-08 + 70 69 -2.34494957460367e-06 1.30293976993798e-04 + 71 71 2.86501389504260e-07 + 72 71 1.47161137241873e-06 6.40480384516026e-04 + 73 73 1.19945207374051e-08 + 74 73 7.26645939985309e-07 9.85599294983465e-04 + 75 75 4.37269569929830e-05 + 76 75 2.99240488256928e-05 3.27922120973797e-05 + 77 75 -6.08759898355879e-07 -1.97512430721209e-06 3.13613345475490e-07 + 78 75 8.68497370974563e-06 2.02150434166870e-05 -2.38733886889366e-06 + 78 78 2.05638513742445e-05 + 79 79 9.03217170718919e-08 + 80 79 -9.69713739576610e-07 1.19133723860190e-05 + 81 79 1.28528517522635e-08 -1.32649438358220e-07 2.16518322638525e-07 + 82 79 -5.60180256357800e-06 5.32837669941050e-05 9.09465528145161e-06 + 82 82 1.48627843153711e-03 + 83 83 1.04460151673214e-06 + 84 83 7.74716006291755e-06 6.68413409511233e-05 + 85 85 3.61045738443990e-08 + 86 85 -1.09439113920535e-06 7.36443365332676e-04 + 87 87 5.07396374143626e-08 + 88 87 4.89683554180545e-06 5.08939998344988e-03 + 89 89 2.10522996740614e-06 + 90 89 -3.78697295078777e-05 9.11663823845962e-04 + 91 91 4.06389215113137e-08 + 92 91 -4.23013270625769e-06 7.16395836770063e-03 + 93 93 1.34979146513207e-08 + 94 93 -5.68026136912454e-07 3.56161619417965e-05 + 95 95 9.52666992219374e-08 + 96 95 -1.58203679680886e-05 5.44649797494068e-03 + 97 97 6.30159849763777e-07 + 98 97 -8.86690518259590e-06 5.32679868845111e-04 + 99 99 1.15936396109516e-06 + 100 99 5.29531318481546e-04 2.85740027841245e-01 + 101 101 3.14314060005763e-07 + 102 101 -1.13871229816897e-04 7.01501719420772e-02 + 103 103 5.33302052836971e-07 + 104 103 5.52756879086295e-05 7.20837481207354e-03 + 105 105 6.65433567778838e-08 + 106 105 2.15758956550369e-05 8.46412532938636e-03 + 107 105 -7.16932365946865e-09 -2.08559658883603e-06 3.10191511189868e-08 + 108 105 -1.07532317732247e-06 -3.31403766048432e-04 -1.73656278774719e-06 + 108 108 7.29557566816043e-04 + 109 109 9.03293471527507e-08 + 110 109 -3.41011970981868e-07 1.42633703588236e-06 + 111 109 -3.65162288929117e-08 1.42963145790788e-07 8.30712196838394e-08 + 112 109 7.74553653866941e-07 -3.55820660127353e-06 -4.19564736638743e-07 + 112 112 2.43031673687188e-04 + 113 113 2.61518744283271e-07 + 114 113 -1.99962244525969e-05 2.35825540078238e-03 + 115 115 9.03293471527507e-08 + 116 115 -3.41011970981868e-07 1.42633703588236e-06 + 117 115 -3.65162288929117e-08 1.42963145790788e-07 8.30712196838394e-08 + 118 115 7.74553653866941e-07 -3.55820660127353e-06 -4.19564736638743e-07 + 118 118 2.43031673687188e-04 + 119 119 2.61518744283271e-07 + 120 119 -1.99962244525969e-05 2.35825540078238e-03 + 121 121 1.33851651970742e-08 + 122 121 -6.60108128192843e-07 5.04262696925127e-05 + 123 123 2.05918587734969e-07 + 124 123 -4.89583119982508e-05 1.28494110420397e-02 + 125 125 3.24909394396211e-08 + 126 125 1.59689907340643e-07 3.58356721850872e-03 + 127 125 -6.12867319909295e-10 2.21490781341012e-07 8.64518550279559e-07 + 128 125 2.75874235344665e-09 -1.16448707838050e-06 3.48783372269650e-07 + 128 128 8.18535893658333e-05 + 129 129 6.47450620552887e-07 + 130 129 1.06632272744604e-04 2.21661602084816e-02 + 131 131 6.47450620552887e-07 + 132 131 1.06632272744604e-04 2.21661602084816e-02 + 133 133 2.43233920776918e-08 + 134 133 2.69983895112576e-06 3.55063583188246e-04 + 135 135 2.43233920776918e-08 + 136 135 2.69983895112576e-06 3.55063583188246e-04 + 137 137 1.42432026557948e-07 + 138 137 4.73090666871270e-05 1.67221502220236e-02 + 139 139 6.80596020821807e-08 + 140 139 4.23103153971027e-06 3.66191301430286e-03 + 141 141 6.31618216809286e-08 + 142 141 -1.84576705381075e-06 2.20599360275040e-03 + 143 141 -4.66808411156481e-09 -1.56457049730309e-06 2.46393520401283e-07 + 144 141 2.87655667218045e-07 7.39604423771428e-05 1.36167832269862e-06 + 144 144 2.49204457746566e-05 + 145 145 1.48037105738326e-08 + 146 145 -2.60876064554246e-06 8.03626012528941e-03 + 147 147 1.26117286530509e-07 + 148 147 -3.30414561050310e-06 3.09926174848144e-04 + 149 149 9.82256755686139e-08 + 150 149 2.46423731643350e-06 4.27916220900436e-04 + 151 151 1.26117286530509e-07 + 152 151 -3.30414561050310e-06 3.09926174848144e-04 + 153 153 9.82256755686139e-08 + 154 153 2.46423731643350e-06 4.27916220900436e-04 + 155 155 1.50906553902980e-07 + 156 155 3.06349436316181e-05 1.74049940899665e-02 + 157 155 3.44646082978298e-09 4.99282089584420e-06 3.33428582582184e-07 + 158 155 1.85643570125553e-07 5.02483224348707e-04 -4.16184069692082e-06 + 158 158 1.38735783137122e-04 + 159 159 2.31734480410641e-07 + 160 159 -4.88825994617433e-05 2.20818049225340e-02 + 161 159 1.64715088906279e-08 -1.47887000417931e-05 7.39806910933734e-08 + 162 159 -2.66262615537690e-07 7.35360091301544e-04 9.15528298747820e-07 + 162 162 2.52664929415180e-04 + 163 163 1.50906553902980e-07 + 164 163 3.06349436316181e-05 1.74049940899665e-02 + 165 163 3.44646082978298e-09 4.99282089584420e-06 3.33428582582184e-07 + 166 163 1.85643570125553e-07 5.02483224348707e-04 -4.16184069692082e-06 + 166 166 1.38735783137122e-04 + 167 167 2.31734480410641e-07 + 168 167 -4.88825994617433e-05 2.20818049225340e-02 + 169 167 1.64715088906279e-08 -1.47887000417931e-05 7.39806910933734e-08 + 170 167 -2.66262615537690e-07 7.35360091301544e-04 9.15528298747820e-07 + 170 170 2.52664929415180e-04 + 171 171 7.79155459404372e-06 + 172 171 -6.66957236057392e-05 1.32301162184042e-03 + 173 171 -4.20761854879152e-06 5.70864238807998e-05 3.07663719454331e-06 + 174 171 -1.71238260101372e-05 5.10057478125575e-04 2.21026962623326e-05 + 174 174 2.54190646508464e-04 + 175 175 3.03256973299314e-06 + 176 175 -1.00658146110481e-04 4.07506481466396e-03 + 177 177 1.50906553902980e-07 + 178 177 3.06349436316181e-05 1.74049940899665e-02 + 179 177 3.44646082978298e-09 4.99282089584420e-06 3.33428582582184e-07 + 180 177 1.85643570125553e-07 5.02483224348707e-04 -4.16184069692082e-06 + 180 180 1.38735783137122e-04 + 181 181 2.31734480410641e-07 + 182 181 -4.88825994617433e-05 2.20818049225340e-02 + 183 181 1.64715088906279e-08 -1.47887000417931e-05 7.39806910933734e-08 + 184 181 -2.66262615537690e-07 7.35360091301544e-04 9.15528298747820e-07 + 184 184 2.52664929415180e-04 + 185 185 1.13360704135814e-07 + 186 185 -6.21301782563587e-07 2.29640155281449e-03 + 187 185 -2.24088942975815e-09 -1.16226677171034e-07 4.61928554034932e-07 + 188 185 1.50964113784924e-08 9.87525462323469e-06 -3.50309809073583e-06 + 188 188 7.72632658827067e-05 + 189 189 8.12078090203370e-08 + 190 189 -1.72517758433108e-06 1.76345826236567e-03 + 191 189 -1.55281185936083e-09 1.04204028798424e-07 3.20874753952908e-07 + 192 189 -1.89440028157212e-08 7.58006702978629e-06 2.66652688478183e-06 + 192 192 6.17698032370462e-05 + 193 193 8.41573206171206e-08 + 194 193 -4.12696862142337e-05 4.45670034789162e-02 + 195 193 5.07205919360246e-09 -2.90885257323544e-06 2.73866146313377e-08 + 196 193 2.45412458539986e-07 -1.57542723360942e-04 -5.17925028599427e-08 + 196 196 4.75551850721919e-04 + 197 197 8.41573206171206e-08 + 198 197 -4.12696862142337e-05 4.45670034789162e-02 + 199 197 5.07205919360246e-09 -2.90885257323544e-06 2.73866146313377e-08 + 200 197 2.45412458539986e-07 -1.57542723360942e-04 -5.17925028599427e-08 + 200 200 4.75551850721919e-04 + 201 201 1.23385466533609e-07 + 202 201 -1.04036874519182e-05 2.05024606910364e-02 + 203 203 3.22428749034586e-08 + 204 203 -2.18961708459409e-06 3.96360447733577e-03 + 205 205 4.40805663830936e-08 + 206 205 1.12216363219135e-06 4.94382838987962e-05 + 207 207 1.27707320690248e-07 + 208 207 2.25676955685580e-05 5.31943569873307e-03 + 209 209 3.67743404862814e-08 + 210 209 1.51841017030739e-05 4.36410826571019e-02 + 211 211 3.45057709917603e-07 + 212 211 3.11042077423375e-05 4.18230130882816e-02 + 213 213 1.40803978661745e-06 + 214 213 4.10070204525923e-07 1.49293740828257e-06 + 215 213 1.18776778502070e-07 -2.06944484325222e-07 9.53370878660788e-08 + 216 213 -1.95296423395471e-06 4.04953445534048e-06 -1.45664926046273e-06 + 216 216 2.44817510134224e-05 + 217 217 1.07847104541127e-08 + 218 217 -5.84493824388813e-07 4.16631018741844e-05 + 219 219 4.41485506810581e-08 + 220 219 -7.41868474739974e-07 2.47614185823178e-05 + 221 221 1.44992918934931e-07 + 222 221 2.15858134832314e-05 3.39813303065865e-03 + 223 223 7.40696982660010e-08 + 224 223 -1.41773791571159e-05 2.96835741972856e-03 + 225 225 1.04508279855662e-07 + 226 225 -1.56050126568827e-05 1.08968498134415e-02 + 227 227 1.87699182061819e-04 + 228 227 -9.35117250653872e-04 5.16307531123533e-03 + 229 229 5.36626886640776e-06 + 230 229 2.01680899899378e-04 2.80507944078274e-02 + 231 231 1.87699182061819e-04 + 232 231 -9.35117250653872e-04 5.16307531123533e-03 + 233 233 5.36626886640776e-06 + 234 233 2.01680899899378e-04 2.80507944078274e-02 + 235 235 1.97313610585759e-06 + 236 235 4.30092591939231e-06 3.28043614652059e-05 + 237 235 1.23687400572550e-07 1.27151737138764e-06 7.29117586305639e-08 + 238 235 2.90385134480116e-06 7.92094741152429e-06 3.93947543101803e-07 + 238 238 5.60418882484327e-06 + 239 239 2.67902529246193e-02 + 240 239 -2.03752508321006e+00 1.55052628130063e+02 + 241 241 7.13869117802644e-07 + 242 241 -1.34548761216583e-05 9.02958243644705e-03 + 243 243 6.63775876061874e-08 + 244 243 3.96799662745095e-06 2.92921232409867e-04 + 245 243 -7.24190631112815e-09 -6.62174860986512e-07 5.24484023307393e-06 + 246 243 -4.41018829423451e-07 -1.88874508555033e-05 -9.08413979963058e-05 + 246 246 2.65529659396367e-03 + 247 243 -1.61418576130698e-08 -5.76047304770441e-07 -6.45185698849678e-06 + 247 246 1.45971428924064e-04 9.05598890011091e-06 + 248 243 -3.79092010646529e-09 -1.47474083790268e-07 -2.01061323956442e-06 + 248 246 3.83610180370340e-05 2.62935549324747e-06 8.94522572754560e-07 + 249 249 3.39987252328037e-03 + 250 249 -1.21347836758155e-01 4.37007807027112e+00 + 251 249 2.22226898683796e-05 -8.55468411057245e-04 2.70732079109181e-07 + 252 249 -2.19016788421484e-05 8.63842435494923e-04 -3.33097894607539e-07 + 252 252 6.03710113938622e-07 + 253 253 2.99486956245080e-07 + 254 253 1.90318252822308e-06 1.84474338926750e-05 + 255 255 1.43354297367405e-07 + 256 255 -1.45532243891445e-05 2.16901237033369e-03 + 257 257 6.82752248177668e-07 + 258 257 4.56285098661855e-06 5.58383533953009e-04 + 259 257 -9.17818222493877e-08 -6.83726006545875e-06 9.59771187744533e-08 + 260 257 -3.71039824207763e-06 1.98341929233934e-04 -2.37339110663098e-06 + 260 260 1.24045124850782e-04 + 261 261 1.45534378443049e-07 + 262 261 4.65197770695862e-05 1.66552307978516e-02 + 263 263 1.91720182518646e-07 + 264 263 6.64725054805140e-06 2.68396275136860e-04 + 265 265 3.78218854240441e-07 + 266 265 -8.52082953429110e-05 2.02343989819696e-02 + 267 267 1.95169137339831e-07 + 268 267 -1.16685148339852e-06 1.68147963031801e-05 + 269 267 4.87922459532196e-09 1.12829096558919e-08 3.25302812386316e-07 + 270 267 -3.56735439821790e-08 1.60216435085696e-06 -2.07581810695272e-06 + 270 270 2.42361584077629e-05 + 271 271 2.72760920229614e-08 + 272 271 -1.93726047552666e-06 7.55972721966779e-03 + 273 273 3.76896782905545e-07 + 274 273 -3.90962014656962e-06 2.21273283411575e-04 + 275 273 -3.15022571606820e-09 -2.09317977985168e-06 3.31471611716875e-08 + 276 273 -3.36257313711227e-06 8.47650791382122e-05 -8.11124776124536e-07 + 276 276 5.46085270740048e-05 + 277 277 3.19021995353960e-07 + 278 277 5.98272596590541e-05 1.73541984937566e-02 + 279 279 7.46093826792333e-06 + 280 279 -4.07043068683772e-04 2.78022576176360e-02 + 281 279 -5.10076381585168e-06 2.58785936283572e-04 8.43751522563116e-04 + 282 279 1.00465573105369e-04 -4.93825630652202e-03 -3.91835407133432e-02 + 282 282 1.88139404209255e+00 + 283 283 4.13520824166613e-05 + 284 283 -1.06170079435801e-03 2.97799837406942e-02 + 285 285 3.99949835861616e-05 + 286 285 3.00807137848667e-04 2.86496124624118e-03 + 287 285 1.25771921939897e-05 1.48080783115476e-04 8.78062201917500e-06 + 288 285 4.51646810568143e-06 6.05591476740094e-05 3.87885823292416e-06 + 288 288 1.84616334253438e-06 + 289 285 -6.55559315099448e-06 -5.42701866451848e-05 -2.52305083674570e-06 + 289 288 -9.84417685088176e-07 3.28093041129718e-06 + 290 285 -1.86631789411213e-04 -1.55548113481467e-03 -7.28603364394620e-05 + 290 288 -2.86146778868029e-05 1.34834243529180e-04 6.07156631924887e-03 + 291 291 1.66553905203769e-07 + 292 291 -1.43688378034203e-07 1.82317155307254e-03 + 293 291 -1.35178042331678e-08 -8.05628752664996e-07 1.22566217054047e-04 + 294 291 4.33586788959409e-07 6.67324870209162e-05 -1.90163157922274e-03 + 294 294 3.69145098730559e-02 + 295 291 -3.20115812350503e-09 -6.99095807376577e-07 8.52730152821007e-06 + 295 294 -2.32712418757320e-04 2.07226195762865e-06 + 296 291 4.62042905383447e-09 8.68396648372862e-07 -1.17002895382335e-05 + 296 294 3.52301930194353e-04 -3.51542697885054e-06 6.64755783544077e-06 + 297 297 1.46462357521225e-08 + 298 297 -8.77475319641642e-07 7.55480663783029e-05 + 299 299 1.21718725684036e-07 + 300 299 1.33352147041544e-05 3.10394108910444e-03 + 301 301 3.30388723368674e-08 + 302 301 2.41186683590278e-06 5.74712685460267e-03 + 303 303 1.12539033328623e-06 + 304 303 -3.59331052007261e-07 1.62600776515774e-06 + 305 303 1.05066304149458e-07 2.17829298528376e-07 1.00941671980695e-07 + 306 303 1.17545375680787e-06 2.98911870785448e-06 1.02848007024910e-06 + 306 306 1.17711005279813e-05 + 307 307 5.20336679545752e-08 + 308 307 -6.40433338253882e-07 7.35223804426604e-05 + 309 307 -5.41001978175543e-09 5.24046013401379e-07 5.23622877193170e-07 + 310 307 -2.42646838493370e-07 8.32787129186577e-06 -7.62958799310986e-07 + 310 310 3.92915104657710e-06 + 311 311 4.00150383739942e-07 + 312 311 -3.25649827367708e-06 5.38675233887521e-04 + 313 313 4.24906082056008e-07 + 314 313 -5.78679308881251e-06 8.51560837995223e-04 + 315 313 -4.04735906491667e-07 2.88019647615853e-05 1.09569645924353e-06 + 316 313 -4.29080589913580e-07 2.39572707416155e-05 9.75438923245022e-07 + 316 316 1.00406876356358e-06 + 317 317 4.25823194225342e-07 + 318 317 -8.38218905355503e-05 2.09837206909513e-02 + 319 319 5.16406758048050e-07 + 320 319 -3.18377961076042e-06 1.30431237715388e-03 + 321 321 1.34979146513207e-08 + 322 321 -5.68026136912454e-07 3.56161619417965e-05 + 323 323 9.52666992219374e-08 + 324 323 -1.58203679680886e-05 5.44649797494068e-03 + 325 325 6.30159849763777e-07 + 326 325 -8.86690518259590e-06 5.32679868845111e-04 + 327 327 2.55404195124899e-08 + 328 327 -2.31231010601996e-06 2.39670494866315e-04 + 329 329 3.93163025126442e-04 + 330 329 4.18584160443335e-02 4.47165415827564e+00 + 331 331 1.64022368085523e-07 + 332 331 -3.62418283298889e-05 1.29927491913308e-02 + 333 333 3.88235958619332e-07 + 334 333 -2.12358873346952e-05 1.25166067087653e-03 + 335 335 4.55272366384519e-08 + 336 335 -5.75953549641166e-06 8.79736505168703e-04 + 337 337 3.86844908595904e-08 + 338 337 -2.50161259686352e-06 1.05077102250925e-03 + 339 339 1.37096551530584e-07 + 340 339 2.13264963667378e-06 2.18425847895200e-04 + 341 339 3.18619110107991e-08 -2.29981907338666e-06 1.29168628934825e-07 + 342 339 -3.19974243439008e-06 2.25180167822265e-04 -1.37780838895462e-05 + 342 342 1.68158728333671e-03 + 343 343 1.07265952738963e-06 + 344 343 3.52526788611883e-06 1.21350869957700e-03 + 345 343 -4.23518121390441e-07 -1.84795565755904e-05 5.10830394872984e-07 + 346 343 -3.64244160202120e-06 7.61961345846920e-04 -1.24481914233700e-05 + 346 346 6.08558305178431e-04 + 347 347 8.99856433367071e-07 + 348 347 -5.83288753678453e-06 1.73673156179342e-04 + 349 347 -1.79697748476046e-09 -2.29812838076259e-06 6.26336257228389e-08 + 350 347 -4.45429163571809e-06 5.91638158232869e-05 -8.29373333526046e-07 + 350 350 3.47640141829430e-05 + 351 351 6.31618216809286e-08 + 352 351 -1.84576705381075e-06 2.20599360275040e-03 + 353 351 -4.66808411156481e-09 -1.56457049730309e-06 2.46393520401283e-07 + 354 351 2.87655667218045e-07 7.39604423771428e-05 1.36167832269862e-06 + 354 354 2.49204457746566e-05 + 355 355 1.48037105738326e-08 + 356 355 -2.60876064554246e-06 8.03626012528941e-03 + 357 357 1.79931547014719e-07 + 358 357 7.04214408901723e-06 3.07569047727955e-04 + 359 357 1.68368333248048e-07 5.76964022335131e-06 2.81596607035894e-07 + 360 357 9.16986136162563e-05 3.21852071597351e-03 1.36849604149256e-04 + 360 360 8.62519800901377e-02 + 361 361 3.07400562480569e-07 + 362 361 -1.62768475489298e-05 9.34632276617515e-04 + 363 361 -3.15869678724953e-07 1.52631843803321e-05 1.01037869038753e-06 + 364 361 -7.72127044131865e-06 3.79897450140171e-04 2.11243811690168e-06 + 364 364 7.23587716718940e-04 + 365 361 2.22800187414192e-07 -1.07386653926269e-05 -2.95907976214394e-07 + 365 364 -1.04768506297520e-05 2.52882907425350e-07 + 366 361 -3.20337375505958e-06 1.55667427668209e-04 -3.09992872385710e-06 + 366 364 3.11819425944745e-04 -4.45467197432103e-06 1.90589160282195e-04 + 367 367 3.03612680085767e-09 + 368 367 -2.39808138253097e-07 2.76537925879429e-05 + 369 369 7.21832912462787e-06 + 370 369 -3.87368837819623e-04 2.14161753691149e-02 + 371 371 2.78468714256563e-06 + 372 371 -2.23074690545696e-04 1.83504811796539e-02 + 373 373 9.99003406855588e-07 + 374 373 7.37992819757229e-05 1.06490220889923e-02 + 375 375 7.21832912462787e-06 + 376 375 -3.87368837819623e-04 2.14161753691149e-02 + 377 377 2.78468714256563e-06 + 378 377 -2.23074690545696e-04 1.83504811796539e-02 + 379 379 9.99003406855588e-07 + 380 379 7.37992819757229e-05 1.06490220889923e-02 + 381 381 3.38787327249529e-08 + 382 381 2.20839906979627e-06 1.99357844365355e-03 + 383 383 3.03612680085767e-09 + 384 383 -2.39808138253097e-07 2.76537925879429e-05 + 385 385 3.03612680085767e-09 + 386 385 -2.39808138253097e-07 2.76537925879429e-05 + 387 387 7.48781012148907e-07 + 388 387 3.82992844232214e-06 5.48474576451763e-05 + 389 389 1.11186291661805e-07 + 390 389 1.58373783566380e-06 3.25230264675211e-05 + 391 391 6.48938401625434e-07 + 392 391 -8.30781747869637e-05 1.56819391326189e-02 + 393 391 5.37830100759222e-08 -1.83168264389704e-05 3.31694955356568e-08 + 394 391 -1.24468369220157e-07 5.63785020793555e-05 -1.21514814841055e-07 + 394 394 7.28992800248338e-07 + 395 395 4.42284833854863e-09 + 396 395 -1.87667829078238e-08 2.09698258837504e-07 + 397 397 1.67420842960580e-06 + 398 397 -1.74581429059140e-07 4.31240470132893e-05 + 399 397 6.72161395305527e-07 3.90994330946211e-06 1.48762683305390e-06 + 400 397 1.02627183961880e-04 6.77581305886904e-04 1.86674358606534e-04 + 400 400 2.60409569778680e-02 + 401 401 1.81278163017912e-08 + 402 401 -8.30733534950918e-08 1.24949591163020e-06 + 403 403 1.22007415192827e-07 + 404 403 4.04303269396918e-06 2.15911380541572e-03 + 405 405 8.94923218297265e-08 + 406 405 -1.75312987023856e-05 6.68520864312058e-03 + 407 407 3.61958062498174e-08 + 408 407 8.50487264257043e-07 9.80048690953665e-04 + 409 409 8.94923218297265e-08 + 410 409 -1.75312987023856e-05 6.68520864312058e-03 + 411 411 3.61958062498174e-08 + 412 411 8.50487264257043e-07 9.80048690953665e-04 + 413 413 1.78446181901817e-07 + 414 413 2.36158053926059e-06 1.15241719959800e-04 + 415 415 6.65677247832789e-06 + 416 415 9.73450212228702e-04 1.50520985536037e-01 + 417 417 2.83092960699795e-07 + 418 417 1.36804625765889e-05 1.60067132952974e-03 + 419 419 1.09027886927689e-06 + 420 419 2.46808399422291e-04 8.33746763509530e-02 + 421 421 1.30746680806945e-06 + 422 421 -3.44635402644593e-05 1.21727731083336e-03 + 423 423 3.29261433569498e-04 + 424 423 -2.95375594573155e-03 2.74598798237114e-02 + 425 425 1.45791127272079e-06 + 426 425 -9.47259367932174e-06 6.77927420560933e-05 + 427 427 9.19697507136973e-08 + 428 427 -3.23607722416184e-05 1.75013896545094e-02 + 429 429 8.02727230263164e-08 + 430 429 -1.24552096298072e-06 5.71013718475453e-04 + 431 429 -4.34250907704345e-09 -2.80387057699896e-06 2.56829252651060e-07 + 432 429 5.10530076226739e-07 6.88192227140081e-05 1.03192067053197e-06 + 432 432 3.19511610588298e-05 + 433 433 1.26707458681566e-08 + 434 433 -1.33031783632189e-06 1.89640997036647e-04 + 435 435 5.06178804282098e-07 + 436 435 -7.39983293262291e-06 3.59035630376560e-04 + 437 437 2.56165583615426e-09 + 438 437 -5.29698341683168e-07 1.99005605042260e-04 + 439 439 3.04853570570028e-07 + 440 439 2.77002532738081e-04 2.88909008705439e-01 + 441 441 1.37096551530584e-07 + 442 441 2.13264963667378e-06 2.18425847895200e-04 + 443 441 3.18619110107991e-08 -2.29981907338666e-06 1.29168628934825e-07 + 444 441 -3.19974243439008e-06 2.25180167822265e-04 -1.37780838895462e-05 + 444 444 1.68158728333671e-03 + 445 445 1.07265952738963e-06 + 446 445 3.52526788611883e-06 1.21350869957700e-03 + 447 445 -4.23518121390441e-07 -1.84795565755904e-05 5.10830394872984e-07 + 448 445 -3.64244160202120e-06 7.61961345846920e-04 -1.24481914233700e-05 + 448 448 6.08558305178431e-04 + 449 449 2.72347326827962e-08 + 450 449 -1.25492569289979e-05 6.54940798681971e-03 + 451 451 2.72347326827962e-08 + 452 451 -1.25492569289979e-05 6.54940798681971e-03 + 453 453 4.50848191149754e-07 + 454 453 3.59220095596736e-05 3.39545072340306e-03 + 455 455 5.76874720899993e-08 + 456 455 2.78532898517247e-06 7.09881769626860e-04 + 457 455 8.94235163721115e-10 -9.31930152693526e-08 4.99729098945382e-07 + 458 455 7.04638387363886e-10 -3.10016259683878e-06 -3.93014903480256e-06 + 458 458 1.54310374072277e-04 + 459 455 1.04813075187712e-08 -3.91465249229382e-07 2.91769429278601e-07 + 459 458 1.60457189583834e-05 6.84823772378310e-06 + 460 455 1.57193355869175e-06 -7.00462857213620e-05 4.40057618260321e-05 + 460 458 2.44351697219747e-03 9.53013663255691e-04 1.37056038379640e-01 + 461 461 1.10897576932616e-08 + 462 461 -7.38054695045721e-07 7.30520624334054e-05 + 463 463 1.29042208943570e-07 + 464 463 4.98432816391972e-06 5.42355960795974e-04 + 465 465 6.25275512618660e-07 + 466 465 -6.12458855859310e-07 5.16920542581029e-03 + 467 465 1.19430479955101e-08 4.50516203356563e-06 4.87458642512427e-08 + 468 465 -2.38248621889223e-06 -7.81987962619300e-04 -1.25905456962875e-06 + 468 468 1.32003823526674e-03 + 469 469 4.39773441308370e-08 + 470 469 -1.44960171433878e-07 1.26286708897675e-03 + 471 471 1.63639614355779e-08 + 472 471 -2.18135060755928e-06 3.84104783118742e-04 + 473 473 1.60303644955248e-08 + 474 473 -2.12300645034258e-06 3.69345477975796e-04 + 475 475 2.72347326827962e-08 + 476 475 -1.25492569289979e-05 6.54940798681971e-03 + 477 477 2.46596106456528e-05 + 478 477 3.03791730634052e-03 3.80810768376883e-01 + 479 479 1.22430874980539e-06 + 480 479 1.88250129909810e-05 5.11085256874184e-04 + 481 479 -2.66828152208203e-09 -4.12788115289584e-09 2.18902574041816e-07 + 482 479 -9.01906486159753e-08 -7.31975492132667e-06 3.75375739316041e-06 + 482 482 2.24527026383486e-04 + 483 483 1.05598583751900e-06 + 484 483 -2.68438395193931e-06 2.22992979296916e-04 + 485 483 -3.17726653208918e-07 7.34443000819783e-06 3.16780984235001e-07 + 486 483 7.71774467298602e-07 6.40001507759629e-05 2.04578668325891e-06 + 486 486 2.50559271085736e-05 + 487 487 1.46197457189658e-07 + 488 487 -9.65624158615232e-06 1.23886604701762e-03 + 489 487 2.03045865613938e-08 -3.59034436551839e-06 4.76209282905310e-07 + 490 487 -1.30083740303037e-07 8.58629531830173e-05 1.62984940329698e-06 + 490 490 2.70101405447079e-05 + 491 487 -6.11395004904100e-09 6.26091346679016e-07 -1.14720954634735e-09 + 491 490 -2.45076920940183e-09 4.02990104332933e-07 + 492 487 1.21025317993314e-07 1.41698678394895e-05 -1.33643394163291e-07 + 492 490 1.26116644148419e-05 -7.66367494017277e-05 1.67609283816218e-02 + 493 493 9.51941584179068e-08 + 494 493 1.30469393735214e-05 2.74190163720278e-03 + 495 495 3.50949503731197e-07 + 496 495 -9.78959163341104e-07 8.65577365824349e-05 + 497 497 3.67053116112154e-07 + 498 497 7.57995562000486e-06 5.59978431159521e-04 + 499 499 4.09510500795515e-07 + 500 499 7.80394315058075e-06 9.75165826684951e-04 + 501 501 8.46465376709242e-09 + 502 501 9.04221738174903e-07 1.23316721856867e-04 + 503 501 -2.85341209617911e-09 -3.08626698057507e-07 1.71163442734952e-07 + 504 501 -5.57655778647682e-08 -6.03291786228880e-06 -1.89485432928136e-06 + 504 504 2.27445359212802e-04 + 505 501 -1.24847346959575e-09 -1.19806094324686e-07 -1.72146113004988e-07 + 505 504 8.58835233391370e-06 3.97925535311335e-07 + 506 501 -3.88358889741283e-10 -3.64791989443628e-08 -9.37193088568638e-08 + 506 504 3.07766367399618e-06 1.68072479226072e-07 8.98965847917911e-08 + 507 507 1.68091724091221e-08 + 508 507 -1.03834682953092e-07 1.52833824793789e-06 + 509 509 3.41938515394024e-05 + 510 509 2.78126564250672e-03 2.32707216095761e-01 + 511 511 8.46465376709242e-09 + 512 511 9.04221738174903e-07 1.23316721856867e-04 + 513 511 -2.85341209617911e-09 -3.08626698057507e-07 1.71163442734952e-07 + 514 511 -5.57655778647682e-08 -6.03291786228880e-06 -1.89485432928136e-06 + 514 514 2.27445359212802e-04 + 515 511 -1.24847346959575e-09 -1.19806094324686e-07 -1.72146113004988e-07 + 515 514 8.58835233391370e-06 3.97925535311335e-07 + 516 511 -3.88358889741283e-10 -3.64791989443628e-08 -9.37193088568638e-08 + 516 514 3.07766367399618e-06 1.68072479226072e-07 8.98965847917911e-08 + 517 517 1.68091724091221e-08 + 518 517 -1.03834682953092e-07 1.52833824793789e-06 + 519 519 3.41938515394024e-05 + 520 519 2.78126564250672e-03 2.32707216095761e-01 + 521 521 7.83007032906665e-07 + 522 521 -1.56712514106401e-06 3.89993238525916e-05 + 523 521 1.27103434799113e-07 2.03111192205683e-06 3.35964267630468e-07 + 524 521 1.65886592535025e-05 2.87432902287774e-04 4.12685169750434e-05 + 524 524 5.25554521918462e-03 + 525 525 3.78279483878985e-08 + 526 525 2.21298304946450e-06 3.09796441693005e-04 + 527 527 2.98358620525200e-07 + 528 527 3.72313843165071e-05 1.75415681872776e-02 + 529 529 2.46587290900260e-08 + 530 529 -3.32084363194395e-06 6.28461837837755e-04 + 531 531 2.18560824792031e-06 + 532 531 8.58772859871811e-05 5.31407832802926e-03 + 533 531 6.79587837714913e-07 6.26836214435765e-05 8.91070109392314e-07 + 534 531 6.22367898954691e-07 8.22851025985264e-05 1.32210176526223e-06 + 534 534 2.41663844326008e-06 + 535 535 1.21470705251895e-07 + 536 535 4.76999524176291e-06 7.69076399698827e-04 + 537 535 1.35648677234963e-10 3.30661268486283e-08 3.52109161542660e-08 + 538 535 1.49528147758440e-08 1.47160641106451e-06 1.23465626109886e-06 + 538 538 6.09723743840638e-05 + 539 539 4.05863062516893e-05 + 540 539 1.85726460891244e-03 8.71828935511515e-02 + 541 541 1.31026558706970e-07 + 542 541 -5.87882350272996e-06 2.90484956640388e-04 + 543 543 1.62201703749211e-08 + 544 543 3.62201215789412e-07 1.11603584434073e-05 + 545 545 3.33054689519535e-08 + 546 545 8.33862695655287e-07 9.37411775511898e-04 + 547 547 6.65318136115849e-09 + 548 547 -6.96842084003594e-08 2.10111537849063e-06 + 549 549 2.42825721894506e-08 + 550 549 -2.91991836773328e-06 5.27464910098498e-04 + 551 551 8.98268493838605e-08 + 552 551 4.65229052265078e-06 1.25577753592169e-03 + 553 553 7.36723048194521e-08 + 554 553 4.26954729239784e-06 2.81117584146126e-04 + 555 555 6.77998091176347e-08 + 556 555 -1.30421053557778e-05 2.74588743600925e-03 + 557 557 5.08991239531465e-07 + 558 557 -3.05248004771322e-05 5.39174936181015e-03 + 559 559 9.49218471983262e-08 + 560 559 2.32235279308343e-06 1.78973307375709e-04 + 561 561 7.84415087940132e-08 + 562 561 -1.31620448341392e-06 7.71007042108213e-05 + 563 563 3.42460858677571e-07 + 564 563 3.75736389100778e-06 6.90255164542080e-04 + 565 565 9.49218471983262e-08 + 566 565 2.32235279308343e-06 1.78973307375709e-04 + 567 567 7.84415087940132e-08 + 568 567 -1.31620448341392e-06 7.71007042108213e-05 + 569 569 3.42460858677571e-07 + 570 569 3.75736389100778e-06 6.90255164542080e-04 + 571 571 4.46983292924780e-08 + 572 571 -4.17846826278608e-06 6.56319502437226e-03 + 573 573 5.14479988082619e-08 + 574 573 5.39871093082500e-06 2.21562524623799e-03 + 575 575 4.41410050344940e-07 + 576 575 -1.34665593656529e-05 7.02052279301071e-03 + 577 577 3.82403550049241e-07 + 578 577 4.07703245460009e-05 4.57797049040725e-03 + 579 579 1.04018497176122e-05 + 580 579 -3.02806061248192e-03 8.93520229415945e-01 +-SOLUTION/MATRIX_ESTIMATE L COVA +%ENDSNX diff --git a/src/test/resources/sp3/missing-agency.sp3 b/src/test/resources/sp3/missing-agency.sp3 new file mode 100644 index 0000000000..0a40177310 --- /dev/null +++ b/src/test/resources/sp3/missing-agency.sp3 @@ -0,0 +1,25 @@ +#cP2001 8 8 0 0 0.00000000 1 ORBIT IGS97 HLM +## 1126 259200.00000000 900.00000000 52129 0.0000000000000 ++ 26 G01G02G03G04G05G06G07G08G09G10G11G13G14G17G18G20G21 ++ G23G24G25G26G27G28G29G30G31 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 +++ 7 8 7 8 6 7 7 7 7 7 7 7 7 8 8 7 9 +++ 9 8 6 8 7 7 6 7 7 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 G 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 +/* ULTRA ORBIT COMBINATION FROM WEIGHTED AVERAGE OF: +/* cou esu gfu jpu siu usu +/* REFERENCED TO cou CLOCK AND TO WEIGHTED MEAN POLE: +/* CLK ANT Z-OFFSET (M): II/IIA 1.023; IIR 0.000 +* 2001 8 8 0 0 0.00000000 +PG01 -11044.805800 -10475.672350 21929.418200 189.163300 18 18 18 219 +EOF diff --git a/src/test/resources/sp3/nsgf.orb.stella.v00.sp3.gz b/src/test/resources/sp3/nsgf.orb.stella.v00.sp3.gz new file mode 100644 index 0000000000..0e0e041b45 Binary files /dev/null and b/src/test/resources/sp3/nsgf.orb.stella.v00.sp3.gz differ