Error with FootprintOverlapDetector

Hello community,

I am attempting to adapt a code I have that computes station+areas visibility windows so that it takes into account FOV. I have another topic open to deal with some issues with the station FOV Visibility of Location with FOV, but this topic is related to area FOV. I am getting the following error:

“Exception in thread “main” java.lang.OutOfMemoryError: Java heap space at org.hipparchus.util.FastMath.sinCos(FastMath.java:2635) at org.orekit.bodies.GeodeticPoint.getZenith(GeodeticPoint.java:127) at org.orekit.propagation.events.FootprintOverlapDetector.sample(FootprintOverlapDetector.java:174) at org.orekit.propagation.events.FootprintOverlapDetector.<init (FootprintOverlapDetector.java:108) at Simulator_Code.EventDetector_Constructor.getEvent_TargetArea_toml_FOV(EventDetector_Constructor.java:506)”

Below is the function I am using, which is incorporated into the main code. The line giving the error I have narrowed down to the actual event detector.

       List<AbsoluteDate> start = new ArrayList<>();
        List<AbsoluteDate> end = new ArrayList<>();
        List<String> order = new ArrayList<>();

        S2Point[] vertice_points = new S2Point[TargetArea.size()];

        // Create FOV for satellite
        FieldOfView fov = new CircularFieldOfView(Vector3D.PLUS_K, FastMath.toRadians(halfFOV), 0);

        //Points defining the Target Area

        for (int j = 0; j < TargetArea.size(); j++) {
            double altitude = FastMath.toRadians(TargetArea.get(j).getDouble("altitude")); // in meters
            double latitude = FastMath.toRadians(TargetArea.get(j).getDouble("latitude"));
            double longitude = FastMath.toRadians(TargetArea.get(j).getDouble("longitude"));
            GeodeticPoint vertice = new GeodeticPoint(latitude, longitude, altitude);
            Vector3D vertice_3d = earth.transform(vertice);
            vertice_points[j] = new S2Point(vertice_3d);
        }

        SphericalPolygonsSet target_area = new SphericalPolygonsSet(0.0001 , vertice_points);

        EventDetector geodetect = new FootprintOverlapDetector(fov, new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, Constants.WGS84_EARTH_FLATTENING, earthFrame),
                target_area, 10).withHandler((s, detector, increasing) -> {
                    if (increasing) {
                        end.add(s.getDate());
                        order.add("e");
                    } else {
                        start.add(s.getDate());
                        order.add("s");
                    }
                    return increasing ? Action.CONTINUE : Action.STOP;
                });

        return new Event_Manager(start, end, geodetect, order, id_TA);

    } catch (OrekitException oe) {
        System.err.println(oe.getLocalizedMessage());
        return null;
    }
}

I tried adjusting the sampling rate up to 10 but that led to another error. Any help would be appreciated!

Hello @francoC,

What is the size and number of points of the target area ?

Since it is an OutOfMemoryError it’s going to be hard to debug without the actual code.

Could you at least provide an example of what you’re doing with: the zone defined, the propagator used, the different variables in the code above that are not initialized ?
Something that we could run in standalone and that would illustrate your bug.

Beware that you are converting your altitude to radians, this is probably a bug in your code :wink:

Maxime, thank you for your reply.

About altitude, I am actually setting it to 0 to it would not give errors, but thank you for pointing it out as it will cause issues in the future. The function above is used within a for loop that iterates through 2 areas, composed of various vertices. At the end of this message I will include that list. There is another function within my code that reads the areas from a .toml file. For the purposes of this debugging, variable TargetArea is composed of 4 vertices New York, Chicago, Houston, and Jacksonville. The line is called like so:

                TLEPropagator Prop = TLEPropagator.selectExtrapolator(TLE_list.get(i));

                EventDetector_Constructor.Event_Manager add_event_detector = EventDetector_Constructor.getEvent_TargetArea_toml_FOV(TA_toml.getTables(TA_identifier),
                        TA_toml.getTables("TAs").get(j).getString("name_TA"), TA_toml.getTables("TAs").get(j).getString("ID_TA"), maxcheck, threshold, halfFOV); // constructs event

                Prop.addEventDetector(add_event_detector.event_detector); // propagates event

This is the list of vertices:

[[TAs]] 
name_TA = "USA East Coast"
ID_TA = "32"

[[TAs.vertices]]
latitude = 40.7128
longitude = -74.0060
altitude = 0.0
Point_ID = "1"
name_station = "New York"

[[TAs.vertices]]
latitude = 41.8781
longitude = -87.6298
altitude = 0.0
Point_ID = "2"
name_station = "Chicago"

[[TAs.vertices]]
latitude = 29.7604
longitude = -95.3698
altitude = 0.0
Point_ID = "3"
name_station = "Houston"

[[TAs.vertices]]
latitude = 30.3322
longitude = -81.6557
altitude = 0.0
Point_ID = "4"
name_station = "Jacksonville"

Hi @francoC,

It seems that you are using a 10 m step to sample a target area of about 1 million km**2:

 EventDetector geodetect = new FootprintOverlapDetector(fov, new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, Constants.WGS84_EARTH_FLATTENING, earthFrame),
                               target_area, 10)

A rough calculation with your data gives about 10 billion sample points, which is a lot, that would explain the memory error.

I’ll try playing around with that. On a previous version of my code, where I actually had 0.001 for the sampling rate, the code was working fine with no errors but I was using the GeographicalZoneDetector. Do you think that would change for FootprintOverlapDetector?

Also, is there a way I could maybe automate this? I use several areas of varying sizes, in some scenarios they are the same areas in others they are not, so it would be necessary to have the code work somewhat accurately at all times in every scenario.

Hi @francoC,

What do you mean by “0.001 for the sampling rate” for the GeographicZoneDetector ?
The difference between both detectors is that:

  • GeographicZoneDetector checks that a point (sub-satellite) is in an Earth-zone;
  • FootprintOverlapDetector, here you have two zones, the Earth-zone and the FOV. For each point sampled on the Earth zone (at 10m in your case), it checks if it’s in the FOV using somewhat the same method as the one used in GeographicZoneDetector.
    So you’re doing the same job but for N sample points (N being huge in your case)

See the g method of each detector to view the difference.

Note also that there is a known issue with FootPrintOverlapDetector opened recently by @greyskyy.

Maxime,

I was confused about the parameters taken by GeographicalZoneDetector. I believe my code works now, whether it is accurate or not I have yet to determine using STK or GMAT.

Could you give me some more detail about that bug you mentioned? I read the issue but it wasn’t specific about the kind of errors I should expect to get. The biggest area I work with is the whole mainland Australia, should I expect any issues with it?

Hi Franco,

I’m not sure, from the description of the issue it seems that country or continent-sized zones may be problematic.
I merely pointed the issue for you to be aware of it, I must admit I haven’t worked on this specific case sorry.
@greyskyy gave some examples of the issues he faced on this forum thread.

Hi Maxime,

I did a couple runs with the sampling rate of 10,000 and half-FOV of 70 degrees. I used STK to confirm my results, however they were not correct, neither was my ground station coverage code. I am not sure if there is a bug, or an error in my implementation. The first window for my sat (LEMUR-2-SPIRE-MINION) and the area described above in this thread using an SGP4 propagator was from Nov 13 03:01:15 to Nov 13 03:14:58.568 UTC, while the code computed from Nov 13 03:01:12 to Nov 13 03:07:50, as you can see a substantial difference in end times. The following windows up to Nov 15 began getting more and more spaced out from the correct values, starting and ending maybe 2 hours before/after in some cases. Any help would be appreciated.

Hi Franco,

Unfortunately I’ll be away this week so I won’t be able to properly help.
But maybe someone else from the community can !
For this I think you should provide a standalone code (with the TLE used, the attitude of the S/C and everything) and the expected results, that way it will be much easier for someone to run the code out of the box and investigate.

Maxime,

This is a runnable code that reads a TLE and list of coordinates/areas and outputs start+end times for windows of visibility. Excluding the orekit initialization for simplicity. Below it are the contents of the .txt files, copy exactly into Notepad or some other similar software. Each type (i.e. TLE, station, and areas) into its own .txt file.


        // Earth and frame
        final Frame inertialFrame = FramesFactory.getEME2000();
        final Frame earthFrame = FramesFactory.getITRF(IERSConventions.IERS_2010, true);
        final BodyShape earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
                Constants.WGS84_EARTH_FLATTENING, earthFrame);

        // define sat properties
        List<String> target_sat = Files.readAllLines(Paths.get("src/main/java/Test_TLE.txt"));
        String tle_line1 = target_sat.get(1);
        String tle_line2 = target_sat.get(2);
        TLE my_TLE = new TLE(tle_line1, tle_line2);
        int halfFOV = 80;
        FieldOfView fov = new CircularFieldOfView(Vector3D.PLUS_K, FastMath.toRadians(halfFOV), 0);

        // load target coordinates
        List<String> target_stations = Files.readAllLines(Paths.get("src/main/java/Test_GS.txt"));

        // create station 1
        double latitude_1 = FastMath.toRadians(Double.parseDouble(target_stations.get(1)));
        double longitude_1 = FastMath.toRadians(Double.parseDouble(target_stations.get(2)));
        double altitude_1 = Double.parseDouble(target_stations.get(3));
        double elevation_1 = FastMath.toRadians(Double.parseDouble(target_stations.get(4)));
        String name_station_1 = target_stations.get(5);
        GeodeticPoint station_geodetic_1 = new GeodeticPoint(latitude_1, longitude_1, altitude_1);
        TopocentricFrame stationFrame_1 = new TopocentricFrame(earth, station_geodetic_1, name_station_1);

        // create station 2
        double latitude_2 = FastMath.toRadians(Double.parseDouble(target_stations.get(8)));
        double longitude_2 = FastMath.toRadians(Double.parseDouble(target_stations.get(9)));
        double altitude_2 = Double.parseDouble(target_stations.get(10));
        double elevation_2 = FastMath.toRadians(Double.parseDouble(target_stations.get(11)));
        String name_station_2 = target_stations.get(12);
        GeodeticPoint station_geodetic_2 = new GeodeticPoint(latitude_2, longitude_2, altitude_2);
        TopocentricFrame stationFrame_2 = new TopocentricFrame(earth, station_geodetic_2, name_station_2);

        // load target areas
        List<String> target_areas = Files.readAllLines(Paths.get("src/main/java/Test_TA.txt"));

        // create area 1
        S2Point[] vertice_points_1 = new S2Point[4];
        GeodeticPoint vertice = new GeodeticPoint(FastMath.toRadians(Double.parseDouble(target_areas.get(0))), FastMath.toRadians(Double.parseDouble(target_areas.get(1))), Double.parseDouble(target_areas.get(2)));
        Vector3D vertice_3d = earth.transform(vertice);
        vertice_points_1[0] = new S2Point(vertice_3d);
        vertice = new GeodeticPoint(FastMath.toRadians(Double.parseDouble(target_areas.get(3))), FastMath.toRadians(Double.parseDouble(target_areas.get(4))), Double.parseDouble(target_areas.get(5)));
        vertice_3d = earth.transform(vertice);
        vertice_points_1[1] = new S2Point(vertice_3d);
        vertice = new GeodeticPoint(FastMath.toRadians(Double.parseDouble(target_areas.get(6))), FastMath.toRadians(Double.parseDouble(target_areas.get(7))), Double.parseDouble(target_areas.get(8)));
        vertice_3d = earth.transform(vertice);
        vertice_points_1[2] = new S2Point(vertice_3d);
        vertice = new GeodeticPoint(FastMath.toRadians(Double.parseDouble(target_areas.get(9))), FastMath.toRadians(Double.parseDouble(target_areas.get(10))), Double.parseDouble(target_areas.get(11)));
        vertice_3d = earth.transform(vertice);
        vertice_points_1[3] = new S2Point(vertice_3d);
        SphericalPolygonsSet target_area_1 = new SphericalPolygonsSet(0.0001 , vertice_points_1);

        // Propagation Characteristics
        AbsoluteDate start_date = my_TLE.getDate(); // Propagation starts from date of most recent TLE (Orekit)
        final double stepT = 60.; // step size of 1 minute for propagation
        AbsoluteDate final_date = start_date.shiftedBy(24*3600); // 1 day
        TLEPropagator Prop = TLEPropagator.selectExtrapolator(my_TLE);
        Prop.clearEventsDetectors();

        // set detectors
        final double maxcheck = 60;  // Checking every x second
        final double threshold = 0.001; //Convergence to 0.001 seconds
        final FieldOfViewDetector fd_1 = new FieldOfViewDetector(stationFrame_1, fov);
        final FieldOfViewDetector fd_2 = new FieldOfViewDetector(stationFrame_2, fov);
        final ElevationDetector ed_1 = new ElevationDetector(stationFrame_1).withConstantElevation(elevation_1);
        final ElevationDetector ed_2 = new ElevationDetector(stationFrame_2).withConstantElevation(elevation_2);

        // create detector station 1
        final EventDetector stationVisibility_1 = BooleanDetector.andCombine(ed_1, BooleanDetector.notCombine(fd_1)).
                withMaxCheck(maxcheck).withThreshold(threshold).
                withHandler((s, detector, increasing) -> {
                    if (increasing) {
                        System.out.println("STATION 1 EVENT");
                        System.out.println("ENTERING: "+s.getDate());
                    } else {
                        System.out.println("STATION 1 EVENT");
                        System.out.println("LEAVING: "+s.getDate());
                    }
                    return increasing ? Action.CONTINUE : Action.STOP;
                });
        Prop.addEventDetector(stationVisibility_1);

        // create detector station 2
        final EventDetector stationVisibility_2 = BooleanDetector.andCombine(ed_2, BooleanDetector.notCombine(fd_2)).
                withMaxCheck(maxcheck).withThreshold(threshold).
                withHandler((s, detector, increasing) -> {
                    if (increasing) {
                        System.out.println("STATION 2 EVENT");
                        System.out.println("ENTERING: "+s.getDate());
                    } else {
                        System.out.println("STATION 2 EVENT");
                        System.out.println("LEAVING: "+s.getDate());
                    }
                    return increasing ? Action.CONTINUE : Action.STOP;
                });
        Prop.addEventDetector(stationVisibility_2);

        // create detector area 1
        final EventDetector geodetect_1 = new FootprintOverlapDetector(fov, new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, Constants.WGS84_EARTH_FLATTENING, earthFrame),
                target_area_1, 10000).
                withMaxCheck(maxcheck).withThreshold(threshold).
                withHandler((s, detector, increasing) -> {
                    if (increasing) {
                        System.out.println("AREA 1 EVENT");
                        System.out.println("ENTERING: "+s.getDate());
                    } else {
                        System.out.println("AREA 1 EVENT");
                        System.out.println("LEAVING: "+s.getDate());
                    }
                    return increasing ? Action.CONTINUE : Action.STOP;
                });
        Prop.addEventDetector(geodetect_1);

        // propagate satellite
        for (AbsoluteDate extrapDate = start_date;
             extrapDate.compareTo(final_date) <= 0;
             extrapDate = extrapDate.shiftedBy(stepT)) {
            TimeStampedPVCoordinates pv = Prop.getPVCoordinates(extrapDate, inertialFrame);
        }
    }

TLE:

LEMUR-2-SPIRE-MINIONS
1 41992U 17008AW  22318.16144769  .00036410  00000+0  81443-3 0  9991
2 41992  97.2491  22.3236 0005163  55.5360 304.6376 15.43385114319430

STATIONS:

“2”
29.0
-81.0
0.0
20.0
“Florida Ground Station”

“101”
35.32
-91.14
0.0
35.0
“User Equipment 1”

AREA:

40.7128
-74.0060
0.0
41.8781
-87.6298
0.0
29.7604
-95.3698
0.0
30.3322
-81.6557
0.0

The following are the expected windows:
Station 1:
14 Nov 2022 15:23:05.457 - 15:27:15.840
15 Nov 2022 03:18:17.593 - 03:22:14.120

Station 2:
14 Nov 2022 04:00:49.901 - 04:02:06.594
15 Nov 2022 03:21:25.978 - 03:23:09.318

Area 1:
14 Nov 2022 03:54:55.341 - 04:08:03.045
14 Nov 2022 05:31:38.122 - 05:34:28.297
14 Nov 2022 13:45:46.692 - 13:52:15.435
14 Nov 2022 15:16:36.589 - 15:30:06.916
15 Nov 2022 01:45:14.589 - 01:55:43.149
15 Nov 2022 03:15:24.194 - 03:29:05.599

Any help would be greatly appreciated. Thank you in adance.

Hi @francoC

I will have a look at this later this week.

I have run your test case.

I think the culprit is due to the fact you did not set up a proper attitude provider. The default provider for TLE propagator is an inertial provider aligned with TEME frame. I guess your satellite should have something like a nadir pointing attitude or similar.
The field of view detector uses the attitude to compute how the field of view is oriented because the field of view is defined in spacecraft frame.

So you can change the attitude provider by calling something like Prop.setAttitudeProvider(new NadirPointing(inertialFrame, earth)) or another attitude provider depending on how your satellite behaves.

Another thing that I would suggest is to remove the custom loop that stops exactly at event detection and rather perform a full propagation in one sweep.Doing several propagations that end just at events occurrences may generate issues in some cases. In fact, even with your wrong attitude, removing the loop generates two more events because in this case there is a small (32 seconds) exit from fov near the beginning of the propagation. Removing the loop is done by:

  1. changing all event handlers to always return Action.CONTINUE instead of continuing only on increasing events and stopping on decreasing events
  2. replacing the loop by a single run: Prop.propagate(finalDate)

Luc,

Thank you very much for the help. The code works perfect. Since you touched on the subject of attitude, my next project is to develop a code that propagates satellite motion and computes Sun windows of visibility (already done) and calculates solar panel battery charging. Are there any Orekit classes that could help me with this?

Yes, there is one feature that was originally developed for battery monitoring years ago: additional states. In your case, you could set up an AdditionalStateProvider or an AdditionalDerivativesProvider. The first one works with all propagators but relies on the availability of a closed-form method to compute the battery state (I doubt it works in your case). The second one works only with integrated propagators (either DSSTPropagator or NumericalPropagator) and relies on the availability of a differential equation for the state (I guess this is what you will have naturally as the charging current depends on spacecraft state). Beware that in the second case, you should set up the initial values for the additional states before starting the propagation.

If you have any question on this, please open a different thread in the forum so other people find the discussion easily in the future.