Just for fun, I was wondering whether it would be possible to compute solar eclipses by the Moon as seen by a ground observer, for example to determine the solar eclipse visible from Europe on August 12, 2026.
It is possible, but we have to cheat a little, because Orekit is primarily designed to compute events along satellite trajectories rather than for ground observers. The workaround is to build a pseudo-orbit from the ground observer’s position and velocity, using a list of absolute PV states.
We also need to build a custom event detector, because the standard EclipseDetector does not take the Sun’s elevation into account. As a result, it could detect an eclipse occurring below the horizon, i.e. during nighttime.
I’ve put my work together in two classes:
SunEclipse — the main class
EclipseAndSunVisibilityDetector — the customized event detector
I’m sharing them here in case this is useful to anyone else, or if anyone has suggestions for improving the approach.
Here’s a snippet of how to do it with upcoming Orekit 14.0, without even using precise ephemerides:
// Inputs
final var date = new AbsoluteDate(2026, 1, 1, 0, 0, 0, TimeScalesFactory.getUTC());
final GeodeticPoint point = new GeodeticPoint(FastMath.toRadians(40.), 0., 0.);
final double minimumDuration = 3600.;
final double window = 86400. * 365.25;
// Instantiations
final Frame frame = FramesFactory.getGCRF();
final PVCoordinatesProvider moon = new AnalyticalLunarPositionProvider();
final PVCoordinatesProvider sun = new AnalyticalSolarPositionProvider();
final double moonApparentAngle = Constants.MOON_EQUATORIAL_RADIUS / moon.getPosition(date, frame).getNorm();
final ReferenceEllipsoid ellipsoid = ReferenceEllipsoid.getWgs84(FramesFactory.getGTOD(true));
final TopocentricFrame topocentricFrame = new TopocentricFrame(ellipsoid, point, "");
// Build event detector
final EventFunction conjunctionFunction = state -> moonApparentAngle - Vector3D.angle(sun.getPosition(state.getDate(), topocentricFrame),
moon.getPosition(state.getDate(), topocentricFrame));
final EventFunction sunFunction = state -> topocentricFrame.getElevation(sun.getPosition(state.getDate(), topocentricFrame), topocentricFrame, state.getDate());
final BooleanDetector detector = BooleanDetector.andCombine(EventDetector.of(conjunctionFunction),
EventDetector.of(sunFunction)).withMaxCheck(minimumDuration);
// Detection
final EventsLogger logger = new EventsLogger();
final var orbit = new EquinoctialOrbit(1e8, 0., 0., 0., 0., 0., PositionAngleType.MEAN, frame, date, Constants.EGM96_EARTH_MU);
final var propagator = new KeplerianPropagator(orbit);
propagator.addEventDetector(logger.monitorDetector(detector));
propagator.propagate(date.shiftedBy(window));
final List<EventsLogger.LoggedEvent> events = logger.getLoggedEvents();
System.out.println(events.getFirst().getState().getDate());
System.out.println(events.get(1).getState().getDate());