skyfielders / python-skyfield

Elegant astronomy for Python
MIT License
1.41k stars 211 forks source link

satellite.find_events calculates wrong setting time if the altitude_degrees parameter is >=45 degrees #1000

Open alshyt opened 5 days ago

alshyt commented 5 days ago

Hi! I'm doing a scientific research and writing a QGIS plugin where I need to define when certain satellites' pass over some target points. There is a limitation, that altitude ot the sattelite should be higher than 50 degrees.

When experimenting with altitude_degrees parameter in satellite.find_events everythings seems to be forking fine. But when this parameter is changed to be >=45 degrees, the setting time is the same and far from reality (rising and culminationg events are still being calculated correctly). Is there any solution or am I doing smth wrong?

from skyfield.api import EarthSatellite, load, wgs84

ts = load.timescale()
            line1 = '1 38707U 12039A   24247.82317651  .00138984  00000-0  16635-2 0  9998'
            line2 = '2 38707  97.4945 189.8924 0007050 108.0286 252.1739 15.60479551673427'
            satellite = EarthSatellite(line1, line2, 'KANOPUS', ts)
            print(satellite)
            observer = wgs84.latlon(+48.6622, +34.8862, 0)
            t0 = ts.utc(2024, 9, 5)
            t1 = ts.utc(2024, 9, 6)
            t, events = satellite.find_events(observer, t0, t1, altitude_degrees=70.0)
            event_names = 'rise above 70°', 'culminate', 'set below 70°'
            print(events)
            for ti, event in zip(t, events):
                name = event_names[event]
                print(ti.utc_strftime('%Y %b %d %H:%M:%S'), name)

Снимок экрана 2024-09-12 140218

brandon-rhodes commented 5 days ago

Well — drat! Looks like the search routine will require some rethinking for cases like yours: find_events() finds the culminations and then launches an underlying routine named _find_discrete() to go look for the moments of rising and setting (in your cases, moments when 70° altitude is reached) between those culminations. The problem is that the list of culmination times is not uniform: there might only be a few minutes between the start time and the first culmination, as in this case where there's around 50 seconds, but then a huge amount of time between that first culmination and the next time — which in this case is the end time, the end of the day, which is 23:59:10 away if I'm doing the math in my head correctly.

So what's the problem?

Here's a comment in _find_discrete():

        # Since we start with equal intervals, they all should fall
        # below epsilon at around the same time; so for efficiency we
        # only test the first pair.

It looks like _find_discrete() thinks it's done as soon as its iteration has cut down the first time interval small enough. But the number of halvings necessary to cut 50 seconds down to a half-second is quite different than the number of halvings to cut 23+ hours down to 50 seconds! So it's ending its search too early for the setting time, and always returns the same thing.

I'll think through whether _find_discrete() should keep going until all intervals are small enough; or adaptively stop some intervals earlier than others; or whether satellites shouldn't even use _find_discrete() under the hood but have their own search routine.

Thanks for the report! I hadn't run across this case.

alshyt commented 5 days ago

Thank's for you qick and kind answer!

I've got the problem. Based on your explanation and for my particular case I've just tried to make the time interval wider (by moving t0 to an earlier date) and the calculations for that concrete satellite pass were correct.