This is the main class:
// Gravitational parameter of the moon (MU):
final double moon_MU = 4.90280105600000e12;
// Set the initial date and duration for the orbit propagation: AbsoluteDate initialDate = new AbsoluteDate(2023, 1, 1, 0, 0, 0., TimeScalesFactory.getUTC());
double dt = 1; // [days]
double intTime = dt * 24 * 3600; // [s]
AbsoluteDate finalDate = initialDate.shiftedBy(intTime);
// Create the initial orbit using Keplerian elements:
Orbit OriginalOrbit = new DefaultSatOrbits().getComOrbit(initialDate);
// -------------------------------------------- //
// Propagator Setup with Earth's gravity field
// -------------------------------------------- //
// Create a lunar orbit propagator generator:
MoonPropagatorGenerator MoonPropagatorGeneratorWithHarmonics = new MoonPropagatorGenerator();
// Obtain the propagator for the given orbit:
NumericalPropagator propagatorWithHarmonics = MoonPropagatorGeneratorWithHarmonics.getPropagator(OriginalOrbit, false, true);
// Generate ephemeris (pre-calculated trajectory over time):
final EphemerisGenerator generatorWithHarmonics = propagatorWithHarmonics.getEphemerisGenerator();
// Propagate for dt [days]: propagatorWithHarmonics.propagate(initialDate, finalDate);
// Retrieve the ephemeris bounded propagator:
BoundedPropagator bpWithHarmonics = generatorWithHarmonics.getGeneratedEphemeris();
// Get the final spacecraft state after propagation:
SpacecraftState finalStateWithHarmonics = bpWithHarmonics.propagate(finalDate); KeplerianOrbit finalOrbitWithHarmonics = new KeplerianOrbit(finalStateWithHarmonics.getOrbit());
CartesianOrbit finalCartesianOrbitWithHarmonics = new CartesianOrbit(finalStateWithHarmonics.getOrbit());
// ---------------------------------------- //
// Propagator Setup as Third Body
// ---------------------------------------- //
// Create a lunar orbit propagator generator:
MoonPropagatorGenerator MoonPropagatorGeneratorThirdBody = new MoonPropagatorGenerator();
// Obtain the propagator for the given orbit:
NumericalPropagator propagatorThirdBody = MoonPropagatorGeneratorThirdBody.getPropagator(OriginalOrbit, false, false);
// Generate ephemeris (pre-calculated trajectory over time):
final EphemerisGenerator generatorThirdBody = propagatorThirdBody.getEphemerisGenerator();
// Propagate for dt [days]:
propagatorThirdBody.propagate(initialDate, finalDate);
// Retrieve the ephemeris bounded propagator:
BoundedPropagator bpThirdBpdy = generatorThirdBody.getGeneratedEphemeris();
// Get the final spacecraft state after propagation:
SpacecraftState finalStateThirdBody = bpThirdBpdy.propagate(finalDate); KeplerianOrbit finalOrbitThirdBody = new KeplerianOrbit(finalStateThirdBody.getOrbit());
CartesianOrbit finalCartesianOrbitThirdBody = new CartesianOrbit(finalStateThirdBody.getOrbit());
This is the class with the propagator and the force model:
public class MoonPropagatorGenerator {
// Builder for creating the ODE integrator used in numerical propagation
private ODEIntegratorBuilder odeIntegratorBuilder;
// Adaoptive Seto Integrator
private AdaptiveStepsizeIntegrator integrator;
// Constants for the Moon's flattening and reference frames
final double moonFlattening = 0.0012;
final Frame LME2000 = CelestialBodyFactory.getMoon().getInertiallyOrientedFrame(); // Use an Earth body-fixed frame
final Frame MoonPA = CelestialBodyFactory.getMoon().getBodyOrientedFrame();
// Define Moon ellipsoid
final OneAxisEllipsoid moonEllipsoid = new OneAxisEllipsoid(Constants.MOON_EQUATORIAL_RADIUS, moonFlattening, MoonPA);
// Forces to include in the propagator:
List<ForceModel> perturbingForces = new ArrayList<>();
final double satMass = 400; // [kg]
/**
* Default constructor for MoonPropagatorGenerator.
* Initializes the class but does not configure the propagator until the
* {@link #getPropagatorBuilder(Orbit, boolean)} or
* {@link #getPropagator(Orbit, boolean)} method
* is called.
*/
public MoonPropagatorGenerator() {
}
/**
* Sets up the propagator configuration, including:
* <ul>
* <li>Lunar gravity field harmonics with a degree and order of 165 for
* precision</li>
* <li>Third-body attraction from Earth, Sun, and other Planets</li>
* <li>A Dormand-Prince 8(5,3) integrator with adaptive step sizes</li>
* </ul>
* This method configures the necessary force models and integrator for the
* propagator with the full force model
*/
private void setUp() {
// clear forces list
perturbingForces.clear();
// Define lunar gravitational force model based on the harmonics data
perturbingForces.add(configureLunarGravityField(165, 165));
// Define Third Bodies forces
perturbingForces.addAll(configureThirdBodyForces(true, true, true, null));
// Defining SRP force
perturbingForces.add(configureSRP(1.0, moonEllipsoid));
}
/**
* Sets up the propagator configuration, including:
* <ul>
* <li>Lunar gravity field harmonics with a degree and order of 165 for
* precision</li>
* <li>Earth's gravity field harmonics with a degree and order of 2 for
* precision</li>
* <li>Third-body attraction from Sun and other Planets</li>
* <li>A Dormand-Prince 8(5,3) integrator with adaptive step sizes</li>
* </ul>
* This method configures the necessary force models and integrator for the
* propagator with the full force model
*/
private void setUpFullModel() {
// clear forces list
perturbingForces.clear();
// Define lunar gravitational force model based on the harmonics data
perturbingForces.add(configureLunarGravityField(165, 165));
// Define the Earth's gravitational force model based on the harmonics data
perturbingForces.add(configureEarthGravityField(2, 2)); // Consider pertubing effects up to J2x2
// Define Third Bodies forces
perturbingForces.addAll(configureThirdBodyForces(false, true, true, null));
// DO NOT include Earth as third body, since its gravity field is modelled separately
// Defining SRP force
perturbingForces.add(configureSRP(1.0, moonEllipsoid));
}
private void setIntegrators(Orbit initialOrbit) {
// Define integration step parameters for the Dormand-Prince 8(5,3) integrator
double minStep = 0.001; // Minimum step size in seconds
double maxStep = 1000; // Maximum step size in seconds
double positionTolerance = 1e-6; // Tolerance for position error in meters
// Create the integrator builder with adaptive step size using Dormand-Prince
// method
odeIntegratorBuilder = new DormandPrince853IntegratorBuilder(minStep, maxStep, positionTolerance);
// Calculate tolerance arrays based on the initial orbit and position tolerance
double[][] tolerances = NumericalPropagator.tolerances(positionTolerance, initialOrbit, OrbitType.CARTESIAN);
// Create an adaptive step-size Dormand-Prince 8(5,3) integrator
integrator = new DormandPrince853Integrator(minStep, maxStep, tolerances[0],
tolerances[1]);
}
/**
* Configures the lunar gravity field force model.
*
* @param degree The degree of the gravity harmonics.
* @param order The order of the gravity harmonics.
* @return A {@link ForceModel} representing the lunar gravity field.
*/
public ForceModel configureLunarGravityField(int degree, int order) {
// Clear previously loaded gravity field data to ensure a clean state
GravityFieldFactory.clearPotentialCoefficientsReaders();
// Load ICGEM gravity field data for the Moon from the JGL165P1 file
ICGEMFormatReader moonPotentialCoefficientsReader = new ICGEMFormatReader("JGL165P1.gfc", false);
GravityFieldFactory.addPotentialCoefficientsReader(moonPotentialCoefficientsReader);
// Create a normalized spherical harmonics provider with degree and order
NormalizedSphericalHarmonicsProvider moonHarmonicsProvider = GravityFieldFactory.getNormalizedProvider(degree, order);
return new HolmesFeatherstoneAttractionModel(MoonPA, moonHarmonicsProvider);
}
/**
* Configure the Earth's gravity field force model.
*
* @param degree The degree of the gravity harmonics.
* @param order The order of the gravity harmonics.
* @return A {@link ForceModel} representing the Earth's gravity field.
*/
public ForceModel configureEarthGravityField(int degree, int order) {
// Clear previously loaded gravity field data to ensure a clean state
GravityFieldFactory.clearPotentialCoefficientsReaders();
// Load ICGEM gravity field data for the Earth from the EIGEN file
ICGEMFormatReader earthPotentialCoefficientsReader = new ICGEMFormatReader("EIGEN-6S4.gfc", false); // EIGEN-6C4.gfc
GravityFieldFactory.addPotentialCoefficientsReader(earthPotentialCoefficientsReader);
// Create a normalized spherical harmonics provider with degree and order
NormalizedSphericalHarmonicsProvider earthHarmonicsProvider = GravityFieldFactory.getNormalizedProvider(degree, order);
Frame earthItrf = FramesFactory.getITRF(IERSConventions.IERS_2010, false); // Use an Earth body-fixed frame
return new HolmesFeatherstoneAttractionModel(earthItrf, earthHarmonicsProvider);
}
/**
* Configures the Solar Radiation Pressure (SRP) force model.
*
* @param cr The coefficient of reflectivity.
* @param moonEllipsoid The Moon ellipsoid model.
* @return A {@link ForceModel} representing the SRP force.
*/
public ForceModel configureSRP(double cr, OneAxisEllipsoid moonEllipsoid) {
return new SolarRadiationPressure(
CelestialBodyFactory.getSun(),
moonEllipsoid,
new IsotropicRadiationSingleCoefficient(10, cr));
}
/**
* Configures a list of third-body gravitational forces.
*
* @param includeEarth Whether to include Earth's attraction.
* @param includeSun Whether to include the Sun's attraction.
* @param includePlanets Whether to include the major planets.
* @param additionalBodies List of additional third bodies to include.
* @return A {@link List} of {@link ForceModel} representing third-body forces.
*/
public List<ForceModel> configureThirdBodyForces(boolean includeEarth, boolean includeSun, boolean includePlanets,
List<ThirdBodyAttraction> additionalBodies) {
List<ForceModel> thirdBodyForces = new java.util.ArrayList<>();
if (includeEarth) {
thirdBodyForces.add(new ThirdBodyAttraction(CelestialBodyFactory.getEarth()));
}
if (includeSun) {
thirdBodyForces.add(new ThirdBodyAttraction(CelestialBodyFactory.getSun()));
}
// note: modify this list according to the bodies you want to include
if (includePlanets) {
thirdBodyForces.add(new ThirdBodyAttraction(CelestialBodyFactory.getVenus()));
thirdBodyForces.add(new ThirdBodyAttraction(CelestialBodyFactory.getMars()));
thirdBodyForces.add(new ThirdBodyAttraction(CelestialBodyFactory.getJupiter()));
thirdBodyForces.add(new ThirdBodyAttraction(CelestialBodyFactory.getSaturn()));
}
// note: this is here in case I missed something to consider
if (additionalBodies != null) {
thirdBodyForces.addAll(additionalBodies);
}
return thirdBodyForces;
}
/**
* Generates a NumericalPropagatorBuilder configured with the necessary force
* models and the initial orbit for the simulation.
*
* @param initialOrbit The initial orbit from which to propagate.
* @param addDesaturationManeuver Flag to include a desaturation maneuver.
* @return A configured {@link NumericalPropagatorBuilder} for the lunar orbit
* simulation.
*/
public NumericalPropagatorBuilder getPropagatorBuilder(Orbit initialOrbit, boolean addDesaturationManeuver,
boolean isFullModelNeeded) {
// Setting up integrators
setIntegrators(initialOrbit);
// Configure force models and integrator for the propagator
if (isFullModelNeeded) {
setUpFullModel();
} else {
setUp();
}
// Create a new numerical propagator builder using the provided initial orbit
NumericalPropagatorBuilder propagatorBuilder = new NumericalPropagatorBuilder(
initialOrbit, odeIntegratorBuilder, PositionAngleType.TRUE, 1);
// Add all perturbing forces
for (ForceModel force : perturbingForces) {
propagatorBuilder.addForceModel(force);
}
propagatorBuilder.setMass(satMass);
// Add attitude provider for VVLH frame
propagatorBuilder.setAttitudeProvider(new LofOffset(LME2000, LOFType.VVLH));
// Reset the orbit of the propagator with the provided initial orbit
propagatorBuilder.resetOrbit(initialOrbit);
// Conditionally add desaturation maneuver
if (addDesaturationManeuver) {
ImpulseManeuver desaturationManeuver = DesaturationManeuverComputation.DesMan(
(KeplerianOrbit) initialOrbit, LME2000);
propagatorBuilder.addImpulseManeuver(desaturationManeuver);
}
return propagatorBuilder;
}
/**
* Generates a fully configured {@link NumericalPropagator} for lunar orbit
* propagation.
*
* @param initialOrbit The initial orbit of the spacecraft.
* @param addDesaturationManeuver Flag to include a desaturation maneuver.
* @return A configured {@link NumericalPropagator} for the lunar orbit.
*/
public NumericalPropagator getPropagator(Orbit initialOrbit, boolean addDesaturationManeuver,
boolean isFullModelNeeded) {
// Setting up integrators
setIntegrators(initialOrbit);
// Configure force models and integrator for the propagator
if (isFullModelNeeded) {
setUpFullModel();
} else {
setUp();
}
// Create a new numerical propagator with the configured integrator
NumericalPropagator propagator = new NumericalPropagator(integrator);
propagator.setOrbitType(OrbitType.CARTESIAN);
// Add all perturbing forces
for (ForceModel force : perturbingForces) {
propagator.addForceModel(force);
}
// Set the initial state of the spacecraft using the provided orbit
propagator.setInitialState(new SpacecraftState(initialOrbit,satMass));
// Add attitude provider for VVLH frame
propagator.setAttitudeProvider(new LofOffset(LME2000, LOFType.VVLH));
// Conditionally add desaturation maneuver
if (addDesaturationManeuver) {
ImpulseManeuver desaturationManeuver = DesaturationManeuverComputation.DesMan(
(KeplerianOrbit) initialOrbit, LME2000);
propagator.addEventDetector(desaturationManeuver);
}
return propagator;
}
}
I propagate for 1 day and two different force models:
- the first one (setUpSimpleModel) considering the Earth as Third Body.
- the second one (setUpFullModel) considering the Earth’s gravity field with harmonics (order 2, degree 2), but not as third body.
It returns a difference in position vector of about 66 km.