Oreczml's InterSatVisu is sometimes confusing?

I tried to construct a LEO and a MEO orbit to test the operation of the intersatellite link. I don’t know if there is a problem with my program, but in some cases, the link array in the “show” list of the generated Output.czml appears “confused”.

 "show":[
        {
          "interval":"2024-03-15T00:00:00Z/2024-03-15T00:00:17.02462289450341Z",
          "boolean":true
        },
        {
          "interval":"2024-03-15T00:00:17.02462289450341Z/2024-03-15T02:35:44.507556439741165Z",
          "boolean":true
        },
        {
          "interval":"2024-03-15T02:35:44.507556439741165Z/2024-03-15T00:00:17.02462289450341Z",
          "boolean":false
        },
        {
          "interval":"2024-03-15T01:29:37.95154078813357Z/2024-03-15T05:16:13.570397322822828Z",
          "boolean":true
        },
        {
          "interval":"2024-03-15T05:16:13.570397322822828Z/2024-03-15T01:29:37.95154078813357Z",
          "boolean":false
        },
        {
          "interval":"2024-03-15T04:15:10.957183739446918Z/2024-03-15T08:01:21.472332699107938Z",
          "boolean":true
        },


public class InterVisuSatError {

    private InterVisuSatError() {
        // empty
    }

    public static void main (final String[] args) throws Exception {
//        CommInit.configureOrekit();
        // Load orekit data
        TutorialUtils.loadOrekitData();

        // Paths
        final String output = TutorialUtils.generateOutput();
        // !!! Here you need to change the path inside 'generateJsPath' to the path you are using for images or Model.
        // This folder can also be the public folder of your cesium javascript interface.
        final String pathToJSFolder = TutorialUtils.generateJSPath(
                System.getProperty("user.dir") + "/Javascript/public");

        // Creation of the clock.

        final double       durationOfSimulation = 32 * 3600; // in seconds;
        final AbsoluteDate startDate            = new AbsoluteDate(2024, 3, 15, 0, 0, 0.0, TutorialUtils.UTC);
        final AbsoluteDate finalDate            = startDate.shiftedBy(durationOfSimulation);
        final Clock clock = new Clock(startDate, finalDate, TutorialUtils.UTC,10);
               // TutorialUtils.STEP_BETWEEN_EACH_INSTANT);

        final Header header = new Header("Setup of the visualisation inter-satellites", clock, pathToJSFolder);

        //// Build of the first satellite with an orbit with 20 degree inclination
        // Build of a LEO orbit
        final KeplerianOrbit firstOrbit = new KeplerianOrbit(7838000, 0, FastMath.toRadians(45), FastMath.toRadians(20),
                FastMath.toRadians(0), FastMath.toRadians(0), PositionAngleType.MEAN, TutorialUtils.EME2000, startDate,
                Constants.WGS84_EARTH_MU);
        final SpacecraftState firstState = new SpacecraftState(firstOrbit);
        
        // Build of a MEO orbit
        final KeplerianOrbit MeoOrbit = new KeplerianOrbit(17878000, 0, FastMath.toRadians(0),
                FastMath.toRadians(120), 
               /*But if pa is "0",the "show" list will be ok.*/
               FastMath.toRadians(0), 
               FastMath.toRadians(0), PositionAngleType.MEAN,
                TutorialUtils.EME2000,
                startDate, Constants.WGS84_EARTH_MU);

        final SpacecraftState MeoState = new SpacecraftState(MeoOrbit);


        // Build of the propagator
        final double[][] tolerances1 = NumericalPropagator.tolerances(TutorialUtils.POSITION_TOLERANCE, firstOrbit,
                OrbitType.CARTESIAN);

        final double[][] tolerancesMeo = NumericalPropagator.tolerances(TutorialUtils.POSITION_TOLERANCE, MeoOrbit,
                OrbitType.CARTESIAN);


        final AdaptiveStepsizeIntegrator integrator1 = new DormandPrince853Integrator(TutorialUtils.MIN_STEP,
                TutorialUtils.MAX_STEP, tolerances1[0],
                tolerances1[1]);

        final AdaptiveStepsizeIntegrator integratorMeo = new DormandPrince853Integrator(TutorialUtils.MIN_STEP,
                TutorialUtils.MAX_STEP, tolerancesMeo[0],
                tolerancesMeo[1]);


        final NormalizedSphericalHarmonicsProvider provider = GravityFieldFactory.getNormalizedProvider(10,
                10);
        final ForceModel holmesFeatherstone = new HolmesFeatherstoneAttractionModel(TutorialUtils.EME2000,
                provider);

        final NumericalPropagator firstPropagator  = new NumericalPropagator(integrator1);

        final NumericalPropagator MeosecondPropagator = new NumericalPropagator(integratorMeo);


        firstPropagator.setOrbitType(OrbitType.CARTESIAN);
        firstPropagator.addForceModel(holmesFeatherstone);
        firstPropagator.setInitialState(firstState);
        final EphemerisGenerator firstGenerator = firstPropagator.getEphemerisGenerator();
        firstPropagator.propagate(startDate, finalDate);
        final BoundedPropagator firstBoundedPropagator = firstGenerator.getGeneratedEphemeris();


        MeosecondPropagator.setOrbitType(OrbitType.CARTESIAN);
        MeosecondPropagator.addForceModel(holmesFeatherstone);
        MeosecondPropagator.setInitialState(MeoState);
        final EphemerisGenerator MeosecondGenerator = MeosecondPropagator.getEphemerisGenerator();
        MeosecondPropagator.propagate(startDate, finalDate);
        final BoundedPropagator MeosecondBoundedPropagator = MeosecondGenerator.getGeneratedEphemeris();


        // Creation of the satellites
        final Satellite firstSatellite = Satellite.builder(firstBoundedPropagator)
                                                  .withColor(Color.MAGENTA)
                                                  .withOnlyOnePeriod()
                                                  .build();


        final Satellite MeoSatellite = Satellite.builder(MeosecondBoundedPropagator)
                .withColor(Color.YELLOW)
                .withOnlyOnePeriod()
                .build();

        // Creation of the inter-sat visualisation
        final InterSatVisu interSatVisu1 = new InterSatVisu(firstSatellite, MeoSatellite, finalDate);

        // Creation of the file
        final CzmlFile file = CzmlFile.builder(output)
                                      .withHeader(header)
                                      .withSatellite(firstSatellite)
                                      .withSatellite(MeoSatellite)
                                      .withInterSatVisu(interSatVisu1)
                                      .build();

        // Writing in the file
        file.write();
    }
}

Is there something wrong with the design of my MEO satellite orbit? Yes. When the PA value is 0, there is no problem. The orbits used for the test was indeed written casually without serious calculation, but I feel in any case that the situation shown in the picture should not occur.

FYI, i observed the same issue

1 Like

It seems that the logic of building the inter-satellite link show list is a bit problematic, and this part of the logic is indeed troublesome, and there are many situations that need to be considered, and each time you need to consider whether the start time and end time are in the middle of a “visible period”.
“buildTimeShowIntervals” function shows that the respectable author has considered most of the cases, but at the moment, it seems that there are still some cases that are not covered accurately. :smiling_face_with_tear:

1 Like

Hello @houmingyang thank you for pointing this out.

I actually discovered that this was not covered by buildTimeShowIntervals a few weeks ago, it’s just my bad that I didn’t think about it. I think that what happens is that your satellites are ‘in visu’ at the beginning of the simulation right ?

I will look into it this week end, please open an issue on the GitLab so that I will be able to fix it.

And I will use your code as a test to fix the problem.

Have a good day

Update:

  1. An issue report has been submitted on Gitlab.https://gitlab.orekit.org/orekit/oreczml/-/issues/61This is my first time trying to commit an issue using Gitlab, not knowing if the format is correct. I ask for your forgiveness if there is a mistake.
    I took a quick look at the “core” code and it felt like maybe it wasn’t all “seenAtTheBeginning” issues? I use another set of test data:
 // Build of a LEO orbit
        final KeplerianOrbit firstOrbit = new KeplerianOrbit(7878000, 0, FastMath.toRadians(45),
                FastMath.toRadians(0),
                FastMath.toRadians(0), FastMath.toRadians(0), PositionAngleType.MEAN,
                FramesFactory.getEME2000(),
                startDate,Constants.WGS84_EARTH_MU);
        final SpacecraftState firstState = new SpacecraftState(firstOrbit);
        
// Build of a MEO orbit
        final KeplerianOrbit secondOrbit = new KeplerianOrbit(17878000, 0, FastMath.toRadians(0),
                FastMath.toRadians(120), FastMath.toRadians(0), FastMath.toRadians(0), PositionAngleType.MEAN,
                FramesFactory.getEME2000(),
                startDate, Constants.WGS84_EARTH_MU);
        final SpacecraftState secondState = new SpacecraftState(secondOrbit);

A new error message has appeared:
Index 12 out of bounds for length 12
at org.orekit.czml.object.primary.InterSatVisu.buildTimeShowIntervals(InterSatVisu.java:915)

Althougn making a minimum length comparison, it also feels dangerous to use “i+1” directly for a List?
2) Why the propagationInterSat function use g() function directly?

 final InterSatDirectViewDetector detector = new InterSatDirectViewDetector(this.getBody(),
                boundedPropagatorSat2).withHandler((spacecraftState, currentDetector, increasing) -> {
                    final double detected = currentDetector.g(spacecraftState);
                    if (detected >= 0) {
                        this.datesWhenNotVisu.add(spacecraftState.getDate());
                        this.booleanList.add(true);
                    } else if (detected < 0) {
                        this.datesWhenVisu.add(spacecraftState.getDate());
                        this.booleanList.add(false);
                    }
                    return Action.CONTINUE;
                });

Are there any special considerations? I think it was enough to just use the “increasing” parameter in the past. I may not have enough understanding of InterSatDirectViewDetector. You must have thought deeply about the visible state at the beginning and end moments, and I see that the init function of your InterSatViewHandler also used a g() judgment. I’d like to use some demos to try orekit’s InterSatDirectViewDetector and try to improve my capabilities and contribute to the project one day. I just want to add a touch of strength to this excellent project hoping without adding any hassle. So you don’t have to reply all my questions. Thank you for your reply in a very busy situation, and I hope you are all successful in your studies and work.
Best regards!
houmingyang

1 Like

Thanks for your consideration ! I hope you will be able to understand deeply the code.

About the g() function used with the IntersatDirectViewDetector it actually is the value of the switching function. Because is it already implemented I used it, I could have used the “increasing” as well, it is just a choice nothing more.

About the issue, thanks, I added a assignee (me) to do it, a milestone and a labels to help me through it. The format of the issue is correct, there is no template for issues for the moment (but it might be a good idea to do one).

Althougn making a minimum length comparison, it also feels dangerous to use “i+1” directly for a List?

Where did you see in the code the ‘i+1’ , indeed it could be very dangerous if not handled properly.

Thank you again for all the feedback and the issue opened.

Best regards,
JL.

The new set of test data made a new error leads to the code:

if (!seenAtTheBeginning) {
            toReturn.add(new TimeInterval(headerInput.getAvailability().getStart(),
                    DateUtils.toJulianDate(datesWhenVisuInput.get(0), headerInput.getTimeScale())));
            this.booleanList.add(true);
            for (int i = 0; i < minimumLength; i++) {
                toReturn.add(new TimeInterval(DateUtils.toJulianDate(datesWhenVisuInput.get(i), headerInput.getTimeScale()),
                        DateUtils.toJulianDate(datesWhenNotVisuInput.get(i), headerInput.getTimeScale())));
                toReturn.add(new TimeInterval(DateUtils.toJulianDate(datesWhenNotVisuInput.get(i), headerInput.getTimeScale()),
                        DateUtils.toJulianDate(datesWhenVisuInput.get(i + 1), headerInput.getTimeScale())));
            }
        }

maybe it should change like this?

for (int i = 0; i < minimumLength - 1; i++) {
    toReturn.add(new TimeInterval(
        DateUtils.toJulianDate(datesWhenVisuInput.get(i), headerInput.getTimeScale()),
        DateUtils.toJulianDate(datesWhenNotVisuInput.get(i), headerInput.getTimeScale())
    ));
    toReturn.add(new TimeInterval(
        DateUtils.toJulianDate(datesWhenNotVisuInput.get(i), headerInput.getTimeScale()),
        DateUtils.toJulianDate(datesWhenVisuInput.get(i + 1), headerInput.getTimeScale())
    ));
}

// For the last one??
if (minimumLength > 0) {
    int lastIdx = minimumLength - 1;
    toReturn.add(new TimeInterval(
        DateUtils.toJulianDate(datesWhenVisuInput.get(lastIdx), headerInput.getTimeScale()),
        DateUtils.toJulianDate(datesWhenNotVisuInput.get(lastIdx), headerInput.getTimeScale())
    ));
}

Of course, this feels a bit like “heal your head when you have a headache, and heal your feet when your feet hurt.” I’ve always felt that there might be a better algorithm to do this without using two lists (datesWhenNotVisu, datesWhenVisu).

PS:
Why propagationInterSat function write like this:

 this.datesWhenNotVisu.add(spacecraftState.getDate());

 this.booleanList.add(true); //false??

I’m sorry,It doesn’t work at the last pair.It seems this question is still too complicated for my current level.Let me study more and think carefully about it again and then ask you for advice. Sorry to bother you.

It’s alright, the answer to this problem is not trivial, that is why.

If you find a solution let me know !

@houmingyang Can you please provide the entire code that you are using that lead to this error please ?

That’s still your code “InterVisuSatExample” , just with a few parameters change. If it’s convenient for you, I wouldn’t mind posting it again.
Here is the “i+1” cause “index out of bounds exception”

/* Copyright 2002-2024 CS GROUP
 * Licensed to CS GROUP (CS) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * CS licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.orekit.czml.intervisu;

import org.orekit.czml.TutorialUtils;
import org.hipparchus.ode.nonstiff.AdaptiveStepsizeIntegrator;
import org.hipparchus.ode.nonstiff.DormandPrince853Integrator;
import org.hipparchus.util.FastMath;
import org.orekit.czml.file.CzmlFile;
import org.orekit.czml.object.primary.Header;
import org.orekit.czml.object.primary.InterSatVisu;
import org.orekit.czml.object.primary.Satellite;
import org.orekit.czml.object.secondary.Clock;
import org.orekit.forces.ForceModel;
import org.orekit.forces.gravity.HolmesFeatherstoneAttractionModel;
import org.orekit.forces.gravity.potential.GravityFieldFactory;
import org.orekit.forces.gravity.potential.NormalizedSphericalHarmonicsProvider;
import org.orekit.frames.FramesFactory;
import org.orekit.orbits.KeplerianOrbit;
import org.orekit.orbits.OrbitType;
import org.orekit.orbits.PositionAngleType;
import org.orekit.propagation.BoundedPropagator;
import org.orekit.propagation.EphemerisGenerator;
import org.orekit.propagation.SpacecraftState;
import org.orekit.propagation.numerical.NumericalPropagator;
import org.orekit.time.AbsoluteDate;
import org.orekit.time.TimeScalesFactory;
import org.orekit.utils.Constants;

import java.awt.Color;

/**
 * The type Inter visu sat example.
 */
public class InterVisuSatExample {

    private InterVisuSatExample () {
        // empty
    }

    /**
     * Main.
     *
     * @param args the args
     * @throws Exception the exception
     */
    public static void main (final String[] args) throws Exception {
        // Load orekit data
        TutorialUtils.loadOrekitData();

        // Paths
        final String output = TutorialUtils.generateOutput();
        // !!! Here you need to change the path inside 'generateJsPath' to the path you are using for images or Model.
        // This folder can also be the public folder of your cesium javascript interface.
        final String pathToJSFolder = TutorialUtils.generateJSPath(
                System.getProperty("user.dir") + "/Javascript/public");

        // Creation of the clock.

        final double       durationOfSimulation = 32 * 3600; // in seconds;
        final AbsoluteDate startDate            = new AbsoluteDate(2024, 3, 15, 0, 0, 0.0, TimeScalesFactory.getUTC());
        final AbsoluteDate finalDate            = startDate.shiftedBy(durationOfSimulation);
        final Clock clock = new Clock(startDate, finalDate, TimeScalesFactory.getUTC(),
                TutorialUtils.STEP_BETWEEN_EACH_INSTANT);

        final Header header = new Header("Setup of the visualisation inter-satellites", clock, pathToJSFolder);

        //// Build of the first satellite with an orbit with 20 degree inclination
        // Build of a LEO orbit
        final KeplerianOrbit firstOrbit = new KeplerianOrbit(7878000, 0, FastMath.toRadians(45),
                FastMath.toRadians(0),
                FastMath.toRadians(0), FastMath.toRadians(0), PositionAngleType.MEAN,
                FramesFactory.getEME2000(),
                startDate,Constants.WGS84_EARTH_MU);
        final SpacecraftState firstState = new SpacecraftState(firstOrbit);

        // Build of a MEO orbit
        final KeplerianOrbit secondOrbit = new KeplerianOrbit(17878000, 0, FastMath.toRadians(0),
                FastMath.toRadians(120), FastMath.toRadians(0), FastMath.toRadians(0), PositionAngleType.MEAN,
                FramesFactory.getEME2000(),
                startDate, Constants.WGS84_EARTH_MU);
        final SpacecraftState secondState = new SpacecraftState(secondOrbit);

        // Build of the propagator


        final double[][] tolerances1 = NumericalPropagator.tolerances(TutorialUtils.POSITION_TOLERANCE, firstOrbit,
                OrbitType.CARTESIAN);
        final double[][] tolerances2 = NumericalPropagator.tolerances(TutorialUtils.POSITION_TOLERANCE, secondOrbit,
                OrbitType.CARTESIAN);
        final AdaptiveStepsizeIntegrator integrator1 = new DormandPrince853Integrator(TutorialUtils.MIN_STEP,
                TutorialUtils.MAX_STEP, tolerances1[0],
                tolerances1[1]);
        final AdaptiveStepsizeIntegrator integrator2 = new DormandPrince853Integrator(TutorialUtils.MIN_STEP,
                TutorialUtils.MAX_STEP, tolerances2[0],
                tolerances2[1]);

        final NormalizedSphericalHarmonicsProvider provider = GravityFieldFactory.getNormalizedProvider(10,
                10);
        final ForceModel holmesFeatherstone = new HolmesFeatherstoneAttractionModel(FramesFactory.getEME2000(),
                provider);

        final NumericalPropagator firstPropagator  = new NumericalPropagator(integrator1);
        final NumericalPropagator secondPropagator = new NumericalPropagator(integrator2);

        firstPropagator.setOrbitType(OrbitType.CARTESIAN);
        firstPropagator.addForceModel(holmesFeatherstone);
        firstPropagator.setInitialState(firstState);
        final EphemerisGenerator firstGenerator = firstPropagator.getEphemerisGenerator();
        firstPropagator.propagate(startDate, finalDate);
        final BoundedPropagator firstBoundedPropagator = firstGenerator.getGeneratedEphemeris();

        secondPropagator.setOrbitType(OrbitType.CARTESIAN);
        secondPropagator.addForceModel(holmesFeatherstone);
        secondPropagator.setInitialState(secondState);
        final EphemerisGenerator secondGenerator = secondPropagator.getEphemerisGenerator();
        secondPropagator.propagate(startDate, finalDate);
        final BoundedPropagator secondBoundedPropagator = secondGenerator.getGeneratedEphemeris();

        // Creation of the satellites
        final Satellite firstSatellite = Satellite.builder(firstBoundedPropagator, header)
                                                  .withColor(Color.MAGENTA)
                                                  .withOnlyOnePeriod()
                                                  .build();

        final Satellite secondSatellite = Satellite.builder(secondBoundedPropagator, header)
                                                   .withColor(Color.GREEN)
                                                   .withOnlyOnePeriod()
                                                   .build();

        // Creation of the inter-sat visualisation
        final InterSatVisu interSatVisu = new InterSatVisu(firstSatellite, secondSatellite, finalDate, header);

        // Creation of the file
        final CzmlFile file = CzmlFile.builder()
                                      .withHeader(header)
                                      .withSatellite(firstSatellite)
                                      .withSatellite(secondSatellite)
                                      .withInterSatVisu(interSatVisu)
                                      .build();
        // Writing in the file
        file.write(output);
    }
}

1 Like

There is another Exception :“One or more of the hour, minute, and second arguments is outside of the acceptable range.”
For convenience here is another entire code:

/* Copyright 2002-2024 CS GROUP
 * Licensed to CS GROUP (CS) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * CS licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.orekit.czml.intervisu;

import org.orekit.czml.TutorialUtils;
import org.hipparchus.ode.nonstiff.AdaptiveStepsizeIntegrator;
import org.hipparchus.ode.nonstiff.DormandPrince853Integrator;
import org.hipparchus.util.FastMath;
import org.orekit.czml.file.CzmlFile;
import org.orekit.czml.object.primary.Header;
import org.orekit.czml.object.primary.InterSatVisu;
import org.orekit.czml.object.primary.Satellite;
import org.orekit.czml.object.secondary.Clock;
import org.orekit.forces.ForceModel;
import org.orekit.forces.gravity.HolmesFeatherstoneAttractionModel;
import org.orekit.forces.gravity.potential.GravityFieldFactory;
import org.orekit.forces.gravity.potential.NormalizedSphericalHarmonicsProvider;
import org.orekit.frames.FramesFactory;
import org.orekit.orbits.KeplerianOrbit;
import org.orekit.orbits.OrbitType;
import org.orekit.orbits.PositionAngleType;
import org.orekit.propagation.BoundedPropagator;
import org.orekit.propagation.EphemerisGenerator;
import org.orekit.propagation.SpacecraftState;
import org.orekit.propagation.numerical.NumericalPropagator;
import org.orekit.time.AbsoluteDate;
import org.orekit.time.TimeScalesFactory;
import org.orekit.utils.Constants;

import java.awt.Color;

/**
 * The type Inter visu sat example.
 */
public class InterVisuSatExample {

    private InterVisuSatExample () {
        // empty
    }

    /**
     * Main.
     *
     * @param args the args
     * @throws Exception the exception
     */
    public static void main (final String[] args) throws Exception {
        // Load orekit data
        TutorialUtils.loadOrekitData();

        // Paths
        final String output = TutorialUtils.generateOutput();
        // !!! Here you need to change the path inside 'generateJsPath' to the path you are using for images or Model.
        // This folder can also be the public folder of your cesium javascript interface.
        final String pathToJSFolder = TutorialUtils.generateJSPath(
                System.getProperty("user.dir") + "/Javascript/public");

        // Creation of the clock.

        final double       durationOfSimulation = 32 * 3600; // in seconds;
        final AbsoluteDate startDate            = new AbsoluteDate(2024, 3, 15, 0, 0, 0.0, TimeScalesFactory.getUTC());
        final AbsoluteDate finalDate            = startDate.shiftedBy(durationOfSimulation);
        final Clock clock = new Clock(startDate, finalDate, TimeScalesFactory.getUTC(),
                TutorialUtils.STEP_BETWEEN_EACH_INSTANT);

        final Header header = new Header("Setup of the visualisation inter-satellites", clock, pathToJSFolder);

        //// Build of the first satellite with an orbit with 20 degree inclination
        // Build of a LEO orbit
        final KeplerianOrbit firstOrbit = new KeplerianOrbit(7838000, 0, FastMath.toRadians(45),
                FastMath.toRadians(0),
                FastMath.toRadians(0), FastMath.toRadians(0), PositionAngleType.MEAN,
                FramesFactory.getEME2000(),
                startDate,Constants.WGS84_EARTH_MU);
        final SpacecraftState firstState = new SpacecraftState(firstOrbit);

        // Build of a MEO orbit
        final KeplerianOrbit secondOrbit = new KeplerianOrbit(17878000, 0, FastMath.toRadians(0),
                FastMath.toRadians(120), FastMath.toRadians(0), FastMath.toRadians(0), PositionAngleType.MEAN,
                FramesFactory.getEME2000(),
                startDate, Constants.WGS84_EARTH_MU);
        final SpacecraftState secondState = new SpacecraftState(secondOrbit);

        // Build of the propagator


        final double[][] tolerances1 = NumericalPropagator.tolerances(TutorialUtils.POSITION_TOLERANCE, firstOrbit,
                OrbitType.CARTESIAN);
        final double[][] tolerances2 = NumericalPropagator.tolerances(TutorialUtils.POSITION_TOLERANCE, secondOrbit,
                OrbitType.CARTESIAN);
        final AdaptiveStepsizeIntegrator integrator1 = new DormandPrince853Integrator(TutorialUtils.MIN_STEP,
                TutorialUtils.MAX_STEP, tolerances1[0],
                tolerances1[1]);
        final AdaptiveStepsizeIntegrator integrator2 = new DormandPrince853Integrator(TutorialUtils.MIN_STEP,
                TutorialUtils.MAX_STEP, tolerances2[0],
                tolerances2[1]);

        final NormalizedSphericalHarmonicsProvider provider = GravityFieldFactory.getNormalizedProvider(10,
                10);
        final ForceModel holmesFeatherstone = new HolmesFeatherstoneAttractionModel(FramesFactory.getEME2000(),
                provider);

        final NumericalPropagator firstPropagator  = new NumericalPropagator(integrator1);
        final NumericalPropagator secondPropagator = new NumericalPropagator(integrator2);

        firstPropagator.setOrbitType(OrbitType.CARTESIAN);
        firstPropagator.addForceModel(holmesFeatherstone);
        firstPropagator.setInitialState(firstState);
        final EphemerisGenerator firstGenerator = firstPropagator.getEphemerisGenerator();
        firstPropagator.propagate(startDate, finalDate);
        final BoundedPropagator firstBoundedPropagator = firstGenerator.getGeneratedEphemeris();

        secondPropagator.setOrbitType(OrbitType.CARTESIAN);
        secondPropagator.addForceModel(holmesFeatherstone);
        secondPropagator.setInitialState(secondState);
        final EphemerisGenerator secondGenerator = secondPropagator.getEphemerisGenerator();
        secondPropagator.propagate(startDate, finalDate);
        final BoundedPropagator secondBoundedPropagator = secondGenerator.getGeneratedEphemeris();

        // Creation of the satellites
        final Satellite firstSatellite = Satellite.builder(firstBoundedPropagator, header)
                                                  .withColor(Color.MAGENTA)
                                                  .withOnlyOnePeriod()
                                                  .build();

        final Satellite secondSatellite = Satellite.builder(secondBoundedPropagator, header)
                                                   .withColor(Color.GREEN)
                                                   .withOnlyOnePeriod()
                                                   .build();

        // Creation of the inter-sat visualisation
        final InterSatVisu interSatVisu = new InterSatVisu(firstSatellite, secondSatellite, finalDate, header);
        //final InterSatVisu interSatVisu = new InterSatVisu( secondSatellite,firstSatellite, finalDate, header);

        // Creation of the file
        final CzmlFile file = CzmlFile.builder()
                                      .withHeader(header)
                                      .withSatellite(firstSatellite)
                                      .withSatellite(secondSatellite)
                                      .withInterSatVisu(interSatVisu)
                                      .build();
        // Writing in the file
        file.write(output);
    }
}

Thank you, I will review it anytime soon.
It is easier for me to have your entire code, It takes less time to launch and debug.
Even if it’s InterSatVisuExample modified.

Maybe I’m thinking about it too simple and naïve?

I think there are only two special cases to consider when the visibility from the propagated start time to the end time: the first is that there is no start visual time in the first lap of the propagated time period, and the first event is the end visual time; The second is that there is no end visual time on the last lap in the propagated time period, only the start visual time.

My idea is that only one event pair list (including event time and start or end) will be generated when Propagating the calculation; Check this “event pair list” before writing the file: if the first event is the beginning, then you don’t need to deal with it; If the first event is the end, add it with propagated start time; If the last event is the beginning, add it with the propagated end time; If the last event is the end, then you don’t have to deal with it. Of course, if there are no events in this “event pair list”, it means that there are no visible circles at all in the start and end time of the Propagation, and a “invisible” from the start time to the end time of the Propagation is added.

I’ve always felt that using “datesWhenVisu” and “datesWhenNotVisu” those two lists increases the logical complexity of the algorithm.So I wrote a small demo that I also try to generate the showlist, I don’t know if it is right or helpful. I’m a beginner and I’m even worried that if you read my code, you might delay something or interrupt your normal progress.
.

Cheers

I don’t know if czml has to be from the start time to the end time, or if it just needs to include the “valid” interval for the propagated time period.

import org.hipparchus.ode.events.Action;
import org.hipparchus.ode.nonstiff.AdaptiveStepsizeIntegrator;
import org.hipparchus.ode.nonstiff.DormandPrince853Integrator;
import org.hipparchus.util.FastMath;
import org.orekit.bodies.OneAxisEllipsoid;
import org.orekit.forces.ForceModel;
import org.orekit.forces.gravity.HolmesFeatherstoneAttractionModel;
import org.orekit.forces.gravity.potential.GravityFieldFactory;
import org.orekit.forces.gravity.potential.NormalizedSphericalHarmonicsProvider;
import org.orekit.frames.Frame;
import org.orekit.frames.FramesFactory;
import org.orekit.orbits.KeplerianOrbit;
import org.orekit.orbits.Orbit;
import org.orekit.orbits.OrbitType;
import org.orekit.orbits.PositionAngleType;
import org.orekit.propagation.BoundedPropagator;
import org.orekit.propagation.EphemerisGenerator;
import org.orekit.propagation.SpacecraftState;
import org.orekit.propagation.events.InterSatDirectViewDetector;
import org.orekit.propagation.numerical.NumericalPropagator;
import org.orekit.time.AbsoluteDate;
import org.orekit.time.TimeScalesFactory;
import org.orekit.utils.Constants;
import org.orekit.utils.IERSConventions;

import java.util.ArrayList;
import java.util.List;
import java.util.TimeZone;

public class SatelliteVisibilityExample {
   //TimeData is a "event pair" class
    static List<TimeData> timeDataList = new ArrayList<>();
    public static void main(String[] args) {

        ExamUtil.configureOrekit();

        final double       durationOfSimulation = 48 * 3600; // in seconds;
        AbsoluteDate startDate = new AbsoluteDate(2024, 3, 15, 0, 0, 0, TimeScalesFactory.getUTC());
        AbsoluteDate finalDate = startDate.shiftedBy(durationOfSimulation);
        // LEO
        Orbit orbit1 = new KeplerianOrbit(7838000, 0, FastMath.toRadians(45),
                FastMath.toRadians(20), FastMath.toRadians(0), FastMath.toRadians(0), PositionAngleType.MEAN,
                FramesFactory.getEME2000(),
                startDate,Constants.WGS84_EARTH_MU);
        // MEO
        Orbit orbit2 = new KeplerianOrbit(17878000, 0, FastMath.toRadians(0),
                FastMath.toRadians(120), FastMath.toRadians(0), FastMath.toRadians(0), PositionAngleType.MEAN,
                FramesFactory.getEME2000(),
                startDate, Constants.WGS84_EARTH_MU);

        SpacecraftState initialState1 = new SpacecraftState(orbit1);
        SpacecraftState initialState2 = new SpacecraftState(orbit2);


        final double[][] tolerances1 = NumericalPropagator.tolerances(10.0, orbit1,OrbitType.CARTESIAN);
        final double[][] tolerances2 = NumericalPropagator.tolerances(10.0, orbit2,OrbitType.CARTESIAN);
        final AdaptiveStepsizeIntegrator integrator1 = new DormandPrince853Integrator(0.001,
                1000, tolerances1[0], tolerances1[1]);
        final AdaptiveStepsizeIntegrator integrator2 = new DormandPrince853Integrator(0.001,
                1000, tolerances2[0], tolerances2[1]);

        final NormalizedSphericalHarmonicsProvider provider = GravityFieldFactory.getNormalizedProvider(10,
                10);
        final ForceModel holmesFeatherstone = new HolmesFeatherstoneAttractionModel(FramesFactory.getEME2000(),
                provider);

        NumericalPropagator propagator1 = new NumericalPropagator(integrator1);
        propagator1.setOrbitType(OrbitType.CARTESIAN);
        propagator1.addForceModel(holmesFeatherstone);
        propagator1.setInitialState(initialState1);
        final EphemerisGenerator firstGenerator = propagator1.getEphemerisGenerator();
        propagator1.propagate(startDate, finalDate);
        final BoundedPropagator firstBoundedPropagator = firstGenerator.getGeneratedEphemeris();

        NumericalPropagator propagator2 = new NumericalPropagator(integrator2);
        propagator2.setOrbitType(OrbitType.CARTESIAN);
        propagator1.addForceModel(holmesFeatherstone);
        propagator2.setInitialState(initialState2);
        final EphemerisGenerator secondGenerator = propagator2.getEphemerisGenerator();
        propagator2.propagate(startDate, finalDate);
        final BoundedPropagator secondBoundedPropagator = secondGenerator.getGeneratedEphemeris();

        Frame ITRF = FramesFactory.getITRF(IERSConventions.IERS_2010, true);
        OneAxisEllipsoid body         = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
                Constants.WGS84_EARTH_FLATTENING, ITRF);

        InterSatDirectViewDetector detector =
                new InterSatDirectViewDetector(body,secondBoundedPropagator::getPVCoordinates)
                        .withMaxCheck(60)
                        .withThreshold(0.001)
                        .withHandler((spacecraftState, currentDetector, increasing) -> {
                if (increasing) {
                    timeDataList.add(new TimeData(spacecraftState.getDate(),"s"));
                } else {
                    timeDataList.add(new TimeData(spacecraftState.getDate(),"e"));
                }
                return Action.CONTINUE;
        });

        firstBoundedPropagator.addEventDetector(detector);
        SpacecraftState finalState = firstBoundedPropagator.propagate(startDate,finalDate);

        System.out.println(" Start date (s) : "
                +"as UTC"+ startDate.getDate().toString(TimeZone.getTimeZone("UTC")));
        System.out.println(" Final date (s) : "
                +"as UTC"+ finalState.getDate().toString(TimeZone.getTimeZone("UTC"))
                +"\n State duration from initial date (s) : " + finalState.getDate().durationFrom(startDate));

        // generateEventPairs
        List<String> eventPairs = generateEventPairs(startDate, finalDate, timeDataList);

        // sout
        for (String event : eventPairs) {
            System.out.println(event);
        }

    }
    public static List<String> generateEventPairs(AbsoluteDate startDate, AbsoluteDate finalDate, List<TimeData> timeDataList) {
        List<String> result = new ArrayList<>();

        // 1.isEmpty
        if (timeDataList.isEmpty()) {
            String interval = startDate.toString() + "/" + finalDate.toString();
            result.add(formatEvent(interval, false));
            return result;
        }

        // 2.1 Begin thing
        if (!timeDataList.isEmpty() && timeDataList.get(0).getData().contains("e")) {
            timeDataList.add(0, new TimeData(startDate, "s"));
        }

        // 2.2 End thing
        if (!timeDataList.isEmpty() && timeDataList.get(timeDataList.size() - 1).getData().contains("s")) {
            timeDataList.add(new TimeData(finalDate, "e"));
        }

        // 3.Get interval 
        for (int i = 0; i < timeDataList.size() - 1; i++) {
            TimeData current = timeDataList.get(i);
            TimeData next = timeDataList.get(i + 1);

            // Interval String 
            String interval = current.getDate().toString() + "/" + next.getDate().toString();

            // Boolean String
            boolean isVisible = current.getData().contains("s") && next.getData().contains("e");

            // Json Like String
            result.add(formatEvent(interval, isVisible));
        }

        return result;
    }

    // Json Like String
    private static String formatEvent(String interval, boolean isVisible) {
        return String.format(
                "\"show\":{\n" +
                        "  \"interval\":\"%s\",\n" +
                        "  \"boolean\":%s\n" +
                        "}",
                interval, isVisible ? "true" : "false"
        );
    }
}

import org.orekit.time.AbsoluteDate;

public class TimeData {
    private AbsoluteDate date;
    private String data;

    public TimeData(AbsoluteDate date, String data) {
        this.date = date;
        this.data = data;
    }

    // Getter 和 Setter 
    public AbsoluteDate getDate() {
        return date;
    }

    public void setDate(AbsoluteDate date) {
        this.date = date;
    }

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }

    // 重写 toString 
    @Override
    public String toString() {
        return "TimeData{" +
                "date=" + date +
                ", data='" + data + '\'' +
                '}';
    }
}

In my opinion,the ‘incomplete exception’ at the begin time or the end time has been solved,and it is simple.

“show”:{

  • “interval”:“2024-03-15T00:00:00.000Z/2024-03-15T01:20:48.59984566372284Z”,*
  • “boolean”:true*
    }
    “show”:{
  • “interval”:“2024-03-15T01:20:48.59984566372284Z/2024-03-15T02:21:27.71247500738056Z”,*
  • “boolean”:false*
    } …

Hi,Zudo
I just tried to write my own “InterSatVisu” and tested it with a few sets of orbits, and there is still a bit of a flaw, but it is very close to the correct answer.

Now I know that in the czml I have to “fill it all up” from the start time of the propagate to the end time of the propagate, otherwise the start or end of the showlist will not be in the right state.

By reading your code, I understand more and more the comprehensiveness of your thinking. Now I’m carefully adding some my own functions to your existing code to try to solve this problem. I’m on a lower level of coding skill, and I also use my spare time outside of work to do this. I don’t know if I can help.
Regards

1 Like