Solar eclipses by Moon

Hello,

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.

SunEclipse.java (8.3 KB)

EclipseAndSunVisibilityDetector.java (6.5 KB)

This is a great idea, and a fun one!

I guess it could be added to the tutorials.

Good one, Sebastien!

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());