Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

next_pass throws ValueError when called during an ongoing pass (sometimes) #164

Open
kerel-fs opened this issue Dec 6, 2019 · 4 comments
Assignees

Comments

@kerel-fs
Copy link

kerel-fs commented Dec 6, 2019

🐞 Problem
The function next_pass invoked with singlepass=True (default since pyephem 3.7.7.0) throws an ValueError when there is an ongoing pass at observer.time sometimes (i.e. not for all datetimes during an onging pass, but for some, thanks @adamkalis for figuring this out!).

ValueError: this software is having trouble with those satellite parameters
Example code

from datetime import datetime, timedelta
import ephem

tle = ['SATHYABAMASAT',
       '1 41600U 16040B   19310.17193730  .00001455  00000-0  63216-4 0  9994',
       '2 41600  97.3537  10.1403 0012192 255.4467 104.5418 15.23822105187350']
station = {"altitude": 100,
           "lat": 42.0,
           "lon": 32.0}

timestamp_str = '2019-11-06T20:01:28Z'

timestamp = datetime.strptime(timestamp_str, '%Y-%m-%dT%H:%M:%SZ')

observer = ephem.Observer()
observer.lat = str(station['lat'])
observer.lon = str(station['lon'])
observer.elevation = station['altitude']
observer.date = timestamp 

satellite = ephem.readtle(*tle)

d = observer.next_pass(satellite, True) # throws ValueError

Expected output: The pass details of the following pass (i.e. the next pass after the currently ongoing one has finished).

Pass details (calculated using sattools/pass)

rise time rise az tt t az t altitude set time set az
2019-11-06T19:58:51 204 20:03:55 267 17 20:09:03 330
2019-11-07T07:25:53 20 07:31:32 98 38 07:37:07 176

References

Best regards,
kerel

@kerel-fs kerel-fs changed the title ValueError: this software is having trouble with those satellite parameters next_pass throws ValueError when called during an ongoing pass (sometimes) Dec 6, 2019
@brandon-rhodes brandon-rhodes self-assigned this Dec 7, 2019
@brandon-rhodes
Copy link
Owner

Thanks for this report, and for making the effort to include sample code! My next few weeks might be busy, but hopefully I'll at some point have a chance to do some debugging to track down why the routine is misbehaving here.

@Bernmeister
Copy link

Using 3.7.7.1 on Ubuntu 20.04 I ran the test script as is, but put in some debug statements into next_pass():

    def next_pass(self, body, singlepass=True):
        """Return the next rising, culmination, and setting of a satellite.
        
        If singlepass is True, return next consecutive set of
            (rising, culmination, setting).
        If singlepass is False, return 
            (next_rising, next_culmination, next_setting)
        """

        if not isinstance(body, EarthSatellite):
            raise TypeError(
                'the next_pass() method is only for use with'
                ' EarthSatellite objects because of their high speed'
                )

        result = _libastro._next_pass(self, body)
        # _libastro behavior is singlepass=False
        if ((not singlepass)
                or (None in result) 
                or (result[4] >= result[0])):
            return result
        # retry starting just before next_rising
        obscopy = self.copy()
        # Almost always 1 minute before next_rising except
        # in pathological case where set came immediately before rise
        print( "Old results:" )
        print( "Date", obscopy.date )
        print( "Rise", result[ 0 ] )
        print( "Set", result[ 4 ]  )
#        obscopy.date = result[ 0 ] - min( 1.0 / 1440, ( result[ 0 ] - result[ 4 ] ) / 2 ) # Original code
        obscopy.date = result[ 0 ] - ( ( result[ 0 ] - result[ 4 ] ) / 2 ) # New code which drops the one minute rollback
        result = _libastro._next_pass(obscopy, body)
        print( "New results:" )
        print( "Date", obscopy.date )
        print( "Rise", result[ 0 ] )
        print( "Transit", result[ 2 ] )
        print( "Set", result[ 4 ]  )
        if result[0] <= result[2] <= result[4]:
            return result
        raise ValueError("this software is having trouble with those satellite parameters")

Firstly, I got the same error as reported. With the added debug statements I get the following:

Old results:
Date 2019/11/6 20:01:28
Rise 2019/11/7 05:57:30
Set 2019/11/6 20:09:13
New results:
Date 2019/11/7 05:56:30
Rise 2019/11/7 07:25:43
Transit 2019/11/7 05:56:57
Set 2019/11/7 05:57:30
Traceback (most recent call last):
  File "164.py", line 23, in <module>
    d = observer.next_pass(satellite, True) # throws ValueError
  File "/usr/lib/python3/dist-packages/ephem/__init__.py", line 543, in next_pass
    raise ValueError("this software is having trouble with those satellite parameters")
ValueError: this software is having trouble with those satellite parameters

The next rise is again past the set, so I modified the rollback and got this result:

Old results:
Date 2019/11/6 20:01:28
Rise 2019/11/7 05:57:30
Set 2019/11/6 20:09:13
New results:
Date 2019/11/7 01:03:21
Rise 2019/11/7 05:57:30
Transit 2019/11/7 05:57:30
Set 2019/11/7 05:57:30

No exception, but something smells fishy here because the rise/transit/set are all the same date/time.

@dmopalmer What is the 1440 in the rollback line?

@dmopalmer
Copy link
Contributor

1440 is the number of minutes in a day(24*60), so 1.0/1440 is 1 minute in days.

So before the modification, it sees the 05:57:30 rise and redoes the calculation starting at 05:56:30. With the modification it has a different start.

My guess is that with your initial conditions, the 05:57:30 event is a very low pass just above the horizon for a second or less. Try plotting altitude vs time at 1s intervals to confirm.

@Bernmeister
Copy link

@kerel-fs I have somewhat of a workaround which I use and may be of use to you. Instead of passing in singlepass = True, I pass in False and then catch the exception. The whole code is wrapped in a loop which asks for the next pass and checks the result.

So for each pass returned by next_pass(), I ensure that rise < transit and transit < set. If so, that's a valid pass. If not, advance the search time by say an hour and loop again.

In the case of an exception being thrown, I check for the values of satellite.circumpolar and satellite.neverup just in case that is the reason for the exception. If either of those values are present, we're done. Otherwise, if those values are absent, something diabolical has happened and so either loop again or bail out. Refer to this issue for satellite.circumpolar and satellite.neverup.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants