How to combine InterSatDirectViewDetector with DateDetector

Hi all,

Sorry to bother you again. I met a problem with the event detectors, and I hope you could give me some suggestions.

I would like to form an ISL that only connects during a certain period of time.

For this purpose, I noticed that using BooleanDetector.andCombine() might be a solution. Also, according to the API documentation of BooleanDetector:

Using this detector with detectors that are not based on entry to or exit from a region, e.g. DateDetector , LongitudeCrossingDetector , will likely lead to unexpected results. To apply conditions to this latter type of event detectors a EventEnablingPredicateFilter is usually more appropriate.

So, after building an InterSatDirectViewDetector (islDetector), I also built an EventEnablingPredicateFilter, and combined them through BooleanDetector:

// Date detector
DateDetector raw = new DateDetector(startDate).withMaxCheck(step-1.0).withHandler(new ContinueOnEvent<DateDetector>());
for (AbsoluteDate extrapDate = startDate.shiftedBy(step); extrapDate.compareTo(endDate) <= 0; extrapDate = extrapDate.shiftedBy(step)) {
EventEnablingPredicateFilter<DateDetector> timeDetector = new EventEnablingPredicateFilter<DateDetector>(raw, new EnablingPredicate<DateDetector>() {

            public boolean eventIsEnabled(SpacecraftState state, DateDetector eventDetector, double g) {
                return false;

// Combine both detectors
BooleanDetector detector = BooleanDetector.andCombine(islDetector, timeDetector);

Here I used return false; just to test if this detector would work. Then I used this combined detector to build EventBasedScheduler<InterSatellitesRange> and then added this scheduler to a generator.

But when I ran the program, I found that the generated measurements are the same with the InterSatDirectViewDetector only. Namely, it seems that the date detector doesn’t work, even if I specifically disabled all events by using return false;.

I also tried to directly combine InterSatDirectViewDetector with DateDetector. And the generated measurements are indeed odd.

Is there anything I missed or did wrong? Do you have any suggestions? Thanks a lot for your help!

PS: the reason why I didn’t directly change the dates via generator.generate(startDate, endDate); is that I also need to add range measurements scheduler to the generator, which would be connected during the whole period.

Hi @xche,

I think the problem comes from the fact that you combined them through BooleanDetector.

I wrote a small test and I have the same result that you had.
Interestingly, in my test, if I use return true instead of false I only get the detection of the dates from the DateDetector, though not all of them.
These are probably the “unexpected results” the API documentation is trying to warn us from.

I managed to do it using a TimeSpanMap and an EventEnablingPredicateFilter.
Here is a small example of code. You’ll have to adapt the time span map definition to your need:

First I create my time span map object.
The idea here is that my visibility will be allowed in the interval [startDate, startDate + step].
Then not allowed in [startDate + step, startDate + 2 * step].
Then allowed in [startDate + 2 * step, startDate + 3 * step].
And so on…

        final TimeSpanMap<Boolean> visiPermissionMap = new TimeSpanMap<Boolean>(Boolean.TRUE);
        Boolean visiAllowed = Boolean.TRUE;
        for (AbsoluteDate extrapDate = startDate; extrapDate.compareTo(endDate) <= 0; extrapDate = extrapDate.shiftedBy(step)) {
            visiPermissionMap.addValidAfter(visiAllowed , extrapDate);
            visiAllowed = !visiAllowed ;

Then I create a predicate filter and adds it to the ISL detector.
Here, given the date of the spacecraft the filter just checks in the visibility permission map if the visibility is allowed or not.

        EventEnablingPredicateFilter<InterSatDirectViewDetector> islFiltered = 
                        new EventEnablingPredicateFilter<InterSatDirectViewDetector>(isl,
                                        new EnablingPredicate<EventDetector>() {

            public boolean eventIsEnabled(SpacecraftState state, EventDetector eventDetector, double g) {
                return visiPermissionMap.get(state.getDate());

Afterwards, if this new islFiltered EventDetector is used in a propagator it does successfully select the visibility given the permission map.

Hope this will be helpful to you.

Thanks a lot for your detailed explanation and help Maxime! I tried your method and that kind of works.

But I met one problem. I tested the codes the first time, it takes some time to get the results but works well. Then I tried to change the allowed visible dates and ran it again, the program finished in just a few seconds and the results are quite strange…

After several attempts, I think the results are basically like this:

Say that the whole period is one day (00:00-24:00). And I tried to activate the ISL during 00:00-03:00 at my first test. This one works fine. Then I changed the ISL activation period to 01:00-02:00. The results would become strange. It contains all unfiltered results from 00:00-01:00 and 02:00-24:00, as well as the correct results from 01:00-02:00. Here “unfiltered resultes” I mean these resultes not only are out of the specific allowed time span, but also contain the ones that should filter out by InterSatDirectViewDetector. Namely, it seems that both TimeSpanMap and InterSatDirectViewDetector are not functioning during these periods.

In another word, the program seems to “remember” the 1st predefined time span. I even tried to close the IDE and reopen it again, but there is no luck… Since I’m new to Java, I’m not sure if it casues by a Java feature or not.

Do you have any idea what causes this problem?

I hope I explained it clearly. Thank you very much for your help!

Hi again @xche,

That is indeed very weird.

Nope… Could you maybe share some sample code (preferably a JUnit test) so we could reproduce your bug and try to work on it?


Hi Maxime,

I just upload the sample codes here, you could download it from Google Drive. It’s my first time to use JUnit, I’m so sorry if it doesn’t suit your requirement.

If you need any other information, just let me know. Thanks a lot for your help!

Hi Xingchi,

It’s very good don’t worry. I managed to reproduce the buggy behaviour.

I don’t think that it comes from Java. There’s an issue in the way we’re using the detectors here, though I don’t know exactly what’s happening.
The ISL detector with the predicate filter behaves well. If you print the start/end of visibility you’ll see that it is filtered correctly.
However it’s when this detector is used inside the measurements’ generator that the problem arises.
Using your example, I’ve printed a log (log-ISLMeasurementScheduler.txt) with several values for the interval [iDate1, iDate2].
First it prints the visibility as seen by the handler of the filtered ISLDetector. It’s always detecting the same visibilities and the filter works.
Then are printed the generated measurements. This is where it does not work anymore.

I don’t know the measurements’ generator package very well so I’ll need more time to debug it. And unfortunately I won’t have time to do it today.

Hopefully, someone else on the forum will have time to look into it and give us a hand.


Hi Maxime,

Many thanks for your debugging and analysis! That helps a lot already.

Then let’s hope someone else may help us. Maybe @luc would be more familar with the generator? :smile:



I’m also not familiar with the measurement generation code, but I think the issue might be how the event detectors are used. The measurement generation code assigns a semantic meaning to the sign of the event detector’s g function (e.g. positive indicates the spacecraft is in access and measurements should be generated). This means the event detector used should also give semantic meaning to the g function.

The EventPredicateFilter is designed to suppress certain events from being detected and in order to do that assumes the sign of the event detector is arbitrary. It will flip the sign of the g function of the original event detector so some events are skipped over. As you noticed this causes problems when you need the g function to have a semantic meaning.

The BooleanDetector is probably a better choice in this case as it assumes a semantic meaning of the g function. Compared to EPF the big difference is that BD defines a multidimensional space where each axis is the g function of one of its constituent detectors. The BD will trigger an event when crossing in/out of the multidimensional space from any side, so in your case it can e.g. trigger an entry event based on the time span and an exit event based on the visibility. In contrast the EPF can only has one event detector and so can only trigger events based on that single g function (dimension), in your case visibility.

The tricky part is that DateDetector does not assign semantic meaning to the sign of its g function, but as long as you don’t use the addEventDate() method you should be able to make it work for you. So I would recommend something like BooleanDetector.and(visibilityDetector, dateDetector). You may need to negate the g function of one of the constituent detectors using BooleanDetector.not() depending on your sign convention.


Hi Evan,

Thanks a lot for your opinion and suggestion! It makes sense.

In my case, I need to activate the ISL during a certain period of time, rather than a specific epoch. Without being able to use addEventDate() method, how could I achieve this by using DateDetector with BooleanDetector? Do you have any suggestion?

Thanks again for your information!


Something like the following. Beware that I haven’t tested it.

accessDetector = BooleanDetector.and(
    new DateDetector(maxCheck, start, end)

Hi Evan,

Thanks for your example.

I didn’t find a constructor of DateDetector that could specify start date and end date. I tried to use DateDetector(double maxCheck, double threshold, TimeStamped... dates) constructor to build a new detector instance, but I got the same results as the simple constructor DateDetector(AbsoluteDate target) with addEventDate(AbsoluteDate target) method. As I tested before, the results would be odd. It’s partly correct…

For example, I would like to form the ISL around 00:00-02:00, and with a fixed step of 60s. In theory, if the ISL could be formed, the observation should be consecutive during that period. I mean, if ISL is not blocked by Earth during 00:00-00:50 for instance, then the generated measurements should be minute-wise (00:00, 00:01, 00:02, 00:03, 00:04 … 00:48, 00:49, 00:50). But if I combined DateDetector with InterSatDirectViewDetector via BooleanDetector, I would get results like (00:00, 00:02, 00:04, 00:06 … 00:48, 00:50).

// Date detector
int count = 0;
for (AbsoluteDate addDates = startDate; addDates.compareTo(endDate) <= 0; addDates = addDates.shiftedBy(step)) {
    count = count + 1;
AbsoluteDate[] dates = new AbsoluteDate[count];
count = 0;
for (AbsoluteDate addDates = startDate; addDates.compareTo(endDate) <= 0; addDates = addDates.shiftedBy(step)) {
    dates[count] = addDates;
    count = count + 1;
DateDetector timeDetector = new DateDetector(step-1, 1.0e-9, dates);

// Boolean detector
BooleanDetector detector = BooleanDetector.andCombine(timeDetector, islDetector);

Do you think what might be the problem? Is there something I missed?

Thank you very much for your help!


Try your detector with just a propagator first to see if the events are occurring where you expect them to and with the expected sign. E.g.

List events = new ArrayList();
propagator.addEventDetector(detector.withHandler(new RecordAndContinue(events));
propagator.addEventDetector(timeDetector.withHandler(new RecordAndContinue(events));
propagator.addEventDetector(islDetector.withHandler(new RecordAndContinue(events));
propagator.propagate(start, end);

Hi Evan,

Thanks for the hint. I tried this and I found out that the problem might come from DateDetector itself.

I think every time when one date was added to the detecor, it would flip the sign. Like the example before, when 00:00 was added, the sign was true. And when the next date 00:01 was added, it would flip the sign and became false. Then again, next date 00:02 it would flip again and became true. Like this, it would explain the odd results above.

And also, I found that if the sign of the last date added was true, then DateDetector seems to assume the date afterwards would always be true. The BooleanDetector would work like an InterSatDirectViewDetector alone afterwards. In another word, like example above, after 02:00, it would continue to propagate until the end of the whole period (24:00), and the results after 02:00 would only be filtered by ISL detector (date detector always assume true afterwards).

Maybe I’m not so familiar with the software and missed something, but I didn’t find a better solution to control the gIncrease sign in DateDetector. The only way I can think of is to add some “non-detectable dates” in between (eg. set the step to 30s instead of 60s, when I actually want a 60s step). And then add another “non-detectable date” in the end to close the detection. And in this way, the program seems to work fine.

If you happen to know any better or more elegant way to do this, I would love to know that.

Thanks a lot for your suggestions!


Thank you @evan.ward for explaining us what was wrong with the predicate filter trial.
@xche I think I successfully managed to implement the behaviour you’re looking for by using a custom TimeZoneDetector.
It uses the same time span map we used before and sets the g function value to +1 when the map returns true, and -1 when it returns false.
If you then “and-combine” that detector with your ISL detector it should work.
Beware of the fact that this detector performs badly since its g function is not continuous.
If you want better performance you should imagine a g function that is continuous and crosses 0 at the transition of the time span map.
It would be like what is done in the DateDetector but with a better control of the sign of the function depending on the time zone you’re in.


Hi Maxime,

Yes it works successfully. Thank you very much for your help!