Hello everyone,
I am new to Orbital mechanics and have put together some code that other folks might find helpful. The code demonstrates the use of Orekit for satellite propagation in a low Earth orbit If there are any mistakes, point them out and I will fix them.
If you run the code you can drop the CZML file in an online viewer
SatellitePropEtude.java (7.8 KB)
package com.code.example;
import org.hipparchus.ode.events.Action;
import org.orekit.bodies.GeodeticPoint;
import org.orekit.bodies.OneAxisEllipsoid;
import org.orekit.data.DataContext;
import org.orekit.data.DataProvidersManager;
import org.orekit.data.DirectoryCrawler;
import org.orekit.frames.Frame;
import org.orekit.frames.FramesFactory;
import org.orekit.frames.TopocentricFrame;
import org.orekit.orbits.KeplerianOrbit;
import org.orekit.orbits.Orbit;
import org.orekit.orbits.PositionAngleType;
import org.orekit.propagation.SpacecraftState;
import org.orekit.propagation.analytical.KeplerianPropagator;
import org.orekit.propagation.events.ElevationDetector;
import org.orekit.propagation.events.EventDetector;
import org.orekit.propagation.events.handlers.EventHandler;
import org.orekit.time.AbsoluteDate;
import org.orekit.utils.Constants;
import org.orekit.utils.IERSConventions;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
/**
* This class demonstrates the use of Orekit for satellite propagation.
*/
public class SatellitePropEtude {
// semi major axis in meters (Earth radius + altitude)
private static final double SEMI_MAJOR_AXIS = 6378137.0 + 500000;
private static final double ECCENTRICITY = 0.01;
private static final double INCLINATION = Math.toRadians(38.0293);
private static final double PERIGEE_ARGUMENT = 0.0;
// AKA, right ascension of ascending node
private static final double RIGHT_ASCENSION = Math.toRadians(78.4767);
private static final double MEAN_ANOMALY = 0.0;
private static final double MU = 3.986004415e+14;
/**
* The main method for the SatellitePropEtude class
* @param args the command line arguments (not used)
*/
public static void main(String[] args) {
try {
// Load Orekit data
File orekitData = new File("/Users/hobojoe/orekit-data");
loadOrekitData(orekitData);
// Set up an event detector. This will trigger an event when the satellite's elevation above the horizon at a specific location exceeds a certain threshold.
EventDetector detector = setupDetector();
// Set the initial date. This is the date at which the orbit is defined.
AbsoluteDate initialDate = AbsoluteDate.J2000_EPOCH;
// Set the inertial frame. This is the frame in which the orbit is defined.
Frame inertialFrame = FramesFactory.getICRF();
// Create the initial orbit. This defines the initial state of the satellite.
Orbit initialOrbit = createInitialOrbit(SEMI_MAJOR_AXIS, ECCENTRICITY, INCLINATION, PERIGEE_ARGUMENT, RIGHT_ASCENSION, MEAN_ANOMALY, inertialFrame, initialDate, MU);
// Set the propagator. This will be used to propagate the orbit over time.
KeplerianPropagator propagator = new KeplerianPropagator(initialOrbit);
// Add the event detector to the propagator.
propagator.addEventDetector(detector);
// Propagate the orbit for 90 minutes (typical period for LEO satellites).
AbsoluteDate finalDate = initialDate.shiftedBy(90.0 * 60.0);
// Write the propagated orbit to a CZML file. This can be used to visualize the orbit.
writeCZML(initialDate, finalDate, propagator);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Loads Orekit data from a specified directory.
* @param orekitData the directory containing the Orekit data
*/
private static void loadOrekitData(File orekitData) {
DataProvidersManager manager = DataContext.getDefault().getDataProvidersManager();
manager.addProvider(new DirectoryCrawler(orekitData));
}
/**
* Sets up an event detector for the satellite propagation.
* The detector triggers an event when the satellite's elevation above the horizon at a specific location exceeds a certain threshold.
* @return the event detector
*/
private static EventDetector setupDetector() {
OneAxisEllipsoid earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
Constants.WGS84_EARTH_FLATTENING,
FramesFactory.getITRF(IERSConventions.IERS_2010, true));
GeodeticPoint charlottesville = new GeodeticPoint(Math.toRadians(38.0293), Math.toRadians(-78.4767), 0.0);
TopocentricFrame charlottesvilleFrame = new TopocentricFrame(earth, charlottesville, "Charlottesville");
double maxcheck = 60.0;
double threshold = 0.001;
double elevation = Math.toRadians(5.0);
EventDetector detector = new ElevationDetector(maxcheck, threshold, charlottesvilleFrame).
withConstantElevation(elevation).
withHandler(new EventHandler() {
@Override
public Action eventOccurred(SpacecraftState spacecraftState, EventDetector eventDetector, boolean b) {
System.out.println("BANG! ->" + spacecraftState.getDate().toString() + " : " + spacecraftState.getOrbit().getPVCoordinates().toString());
return Action.CONTINUE;
}
});
return detector;
}
/**
* Creates the initial orbit for the satellite propagation.
* @param a the semi-major axis
* @param e the eccentricity
* @param i the inclination
* @param omega the perigee argument
* @param raan the right ascension of the ascending node
* @param lM the mean anomaly
* @param inertialFrame the frame in which the orbit is defined
* @param initialDate the date at which the orbit is defined
* @param mu the gravitational parameter
* @return the initial orbit
*/
private static Orbit createInitialOrbit(double a, double e, double i, double omega, double raan, double lM, Frame inertialFrame, AbsoluteDate initialDate, double mu) {
return new KeplerianOrbit(a, e, i, omega, raan, lM, PositionAngleType.MEAN,
inertialFrame, initialDate, mu);
}
/**
* Writes the propagated orbit to a CZML file.
* @param initialDate the start date of the propagation
* @param finalDate the end date of the propagation
* @param propagator the propagator used for the propagation
*/
private static void writeCZML(AbsoluteDate initialDate, AbsoluteDate finalDate, KeplerianPropagator propagator) {
try (FileWriter writer = new FileWriter("orbit.czml")) {
// Write the CZML header
writer.write("[\n");
writer.write("{\"id\":\"document\",\"version\":\"1.0\"},\n");
// Write the satellite's path
writer.write("{\"id\":\"satellite\",\"availability\":\"" + initialDate + "/" + finalDate + "\",\n");
writer.write("\"position\":{\"interpolationAlgorithm\":\"LAGRANGE\",\"interpolationDegree\":5,\"epoch\":\"" + initialDate + "\",\"cartesian\":[");
// Write the satellite's position at each time step
AbsoluteDate date = initialDate;
while (date.compareTo(finalDate) <= 0) {
SpacecraftState state = propagator.propagate(date);
double[] position = state.getPVCoordinates().getPosition().toArray();
writer.write("\n" + date.durationFrom(initialDate) + "," + position[0] + "," + position[1] + "," + position[2]);
date = date.shiftedBy(60.0);
if (date.compareTo(finalDate) <= 0) {
writer.write(",");
}
}
// Write the CZML footer
writer.write("\n]},\n");
writer.write("\"path\":{\"show\":[{\"boolean\":true}]},\n");
writer.write("\"point\":{\"pixelSize\":5,\"color\":{\"rgba\":[255,255,0,255]}}}\n");
writer.write("]\n");
} catch (IOException e2) {
e2.printStackTrace();
}
}
}