Confusion with using dec/ra to compute sub-lunar location - pyephem

I have a need to compute the apparent azimuth and elevation angles as well as the sub-lunar lat/lon for a given date/time. The az/el angles I get generally agree with other sources (MoonCalc.org, Horizons, etc.) but there are not good comparison sources for the sub-lunar lat/lon. More importantly, I doubt the lat/lon I get using the dec/ra values because the ra barely changes over long time frames.
Here is the basic call I am making:
roc.date='2018/1/1 01:00:00'
moon=ephem.Moon(roc)
print('rocMoonTest: %s UTC-4, lat/lon = %0.4f [+N], %0.4f [+E]' %
(roc.date, math.degrees(roc.lat), math.degrees(roc.lon)))
print('Moon dec/ra = %s [+N], %s [+W]' % (moon.dec, moon.ra ))
print('Moon a_dec/a_ra = %s [+N], %s [+W]' % (moon.a_dec, moon.a_ra ))
print('Moon g_dec/g_ra = %s [+N], %s [+W]' % (moon.g_dec, moon.g_ra ))
print('Moon az/el = %0.4f, %0.4f' %
(math.degrees(moon.az), math.degrees(moon.alt)))
And then I iterate on that every 3 hours. Below is the output:
rocMoonTest: 2018/1/1 01:00:00 UTC-4, lat/lon = 43.0000 [+N], -78.0000 [+E]
Moon dec/ra = 18:53:07.1 [+N], 5:43:03.33 [+W]
Moon a_dec/a_ra = 19:22:21.3 [+N], 5:39:38.43 [+W]
Moon g_dec/g_ra = 19:22:44.7 [+N], 5:40:41.41 [+W]
Moon az/el = 105.3953, 43.0670
rocMoonTest: 2018/1/1 04:00:00 UTC-4, lat/lon = 43.0000 [+N], -78.0000 [+E]
Moon dec/ra = 19:07:55.4 [+N], 5:49:00.24 [+W]
Moon a_dec/a_ra = 19:32:24.2 [+N], 5:47:42.22 [+W]
Moon g_dec/g_ra = 19:32:35.1 [+N], 5:48:45.29 [+W]
Moon az/el = 169.5907, 65.8406
rocMoonTest: 2018/1/1 07:00:00 UTC-4, lat/lon = 43.0000 [+N], -78.0000 [+E]
Moon dec/ra = 19:13:15.7 [+N], 5:54:49.89 [+W]
Moon a_dec/a_ra = 19:41:07.2 [+N], 5:55:47.50 [+W]
Moon g_dec/g_ra = 19:41:05.5 [+N], 5:56:50.65 [+W]
Moon az/el = 246.5737, 49.4664
As expected and as verified by the az/el angles, the moon swings from East to West as the earth rotates and reaches a peak altitude somewhere during the period. However, none of the various dec/ra values change significantly. Over this 6 hour span, I would expect to see approximately a 6 hour change in the ra. Obviously, when I use any of these ra values to compute the longitude, I get the wrong answer. It appears the reference frame for dev/ra is not rotating with the earth. However, the docs indicate that I should expect it to.
Anyone care to explain where I went wrong in my understanding of the various right ascension variables and what the most direct way is to compute the sub-lunar lat/lon? Note, I would rather avoid using an approach that rotates the apparent az/el position into geodetic lat/lon.

The measurement “Right Ascension" is made not against the rotating surface of the Earth, but against the fixed stars of the sky — it is a kind of longitude but whose origin is the point on a star chart where the two great “equators of the sky,” the Earth’s equator and the “Solar System's equator”, the Ecliptic (which is not truly the Solar System equator because it's the plane of the Earth's orbit, rather than the weighted average of all planetary orbits), cross.
Since the point of their crossing itself moves as the ages pass, the system of Right Ascension is very slightly different every year, and very different across centuries and millenia. So Right Ascension and declination (celestial latitude) always have to be specified relative to some date, some exact moment like B1950 or J2000.
There is now a fixed coordinate system for RA and dec that doesn't move, the ICRS, which is oriented like J2000 but is defined using the positions of quasars which (we assume) won't move measurably within the lifetime of our species.

regarding this part of your question
what the most direct way is to compute the sub-lunar lat/lon?
Here is my code to calculate the sublunar point.
greenwich = ephem.Observer()
greenwich.lat = "0"
greenwich.lon = "0"
greenwich.date = datetime.utcnow()
#add Moon Sub Solar Point
moon = ephem.Moon(greenwich)
moon.compute(greenwich.date)
moon_lon = math.degrees(moon.ra - greenwich.sidereal_time() )
# map longitude value from -180 to +180
if moon_lon < -180.0 :
moon_lon = 360.0 + moon_lon
elif moon_lon > 180.0 :
moon_lon = moon_lon - 360.0
moon_lat = math.degrees(moon.dec)
print "moon Lon:",moon_lon, "Lat:",moon_lat
Hope that helps. I also use the same approach to calculate the sub-solar point. Works great for me.
EDIT: Yes... latitude of Greenwich is set to zero BECAUSE it doesn't matter at all for this calculation.

You might reinforce your thinking in the direction of this approach by taking a look at this other link:
Computing sub-solar point
Which gives basically the same solution, but for the sub_solar_point, also from Liam Kennedy (where he gave a non zero Lat for Greenwich), and also with an answer from Brandon Rhodes who wrapped the xephem library in python to give us pyephem, and until recently was actively maintaining it. Brandon is now focusing more on his next iteration, a pure python library called skyfield, which uses the latest available ephemeris with a more intuitive API.
https://pypi.org/project/skyfield/
https://rhodesmill.org/skyfield/installation.html
Although I can not contribute it now, might I suggest a comparison of the results of pyephem and skyfield, perhaps in a matplotlib figure or two, IOW, just how different/improved might be the results from skyfield?

Related

Lunar Librations and Subsolar Point in Matlab

So I have been trying to write a program to generate the latitude and longitude of the lunar subsolar point in Matlab.
I have the ephemeris data from aero toolbox, but I can't seem to get values that make sense.
The method I'm currently trying (without success) is...
Generate a juliandate for a specific time
Find the position of the sun relative to the moon from the ephemeris data for the specified date
Find the (φ, θ, ψ) lunar attitude from the ephemeris data
Create a rotational matrix from the lunar attitude values
Transpose the matrix to get the inverse rotation (to convert the vector from the ICRF frame to the moon's coordinate system)
Apply that rotation to the direction of the sun vector
Convert to spherical coordinates (longitude, latitude)
mission_time = juliandate(2022, 1, 1);
sun_pos = planetEphemeris(mission_time, 'Moon', 'Sun');
moon_rot = moonLibration(mission_time);
rotm = eul2rotm(moon_rot);
rotm = transpose(rotm);
sun_vec = sun_pos / norm(sun_pos);
sun_vec = sun_vec * rotm;
[ss_long, ss_lat] = cart2sph(sun_vec(1), sun_vec(2), sun_vec(3))
fprintf("Subsolar Lat: %2.2f°\tSubsolar Long: %2.2f°\n", rad2deg(ss_long), rad2deg(ss_lat))
The subsolar latitude should be like ±1.57° but this calculation goes all over the place. What am I missing?

How to find the local horizon longitude of the highest point of the ecliptic

I would like to use pyephem or skyfield to find the degree of the local horizon (starting from South = 0°) at which is located the highest point of the ecliptic. Note that I am not trying to find the culmination of a planet. It could well be that, when looking South, there is no planet on the ecliptic by that time/latitude, but there will still be a highest point of the ecliptic. Can anyone help?
While it’s likely that someone will soon jump in with a closed-form solution that uses spherical trigonometry — in which case it’s likely that Skyfield or PyEphem will only be used to determine the Earth orientation — here’s a quick way to get an answer within about a degree:
Generate the ecliptic as 360 points one degree apart across the sky.
Compute the altitude and azimuth of each one.
Choose the highest.
The result agrees closely to what I see if I open Stellarium, turn on the ecliptic, and choose a field star right near the point where the ecliptic reaches the highest point in the sky.
import numpy as np
from skyfield import api, framelib
from skyfield.positionlib import Apparent
𝜏 = api.tau
ts = api.load.timescale()
eph = api.load('de421.bsp')
bluffton = api.Topos('40.74 N', '84.11 W')
t = ts.utc(2021, 2, 16, 22, 52)
angle = np.arange(360) / 360.0 * 𝜏
zero = angle * 0.0
f = framelib.ecliptic_frame
d = api.Distance([np.sin(angle), np.cos(angle), zero])
v = api.Velocity([zero, zero, zero])
p = Apparent.from_time_and_frame_vectors(t, f, d, v)
p.center = bluffton
alt, az, distance = p.altaz()
i = np.argmax(alt.degrees) # Which of the 360 points has highest altitude?
print('Altitude of highest point on ecliptic:', alt.degrees[i])
print('Azimuth of highest point on ecliptic:', az.degrees[i])
The result:
Altitude of highest point on ecliptic: 67.5477569215633
Azimuth of highest point on ecliptic: 163.42529398930515
This is probably a computationally expensive enough approach that it won’t interest you once you or someone else does the spherical trigonometry to find an equation for the azimuth; but at the very least this might provide numbers to check possible formulae against.

Ecliptic coordinates of an Observer in PyEphem

I would like to have the ecliptic coordinates of a point on Earth. I can set up an observer using Pyephem:
station = ephem.Observer()
station.lon = '14:18:40.7'
station.lat = '48:19:14.0'
station.pressure = 0
station.epoch = ephem.J2000
station.elevation = 100.0
Then setup a date
station.date = ephem.date(datetime.utcnow())
But I don't know how to get this point coordinates in the Ecliptic system. If I try the Ecliptic function on the station Object it fails. Is there a way to do that in PyEphem?
If by “the ecliptic coordinates of a point on Earth” you mean “the ecliptic latitude and longitude which are overhead for a point on Earth,” then you might be able to generate a correct position by asking for the RA and declination of the point in the sky directly overhead for the location — where “directly overhead” is expressed astronomically as “at 90° altitude in the sky.” You would follow your code above with something like:
ra, dec = station.radec_of('0', '90')
ec = ephem.Ecliptic(ra, dec)
print 'Ecliptic latitude:', ec.lat
print 'Ecliptic longitude:', ec.lon
Try this technique out and see whether it returns a value anything like what you are expecting!

Calculate heading angle from x and y information

I have data that records the x and y positions of an animal in a 2D assay over time stored in a matlab matrix. I can plot these co-ordinates over time, and extract the velocity information and plot this using cline.
The problem I am having at the moment is calculating the heading angle. It should be a trivial trigonometry question, but I am drawing a blank on the best way to start.
The data is stored in a matrix xy representing x and y co-ordinates:
796.995391705069 151.755760368664
794.490825688073 150.036697247706
788.098591549296 145.854460093897
786.617021276596 144.327659574468
781.125000000000 140.093750000000
779.297872340426 138.072340425532
775.294642857143 133.879464285714
What I would like to be able to do is know the angle of the line drawn from (796.995, 151.755) to (794.490, 150.036), and so on. My research suggests atan2 will be the appropriate function, but I am unsure how to call it correctly to give useful information.
difx = xy(1,1) - xy(2,1);
dify = xy(1,2) - xy(2,2);
angle = atan2(dify,difx);
angle = angle*180/pi % convert to degrees
The result is 34.4646. Is this correct?
If it is correct, how do I get the value to be in the range 0-360?
You can use the diff function to get all the differences at once:
dxy = diff(xy); % will contain [xy(2,1)-xy(1,1) xy(2,2)-xy(1,2); ...
Then you compute the angle using the atan2 function:
a = atan2(dxy(:,2), dxy(:,1));
You convert to degrees with
aDeg = 180 * a / pi;
And finally take the angle modulo 360 to get it between 0 and 360:
aDeg = mod(aDeg, 360);
So - you pretty much got it right, yes. Except that you have calculated the heading from point 2 to point 1, and I suspect you want to start at 1 and move towards 2. That would give you a negative number - or modulo 360, an angle of about 325 degrees.
Also, using the diff function gets you the entire array of headings all at once which is a slight improvement over your code.
[rc mi]=
EDIT the problem of "phase wrapping" - when the heading goes from 359 to 0 - is quite a common problem. If you are interested in knowing when a large change happens, you can try the following trick (using aDeg from above - angle in degrees).
dDeg1 = diff(aDeg); % the change in angle
dDeg2 = diff(mod(aDeg + 90, 360)); % we moved the phase wrap point by 180 degrees
dDeg12 = [dDeg1(:) dDeg2(:)]';
[rc mi]= min(abs(dDeg12));
indx = sub2ind(size(dDeg12), mi, 1:size(dDeg12, 2));
result = dDeg12(ii);
What I did there: one of the variables (dDeg or dDeg2) does not see the phase wrap, and the min function finds out which one (it will have a smaller absolute difference). The sub2ind looks up that number (it is either positive or negative - but it's the smaller one of the two), and that is the value that ends up in result.
You can verify the angle by plotting a little line that starts at the first point and end in the direction of the heading. If the angle is correct, it will point in the direction of the next point in xy. Everything depends on where yo define 0 degrees at (straight up, say) from and whether positive degrees is rotation counterclockwise (I do) or clockwise. In MATLAB you can get the numbers between 0 and 360 but using modulo---or you can just add 180 to your results but this will change the definition of where the 0 degree mark is.
I made the following script that is a bit complex but shows how to calculate the heading/angle for all points in vector format and then displays them.
xy =[ 796.995391705069 151.755760368664
794.490825688073 150.036697247706
788.098591549296 145.854460093897
786.617021276596 144.327659574468
781.125000000000 140.093750000000
779.297872340426 138.072340425532
775.294642857143 133.879464285714];
% t = linspace(0,3/2*pi, 14)';
% xy = [sin(t), cos(t)];
% calculate the angle:
myDiff = diff(xy);
myAngle = mod(atan2(myDiff(:,1), myDiff(:,2))*180/pi, 360);
% Plot the original Data:
figure(1);
clf;
subplot(1,3,1);
plot(xy(:,1), xy(:,2), '-bx', 'markersize', 12);
hold all
axis equal;grid on;
title('Original Data');
% Plot the calculated angle:
subplot(1,3,2);
plot(myAngle);
axis tight; grid on;
title('Heading');
% Now plot the result with little lines pointing int he heading:
subplot(1,3,3);
plot(xy(:,1), xy(:,2), '-bx', 'markersize', 12);
hold all
% Just for visualization:
vectorLength = max(.8, norm(xy(1,:)- xy(2,:)));
for ind = 1:length(xy)-1
startPoint = xy(ind,:)';
endPoint = startPoint + vectorLength*[sind(myAngle(ind)); cosd(myAngle(ind))];
myLine = [startPoint, endPoint];
plot(myLine(1,:), myLine(2, :), ':r ', 'linewidth', 2)
end
axis equal;grid on;
title('Original Data with Heading Drawn On');
For example, if you use my test data
t = linspace(0,3/2*pi, 14)';
xy = [sin(t), cos(t)];
You get the following:
and if you do yours you get
Note how the little red line starts at the original data point and moves in the direction of the next point---just like the original blue line connecting the points.
Also note that the use of diff in the code to difference all the points properly at once. This is faster and avoids any problems with the direction--looks like in your case it's swapped.

Matlab - The second derivative

I am working on some problems surrounding velocity, acceleration, etc, involving curve fitting, interpolation etc, from given data points. I am in need of finding the second derivative, acceleration, given time and distance points. I am currently finding the velocity using the following code, after doing a cubic fit with 100 points.
time = [0:12];
altitude = [0,107.37,210.00,307.63,400.00,484.60,550.00,583.97,580.00,549.53,...
570.00,699.18,850];
newTime = linspace(0,12,100);
rcubic = polyfit(time,altitude,3);
vrcubic = polyval(rcubic,newTime);
velocity = polyval(polyder(rcubic),newTime);
However when I apply the same formula to try and get acceleration, code below, I get answers in the order of power 110
acceleration = polyval(polyder(velocity,newTime));
Am I not finding velocity correctly, or is my method for acceleration wrong, and if so how do I solve it.
I believe that your method for the calculation of acceleration may be wrong. You used polyder correctly when calculating velocity, but not the acceleration. To obtain the acceleration you would need to apply the polynomial derivative twice:
time = [0:12];
altitude = [0,107.37,210.00,307.63,400.00,484.60,550.00,583.97,580.00,549.53,...
570.00,699.18,850];
newTime = linspace(0,12,100);
rcubic = polyfit(time, altitude, 3);
vrcubic = polyval(rcubic, newTime);
figure;
hold on;
plot(time, altitude);
plot(newTime, vrcubic);
velocity = polyval(polyder(rcubic), newTime);
plot(newTime, velocity)
acceleration = polyval(polyder(polyder(rcubic)), newTime);
plot(newTime, acceleration)
legend('original', 'poly', 'velocity poly', 'acceleration poly');