Pyephem 15 minutes discrepancy when manually computing equinoxes and solstices - python

the code below tries to compute manually the first equinox of 2019.
It returns
('d1=', 2019/3/20 21:43:48)
('d2=', 2019/3/20 21:43:49)
2019/3/20 21:58:31
that is, a discrepancy of 15 minutes with the real equinox. Is this normal?
Did I forget something? The problem also occurs with the solstices, and also if I used the integrated newton method. Could it have something to do with the epoch of computation?
Thanks,
Dennis
import ephem
sun = ephem.Sun()
# computing Spring equinox:
d1 = ephem.Date('2019/03/15')
d2 = ephem.Date('2019/03/25')
a=ephem.degrees('180.0')
for i in range(20):
#middle date
d3=(d1+d2)/2
sun.compute(d3)
if sun.hlon>a:
d2=d3
else:
d1=d3
print("d1=",ephem.Date(d1))
print("d2=",ephem.Date(d2))
d1 = ephem.next_equinox('2019')
print(d1)

It looks like the difference is because PyEphem's underlying astronomy library always measures heliocentric longitude relative to the coordinates of J2000, which by the date you are asking about is noticeably different from the coordinates-of-date which are used to define the equinox.
Try running this as your compute step:
sun.compute(d3, epoch=d3)
and then look for when sun.ra is zero degrees; the result should be the equinox. I'll see about getting the PyEphem Quick Reference updated to note that heliocentric coordinates don't seem to pay attention to the epoch= parameter.

Many thanks, Brandon, this is very helpful and I am finally getting the correct value! In fact, it seems that the equinoxes are defined by the right ascension being equal to 0h, 6h, 12h, 18h, and not the heliocentric longitude being 0, 90, 180, 270. There is a slight difference between ra and hlon, when you run the code below. But this leads to another question. The Wikipedia page https://en.wikipedia.org/wiki/Equinox says that the equinoxes are defined by the longitude being 0 or 180. So who is correct?
import ephem
sun = ephem.Sun()
d1 = ephem.Date('2019/03/15')
d2 = ephem.Date('2019/03/25')
a=ephem.degrees('0.0') # or 90, or 180, or 270
def spring_equinox(date):
sun.compute(date)
return ephem.degrees(sun.ra - a).znorm
d = ephem.newton(spring_equinox, d1, d2)
print(ephem.Date(d))
print sun.ra
print sun.hlon

Related

Whats wrong with my RK4 Orbital Propagator?

I am working in a RK4 orbital propagator, and I have followed the basic structure of:
k1​=dt×f(tn​,yn​)
k2​=dt×f(tn​+dt/2​,yn​+k1/2​​)
k3​=dt×f(tn​+dt/2​,yn​+k2/2​​)
k4​=dt×f(tn​+dt,yn​+k3​)​
yn+1=yn+1/6(k1+2k2+2k3+k4)
tn+1=tn+dt
I have bassed this procces in a simpler one, created by me for RK1 implementation:
#NOTE: sat is an array with the structure [x,y,z,vx,vy,vz,ax,ay,az]
def pasosimple(sat,dt):
#physics constants
G = 6.6742e-20
m_s = 1.989e30
mu=G*m_s
#Position Change from previous values of velocity and acceleration
dx=sat[3]*dt+sat[6]*(dt**2)/2
dy=sat[4]*dt+sat[7]*(dt**2)/2
dz=sat[5]*dt+sat[8]*(dt**2)/2
#Velocity change due to previous acceleration
dvx=sat[6]*dt
dvy=sat[7]*dt
dvz=sat[8]*dt
#xyz update
x=dx+sat[0]
y=dy+sat[1]
z=dz+sat[2]
#With the xyz update, we calculate new accelerations
ax=(-mu*(x)/(np.sqrt((x)**2+(y)**2+(z)**2)**3))
ay=(-mu*(y)/(np.sqrt((x)**2+(y)**2+(z)**2)**3))
az=(-mu*(z)/(np.sqrt((x)**2+(y)**2+(z)**2)**3))
#Substraction to obtain the difference acceleration
dax=ax-sat[6]
day=ay-sat[7]
daz=az-sat[8]
dsat=np.array([dx,dy,dz,dvx,dvy,dvz,dax,day,daz])
sat=np.array([x,y,z,dvx+sat[3],dvy+sat[4],dvz+sat[5],ax,ay,az])
return dsat,sat
This code works, as far as I know, and I have already tested it.
Now, for imlementing the RK4, I'm doing this:
def rk4(sat,dt):
d1,s1=pasosimple(sat,dt)
d2,s2=pasosimple(sat+d1/2,dt+0.5*dt)
d3,s3=pasosimple(sat+d2/2,dt+0.5*dt)
d4,s4=pasosimple(sat+d3,dt+dt)
sat=sat+(1/6)*(d1+d2*2+d3*2+d4)
return sat
Which does not work.
I was hoping that anyone could give some insight about what i am doing wrong.
Thank you all.
EDIT:
I have tried this alternate configuration for pasosimple, following (or trying to follow) the comments below. The result did not work either. However, using only the new pasosimple code and not nesting it into rk4works nicely, which makes me think the problem is now in rk4.
def pasosimple(sat,dt):
G = 6.6742e-20
m_t=5.972e24
m_s = 1.989e30
m_m=6.39e23
mu=G*m_s
mu_t=G*m_t
mu_m=G*m_m
ax=(-mu*(sat[0])/(np.sqrt((sat[0])**2+(sat[1])**2+(sat[2])**2)**3))
ay=(-mu*(sat[1])/(np.sqrt((sat[0])**2+(sat[1])**2+(sat[2])**2)**3))
az=(-mu*(sat[2])/(np.sqrt((sat[0])**2+(sat[1])**2+(sat[2])**2)**3))
x_punto=np.array([sat[3],sat[4],sat[5],ax,ay,az])
x=np.array([sat[0]+x_punto[0]*dt,sat[1]+x_punto[1]*dt,sat[2]+x_punto[2]*dt,sat[3]+x_punto[3]*dt,sat[4]+x_punto[4]*dt,sat[5]+x_punto[5]*dt])
dsat=np.array([x_punto[0]*dt,x_punto[1]*dt,x_punto[2]*dt,x_punto[3]*dt,x_punto[4]*dt,x_punto[5]*dt,ax-sat[6],ay-sat[7],ay-sat[8]])
sat=np.array([x[0],x[1],x[2],x[3],x[4],x[5],ax,ay,az])
return dsat,sat

How to take a datetime.time object in UT and convert to local time given a longitude

I need to convert a time in UT combined with a longitude into local time. I am working on analyzing a future Earth observing satellite mission and need to do this conversion to continue.
I found a general solution to this problem here: T(lt) = T(ut) + lon/(360/24), however implementing it is driving me absolutely bonkers.
My data are datetime time objects:
In[9]: sattime[0]
Out[9]: datetime.time(18, 0)
and longitude coordinates from 0 to 360 degrees.
I need to take this object and use the above equation to convert to a local time. I ONLY care about the time relative to midnight and NOT about the date (in fact the rest of my code is currently using the datetime.time object only and preferably the local time output would be the same type of object).
I have tried the following from here but am stuck getting the local variable back into a time object.
test = 4
for (i,j) in zip(sattime, satloncor):
td = datetime.datetime.combine(datetime.datetime.min, i) - datetime.datetime.min
seconds = td // datetime.timedelta(milliseconds=1)
local = (seconds + (j/(360/86400)))/1000
print (local)
if test<0:
break
test-=1
The test part of the code just makes sure I am not wasting time doing the conversion for all ~400,000 data points.
So to summarize, I want to take a datetime.time object in UT coupled with a corresponding longitude, and convert it to local solar time as a datetime.time object.
This all seems super convoluted and seems like there should be an easier way. Any help is greatly appreciated! Thanks!
I figured it out but it is not pretty.
localtimes = []
for (i,j) in zip(sattime, satloncor):
td = dt.datetime.combine(dt.datetime.min, i) - dt.datetime.min
seconds = td // dt.timedelta(seconds=1)
local = (seconds + (j/(360/86400)))/3600
if local>24:
local-=24
strip = [math.modf(local)[1],math.modf(local)[0]*60 ]
if strip[0]==24:
localtimes.append(dt.time(0, int(strip[1]),0))
else:
localtimes.append(dt.time(int(strip[0]), int(strip[1]),0))
Sorry, but it is super convoluted because it's a complicated subject. For just one example, did you know that every few years there is a day with 86401 seconds in it?
A couple of good web sites to start with:
http://www.ephemeris.com/books.html
I can recommend the "Practical Astronomy with your Calculator" book.
https://www.timeanddate.com/
Hope this helps.

How do I get a PyEphem observer's meridian in "epoch of date" coordinates?

I'm trying to determine the apparent right ascension of the an observer's meridian in the context of right ascension ephemerides that I'm getting for the observer using PyEpehm's Observer class. I understand that the latter will be provided in "epoch of date" coordinates, but don't know how to get the meridian's right ascension for the observer in those coordinates.
I assume that sidereal_time will give me what I'm looking for
import ephem
import numpy
zone1 = ephem.city('London')
zone1.date = '2014/08/04 11:00:00'
meridian = zone1.sidereal_time()
but am not sure what epoch the resulting coordinates are in.
How do I get a PyEphem observer's meridian in "epoch of date" coordinates?
Per its definition (at least as I understand it!), the sidereal time is necessarily a relationship between two epoch-of-date coordinates: the equinox point of that date, and the point where the observe is located on the Earth’s surface.
We can check this by choosing a point on the meridian — say, the point at 0° latitude where it crosses the celestial equator — and ask PyEphem, using its slightly awkward interface, what the RA of that point is for the moment that you are asking about:
import ephem
zone1 = ephem.city('London')
zone1.date = '2014/08/04 11:00:00'
zone1.pressure = 0.0
meridian = zone1.sidereal_time()
print 'Hour angle:', meridian
print
overhead = ephem.degrees('90:00')
south = ephem.degrees('180:00')
zone1.epoch = zone1.date
ra, dec = zone1.radec_of(az=south, alt=overhead - zone1.lat)
print 'Epoch-of-date:'
print 'RA:', ra
print 'dec:', dec
print
zone1.epoch = '2000/1/1'
ra, dec = zone1.radec_of(az=south, alt=overhead - zone1.lat)
print 'Epoch J2000:'
print 'RA:', ra
print 'dec:', dec
print
The output, I think, confirms that the sidereal time is the same as the epoch-of-date right ascension of the point we asked about — at least I think that the tiny, less-than-an-arcsecond discrepancy is due to rounding errors inside of the libastro that PyEphem relies upon, and not to some additional theoretical complication that is escaping us:
Sidereal time: 7:51:14.43
Epoch-of-date:
RA: 7:51:15.24
dec: 0:00:03.5
Epoch J2000:
RA: 7:50:30.36
dec: 0:02:19.5
Because I find PyEphem a bit awkward for these kinds of questions, note that I have started a new Skyfield library that tries to make computation a bit simpler, if you ever feel like trying out an alternative. Either way: enjoy!
Local Sidereal Time is by definition the RA of the local meridian. To be more precise, Local Apparent Sidereal Time is defined as the hour angle of the vernal equinox at that locality: it has the same value as the right ascension of any celestial body that is crossing the local meridian at that same moment, which is exactly what you want.
Your use of pyephem's sideral_time() function would produce the apparent RA ("epoch-of-date" RA is also probably the same thing under this definition but doesn't mean the same thing).
>>> GST = ephem.Observer()
>>> GST.lat = '0'
>>> GST.lon = '0'
>>> GST.elevation = '0'
>>> GST.elevation = 0
>>> GST.date = '2014/08/04 11:00:00'
>>> GST.sidereal_time()
7:51:44.73
Notice that London and Greenwich are not the same thing either.
Now let's place a star at this RA and see what we get--this won't work out exact, but it will be close enough to show the relationship.
>>> star._ra = '7:51:44.73'
>>> star._dec = '45'
>>> star._epoch = '2014/08/04 11:00:00'
>>> GST.pressure = 0
>>> star.compute(GST)
>>> star.az, star.alt
(359:59:38.7, 45:00:08.2)
Here we can see that our fictitious star is on our meridian (well as close and pyephem is going to allow us to calculated it), where the meridian is defined as azimuth angles of 0 or 180 degrees. In pyephem, it has always behaved a bit oddly when you try to use it basically backwards as in this demonstration. Let's try another in the forward direction.
This time we'll choose a known star and adjust the time at the site until it the LST matches the apparent RA of the star.
>>> vega = ephem.star('Vega') #retrieve vega from star catalog
>>> ephem.now() #get the current time
2015/7/9 01:42:49
>>> GST.date = '2015/7/9 01:42:49' #set time at site
>>> vega.compute(GST) #compute vega over the site for this time
>>> vega.a_ra #retrieve the astronomical RA for vega epoch J2000
18:36:56.47
>>> vega.ra #retrieve apparent RA for vega epoch now
18:37:29.64
Notice that they are a bit different owing to precession of the earth about its axis and nutation of the axis owing to earth-moon interactions
>>> GST.sidereal_time() #get the sidereal time for now
20:49:34.12
Note that the sidereal time is a bit off from the apparent RA of vega, so let's adjust the time until they are exactly the same
>>> GST.date = '2015/7/8 23:31:06.16'
>>> GST.sidereal_time()
18:37:29.64
Now they are identical. Now compute vega for the site again.
>>> vega.compute(GST)
>>> vega.ra, vega.dec
(18:37:29.64, 38:48:06.5)
>>> vega.az, vega.alt
(0:00:00.0, 51:11:53.5)
As you can see here that the azimuth angle is precisely on the meridian as expected. This forward demonstration shows that LST is equivalent to the apparent RA for the local meridian.

pyephem FixedObject() for given RA/Dec

I'm looking to determine the alt/az of (un-famous) stars at given RA/Dec at specific times from Mauna Kea. I'm trying to compute these parameters using pyephem, but the resulting alt/az don't agree with other sources. Here's the calculation for HAT-P-32 from Keck:
import ephem
telescope = ephem.Observer()
telescope.lat = '19.8210'
telescope.long = '-155.4683'
telescope.elevation = 4154
telescope.date = '2013/1/18 10:04:14'
star = ephem.FixedBody()
star._ra = ephem.degrees('02:04:10.278')
star._dec = ephem.degrees('+46:41:16.21')
star.compute(telescope)
print star.alt, star.az
which returns -28:43:54.0 73:22:55.3, though according to Stellarium, the proper alt/az should be: 62:26:03 349:15:13. What am I doing wrong?
EDIT: Corrected latitude and longitude, which were formerly reversed.
First, you've got long and latitude backwards; second, you need to provide the strings in hexadecimal form; and third, you need to provide the RA as hours, not degrees:
import ephem
telescope = ephem.Observer()
# Reversed longitude and latitude for Mauna Kea
telescope.lat = '19:49:28' # from Wikipedia
telescope.long = '-155:28:24'
telescope.elevation = 4154.
telescope.date = '2013/1/18 00:04:14'
star = ephem.FixedBody()
star._ra = ephem.hours('02:04:10.278') # in hours for RA
star._dec = ephem.degrees('+46:41:16.21')
star.compute(telescope)
This way, you get:
>>> print star.alt, star.az
29:11:57.2 46:43:19.6
PyEphem always uses UTC for time, so that programs operate the same and give the same output wherever they are run. You simply need to convert the date you are using to UTC, instead of using your local time zone, and the results agree fairly closely with Stellarium; use:
telescope.date = '2013/1/18 05:04:14'
The result is this alt/az:
62:27:19.0 349:26:19.4
To know where the small remaining difference comes from, I would have to look into how the two programs handle each step of their computation; but does this get you close enough?

lat/lon to utm to lat/lon is extremely flawed, how come?

I've tried the following,
input: lat/lon data
then I'll calculate a box around it by, let's say 50 m, so +/- 50 m on easting/northing value.
Now I reconvert it to lat/lon and with a script:
http://robotics.ai.uiuc.edu/~hyoon24/LatLongUTMconversion.py I get a result that just can't be, lon before is around 7, afterwards around 2.
zone, easting, northing = LLtoUTM(23, location.get_lat(), location.get_lon())
topUTM = northing + error
bottomUTM = northing - error
leftUTM = easting - error
rightUTM = easting + error
left, top = UTMtoLL(23, leftUTM, topUTM, zone)
Is the error in my code, or might be the script flawed?
So I've tried to use pyproj, just lat/lon to utm to lat/lon to see what happens
>>> p = pyproj.Proj(proj='utm', zone=32, ellps='WGS84')
>>> p
<pyproj.Proj object at 0x7ff9b8487dd0>
>>> x,y = p(47.9941214, 7.8509671)
>>> print x,y
5159550.36822 1114087.43925
>>> print p(x,y,inverse=True)
(47.971558538495991, 7.8546573140162605)
And here it's not as extremely far off as with the script from above, but it still seems strongly enough incorrect as to not be able to use it. How come? What can I do to get more exact results?
EDIT:
I ran test() and it all tests passed.
in epsg file there is no such thing. The closest I've found was this:
<32632> +proj=utm +zone=32 +ellps=WGS84 +datum=WGS84 +units=m +no_defs <>
no tmerc. Also What would I need to pass the towgs84 as parameters? The ones above?
I've created a small UTM conversion library for Python last week and uploaded it to the Python Package Index: http://pypi.python.org/pypi/utm
I have compared it to using pyproj and it is faster and more accurate. Given your sample data, this is the result:
>>> import utm
>>> u = utm.from_latlon(47.9941214, 7.8509671)
>>> print u
(414278, 5316285, 32, 'T')
>>> print utm.to_latlon(*u)
(47.994157948891505, 7.850963967574302)
UPDATE: Richards answer below describes the real solution for this issue.
The error is in your code.
First off, the PyProj issue listed in one of the other answers is real. You should check your epsg file and make sure it includes the line
<2392> +proj=tmerc +lat_0=0 +lon_0=24 +k=1.000000 +x_0=2500000 +y_0=0 +ellps=intl +towgs84=-90.7,-106.1,-119.2,4.09,0.218,-1.05,1.37 +units=m +no_defs no_defs <>
Note the towgs84 parameter.
Your problem with PyProj stems from mis-using the projection command.
If we take 47.9941214N, 7.8509671E and convert to UTM we get Zone 32, 414278 Easting, 5316286 Northing.
You perform the following PyProj operations:
p = pyproj.Proj(proj='utm', zone=32, ellps='WGS84')
>>> x,y = p(47.9941214, 7.8509671)
>>> print x,y
5159550.36822 1114087.43925
>>> print p(x,y,inverse=True)
(47.971558538495991, 7.8546573140162605)
But, if we consult the PyProj documentation, we see the following:
Calling a Proj class instance with the arguments lon, lat will convert
lon/lat (in degrees) to x/y native map projection coordinates (in
meters).
Let's try running the OP's PyProj operations again, but switch the order of the lon/lat arguments:
p = pyproj.Proj(proj='utm', zone=32, ellps='WGS84')
>>> x,y = p(7.8509671, 47.9941214)
>>> print x,y
414278.16731 5316285.59492
>>> print p(x,y,inverse=True)
(7.850967099999812, 47.994121399999784)
The operation inverts itself (pretty much) perfectly!
To answer the first part of your question, if you look in http://robotics.ai.uiuc.edu/~hyoon24/LatLongUTMconversion.py at the definition of UTMtoLL, you find the following:
UTMtoLL(ReferenceEllipsoid, northing, easting, zone)
Yet you use UTMtoLL(23, leftUTM, topUTM, zone) where leftUTM is an Easting and topUTM is a Northing.
Therefore, in the case of both your first script and PyProj, you've used the wrong order of arguments.
It's a good reminder to always double- (or triple-) check your work before suggesting that someone else's is wrong. That said, Python's documentation is not the greatest and PyProj's documentation in this instance is cryptic at best. A nice web-based explanation of this command and accompanied by examples of its usage would have probably prevented angst on your part.
I have no problem with pyproj, try the following code
from pyproj import Proj
Lat = 52.063098675
Lon = -114.132980348 #Calgary
ZoneNo = "11" #Manually input, or calcuated from Lat Lon
myProj = Proj("+proj=utm +zone="+ZoneNo+",\
+north +ellps=WGS84 +datum=WGS84 +units=m +no_defs") #north for north hemisphere
UTMx, UTMy = myProj(Lon, Lat)
########################################
#UTM ==> Lat Lon:
ZoneNo = "11" #Manually input or from other sources
myProj = Proj("+proj=utm +zone="+\
ZoneNo+", +north +ellps=WGS84 +datum=WGS84 +units=m +no_defs")
Lon2, Lat2 = myProj(UTMx, UTMy,inverse=True)
print Lat2
print Lon2
Your issue with pyProj sounds just like the one described here:
http://code.google.com/p/pyproj/issues/detail?id=3
which is resolved:
solved! in epsg file there must be
<2392> +proj=tmerc +lat_0=0 +lon_0=24 +k=1.000000 +x_0=2500000 +y_0=0 +ellps=intl
+towgs84=-90.7,-106.1,-119.2,4.09,0.218,-1.05,1.37 +units=m +no_defs no_defs <>
note the towgs84 parameter!
Check that thread out if you want to continue to use pyproj.
Also, does the test() function of the module work? Have you tried any of the scripts that come with it in the test directory?

Categories

Resources