Hello everyone,
I have been experimenting with an extension to satnogs-auto-scheduler that allows priority passes to be truncated around already scheduled observations, instead of dropping the entire pass when only part of it overlaps an existing observation.
The implementation is now available in my GitLab fork:
Repository:
Branch:
feature/truncate-overlaps
The branch currently contains the following commits:
00fb13c Add optional truncated overlap scheduling
6dff8b5 Apply truncation only during priority scheduling
6a48e35 Isolate truncated scheduling to priority transmitters
0157aa3 Fix overlap checks after truncated scheduling
This is currently an experimental implementation, not an upstream SatNOGS feature.
I have been running it continuously on my SatNOGS stations 4394 and 4434, 5049 for several weeks, and so far it appears to be working reliably.
The published branch also passes the GitLab CI lint, test, and build jobs.
Motivation
I use a priority list for satellites that I particularly want to receive.
However, if a priority pass partially overlaps an observation that has already been scheduled on the station, the normal scheduling behavior may result in the whole candidate pass being discarded, even when a useful portion of the pass is still available.
For example:
Existing observation
|-------------|
Priority pass
|------------------------|
Normal behavior:
[ dropped completely ]
With truncation:
|-----|
Another possible case is:
Priority pass
|-----------------------------|
Existing observation
|--------|
Remaining windows
|---------| |----------|
The purpose of this modification is therefore to preserve usable portions of explicitly prioritized passes whenever possible.
Implementation approach
satnogs-predict already provides several overlap-handling modes, including:
OverlapHandling.DROPOverlapHandling.TRUNCATEOverlapHandling.FULL
so much of the underlying functionality was already available.
I added an optional argument to satnogs-auto-scheduler:
--truncate-overlaps
An important design decision was not to apply TRUNCATE to every candidate pass.
Instead, I use two candidate pools:
Normal candidate pool
-> OverlapHandling.DROP
Explicit priority-transmitter pool
-> OverlapHandling.TRUNCATE
This is intentional.
If all passes are predicted using TRUNCATE, the candidate set available to the normal scheduler can change significantly. That can alter normal auto-scheduler behavior even for transmitters that are not explicitly included in the priority file.
Therefore, when --truncate-overlaps is enabled:
- All transmitters are predicted using the original
DROPbehavior. - Transmitters explicitly listed in the priority file are additionally predicted using
TRUNCATE. - Priority scheduling uses the truncated priority-pass pool.
- Normal gap filling continues to use the original
DROPcandidate pool.
Conceptually:
drop_passes = find_passes( ..., overlap_handling=OverlapHandling.DROP,)truncated_priority_passes = find_passes( ..., overlap_handling=OverlapHandling.TRUNCATE,)
The second prediction is performed only for transmitters explicitly listed in the priority file.
For truncated scheduling, the planning configuration is approximately:
PlanningConfig( overlap_handling=OverlapHandling.TRUNCATE, split_long_windows=False, overlap_padding=Duration.from_seconds(wait_time_seconds),)
Normal candidates continue to be handled by the existing ordered_scheduler().
An overlap-check issue found during testing
During the initial implementation, I found an issue in my overlap check.
The first version effectively checked the candidate together with the entire collection of occupied ranges. As a result, a candidate could be rejected because two unrelated occupied ranges overlapped each other, even when the candidate itself did not overlap them.
I changed the check so that the candidate is compared individually against each occupied range:
def _overlaps_existing(predicted_pass, occupied_ranges, wait_time_seconds): candidate_range = _predicted_pass_to_time_range(predicted_pass) for occupied_range in occupied_ranges: ordered_ranges = sorted( [occupied_range, candidate_range], key=lambda time_range: time_range.start, ) if has_range_list_overlap( ordered_ranges, padding=Duration.from_seconds(wait_time_seconds), ): return True return False
After this change, unrelated overlaps between existing observations no longer caused valid new candidates to be rejected.
A small positive WAIT value seems important
During actual scheduling, I also encountered cases where two observations whose boundaries exactly touched were rejected by SatNOGS Network as overlapping.
For example:
Observation A:
16:50:00 - 16:57:09
Observation B:
16:57:09 - 17:05:00
Using:
-w 1
or, in my local wrapper configuration:
WAIT=1
gives a one-second separation:
Observation A:
16:50:00 - 16:57:08
Observation B:
16:57:09 - 17:05:00
and this has worked reliably in my testing.
The same value is also used as overlap_padding during truncated planning.
For this reason, I recommend using a small positive WAIT value when experimenting with this implementation.
Example output
The scheduler now produces messages such as:
Truncated priority pass NORAD 68795:
2026-09-22 17:31:52.135628+00:00
->
2026-09-22 17:39:34.857957+00:00
In actual operation, I have observed multiple priority passes being truncated and successfully fitted between already scheduled observations during the same scheduler run.
Current usage
In my local wrapper, I enable this behavior using:
TRUNCATE_OVERLAPS=1
WAIT=1
which is translated to:
--truncate-overlaps
-w 1
Transmitters that are not explicitly listed in the priority file continue to use the original DROP behavior.
Testing and caveats
A few notes:
- This is currently experimental and is not an upstream SatNOGS feature.
- I recommend testing with dry-run mode first.
- Only explicitly configured priority transmitters are truncated.
- Normal candidates intentionally retain the original
DROPbehavior. - A small positive observation spacing such as
WAIT=1appears useful because exact boundary equality may still be rejected by SatNOGS Network. - I have operated this implementation continuously for several weeks on stations 4394 and 4434, 5049 without seeing problems related to the truncation feature.
- The published branch passes the GitLab CI
lint,test, andbuildjobs. - The branch is currently based on an earlier upstream
masterrevision. I intend to rebase it onto the latest upstream version before considering an upstream merge request.
During testing I also encountered some separate edge cases in satnogs-predict involving non-finite propagated coordinates from problematic orbital data. I am treating those separately, since they are not caused by this truncation implementation.
Acknowledgements
This implementation was developed with assistance from ChatGPT, which I used as a coding and debugging assistant while investigating the scheduler behavior, reviewing implementation approaches, and troubleshooting several edge cases.
The implementation decisions, testing, and operation on my SatNOGS stations were carried out and verified by me.
I would be very interested in feedback on this approach, especially:
- whether keeping the normal
DROPcandidate pool separate from the truncated priority pool makes sense; - whether the overlap handling could be simplified;
- and whether something similar could be useful as an optional upstream feature.
73,
Kazuki NISHIOKA, JF6FYI
