Module histastro.coordinates

Coordinate transformations and related functions for HistAstro.

Expand source code
#  Copyright (c) 2019  Marc van der Sluys - marc.vandersluys.nl
#   
#  This file is part of the HistAstro Python package,
#  see: https://astro.ru.nl/~sluys/HistAstro/
#   
#  This is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License
#  as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
#  
#  This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
#  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
#  
#  You should have received a copy of the GNU General Public License along with this code.  If not, see 
#  <https://www.gnu.org/licenses/>.


"""Coordinate transformations and related functions for HistAstro."""


# Modules:
import math as m
import numpy.core as np
from histastro.constants import pi,pi2, r2d,as2r, earthRad,AU
import histastro.datetime as dt



def obliquity(jd):
    """
    Compute the obliquity of the ecliptic in radians from the JD(E).
    
    Arguments:
      jd (double):  Julian day (days).
    
    Returns:
      double:  eps: Obliquity of the ecliptic (rad).
    
    References:
      - Seidelman 1992, Eq. 3.222-1.
    
    """
    
    tJC = dt.jd2tjc(jd)  # Time in Julian centuries since J2000.0
    eps = 0.409092804 - 2.269655e-4*tJC - 2.86e-9*tJC**2 + 8.78967e-9*tJC**3  # Obliquity of the ecliptic (rad)
    
    return eps


def eq2ecl(ra,dec, eps):
    """
    Convert equatorial coordinates to ecliptical.

    Arguments:
      ra (double):   Right ascension (rad).
      dec (double):  Declination (rad).
      eps (double):  Obliquity of the ecliptic (rad).
    
    Returns:
      tuple (double,double): tuple containing (lon, lat):
    
        - lon (double):  Ecliptical longitude (rad).
        - lat (double):  Ecliptical latitude (rad).
    
    """
    
    lon = np.arctan2( np.sin(ra)  * m.cos(eps) + np.tan(dec) * m.sin(eps),  np.cos(ra) ) % pi2
    lat =  np.arcsin( np.sin(dec) * m.cos(eps) - np.cos(dec) * m.sin(eps) * np.sin(ra) )
    
    return lon,lat


def ecl2eq(lon,lat, eps):
    """Convert (geocentric) spherical ecliptical coordinates to spherical equatorial coordinates.
    
    Arguments:
      lon (double):  Ecliptical longitude (rad).
      lat (double):  Ecliptical latitude (rad).
      eps (double):  Obliquity of the ecliptic (rad).
    
    Returns:
      tuple (double,double): tuple containing (ra, dec):
    
        - ra (double):   Right ascension (rad).
        - dec (double):  Declination (rad).
    
    References:
      - [Explanatory Supplement to the Astronomical Almanac 3rd Ed,
        Eq.14.43](https://aa.usno.navy.mil/publications/docs/exp_supp.php)

    """
    
    ra  = np.arctan2( np.sin(lon) * np.cos(eps)  -  np.tan(lat) * np.sin(eps),  np.cos(lon) ) % pi2
    dec =  np.arcsin( np.sin(lat) * np.cos(eps)  +  np.cos(lat) * np.sin(eps) * np.sin(lon) )
    
    return ra,dec


def par2horiz(ha,dec, phi):
    """Convert parallactic coordinates to horizontal.

    Arguments:
      ha (double):   Hour angle (rad).
      dec (double):  Declination (rad).
      phi (double):  Geographical latitude (rad, N>0).
    
    Returns:
      tuple (double,double): tuple containing (az, alt):
    
        - az (double):   Azimuth (rad, S=0).
        - alt (double):  Altitude (rad).
    
    """
    
    az  = np.arctan2( np.sin(ha),   np.cos(ha) * m.sin(phi) - np.tan(dec) * m.cos(phi) ) % pi2
    alt = np.arcsin(  np.sin(dec) * m.sin(phi) + np.cos(ha) * np.cos(dec) * m.cos(phi) )
    
    return az,alt


def properMotion(startJD,targetJD, ra,dec, pma,pmd):
    """Compute the proper motion from startJD to targetJD for the given positions and proper motions.
    
    Arguments:
      startJD (double):   Julian day of the initial epoch (days).
      targetJD (double):  Julian day of the target epoch (days).
    
      ra (double):        Right ascension (numpy array, rad).
      dec (double):       Declination (numpy array, rad).
    
      pma (double):       Proper motion in right ascension (numpy array, rad/yr).
      pmd (double):       Proper motion in declination (numpy array, rad/yr).

    Returns:
      tuple (double,double): tuple containing (raTarget, decTarget):
    
        - raTarget (double):   Right ascension for the target epoch (rad).
        - decTarget (double):  Declination for the target epoch (rad).

    """
    
    dtYr   = (targetJD - startJD)/365.25
    raTarget  = ra  + pma*dtYr / np.cos(dec)
    decTarget = dec + pmd*dtYr
    
    return raTarget,decTarget


def precessHip(jd, ra,dec):
    """Compute precession in equatorial coordinates from the Hipparcos equinox (J2000) to that of the specified JD.
    
    Arguments:
      jd (double):   Julian day (days).
      ra (double):   Right ascension (rad).
      dec (double):  Declination (rad).
    
    Returns:
      tuple (double,double): tuple containing (raTarget, decTarget):
    
        - raNew (double):   Right ascension for the target equinox (rad).
        - decNew (double):  Declination for the target equinox (rad).
    
    """
    
    tJC  = dt.jd2tjc(jd)  # Time in Julian centuries since J2000.0
    tJC2 = tJC**2
    tJC3 = tJC*tJC2
    
    zeta  = (2306.2181*tJC + 0.30188*tJC2 + 0.017998*tJC3)*as2r
    z     = (2306.2181*tJC + 1.09468*tJC2 + 0.018203*tJC3)*as2r
    theta = (2004.3109*tJC - 0.42665*tJC2 - 0.041833*tJC3)*as2r
    
    raNew  = (np.arctan2( np.sin(ra + zeta) * np.cos(dec),  np.cos(ra + zeta) * m.cos(theta) * np.cos(dec) - m.sin(theta) * np.sin(dec) ) + z) % pi2
    decNew = np.arcsin( np.cos(ra + zeta) * m.sin(theta) * np.cos(dec)  +  m.cos(theta) * np.sin(dec) )
    
    return raNew,decNew


def geoc2topoc_ecl(gcLon,gcLat, gcDist,gcRad, eps,lst, obsLat,obsEle=0, debug=False):
    """Convert spherical ecliptical coordinates from the geocentric to the topocentric system.
    
    Arguments:
      gcLon (double):   Geocentric ecliptic longitude (rad).
      gcLat (double):   Geocentric ecliptic latitude (rad).
      gcDist (double):  Geocentric distance (AU).
      gcRad (double):   Geocentric semi-diameter (rad).
      
      eps (double):     Obliquity of the ecliptic (rad).
      lst (double):     Local sidereal time (rad).
      
      obsLat (double):  Geographical latitude of the observer (rad).
      obsEle (double):  Altitude/elevation of the observer above sea level (metres, optional, default value = 0).
      
      debug (double):   Print debug output (True/False, optional, default value = True).
    
    Returns:
      tuple (double,double,double): tuple containing (tcLon, tcLat, tcRad):
    
        - tcLon (double):  Topocentric ecliptic longitude (rad).
        - tcLat (double):  Topocentric ecliptic latitude (rad).
        - tcRad (double):  Topocentric semi-diameter (rad).
    
    """
    
    # Meeus, Ch.11, p.82:
    Req = earthRad*1000      # Equatorial radius of the Earth in metres (same units as the elevation)
    #                        (https://earth-info.nga.mil/GandG/publications/tr8350.2/wgs84fin.pdf)
    RpolEq = 0.996647189335  # Rpol/Req = 1-f: flattening of the Earth - WGS84 ellipsoid 
    
    u  = m.atan(RpolEq*m.tan(obsLat))
    RsinPhi = RpolEq*m.sin(u) + obsEle/Req * m.sin(obsLat)
    RcosPhi = m.cos(u)        + obsEle/Req * m.cos(obsLat)
    
    sinHp = m.sin(earthRad/AU)/(gcDist/AU)  # Sine of the horizontal parallax, Meeus, Eq. 40.1
    
    # Meeus, Ch.40, p.282:
    N  = m.cos(gcLon)*m.cos(gcLat) - RcosPhi*sinHp*m.cos(lst)
    
    tcLon = m.atan2( m.sin(gcLon)*m.cos(gcLat) - sinHp*(RsinPhi*m.sin(eps) + RcosPhi*m.cos(eps)*m.sin(lst)) , N ) % pi2  # Topocentric longitude
    tcLat = m.atan((m.cos(tcLon)*(m.sin(gcLat) - sinHp*(RsinPhi*m.cos(eps) - RcosPhi*m.sin(eps)*m.sin(lst))))/N)         # Topocentric latitude
    tcRad = m.asin(m.cos(tcLon)*m.cos(tcLat)*m.sin(gcRad)/N)                                                   # Topocentric semi-diameter
    
    # print(gcDist, gcDist*gcRad/tcRad)
    
    if debug:
        print()
        print('geoc2topoc_ecl():')
        print('%10s  %25s  %25s' % ('', 'rad/km/...','deg'))
        print()
        print('%10s  %25.15f' % ('Req: ', Req) )
        print('%10s  %25.15f' % ('RpolEq: ', RpolEq) )
        print()
        print('%10s  %25.15f  %25.15f' % ('u: ', u, u*r2d) )
        print('%10s  %25.15f' % ('RsinPhi: ', RsinPhi) )
        print('%10s  %25.15f' % ('RcosPhi: ', RcosPhi) )
        print()
        print('%10s  %25.15f' % ('sinHp: ', sinHp) )
        print('%10s  %25.15f  %25.15f' % ('N: ', N, N*r2d) )
        print()
        print('%10s  %25.15f  %25.15f' % ('tcLon: ', tcLon, tcLon*r2d) )
        print('%10s  %25.15f  %25.15f' % ('tcLat: ', tcLat, tcLat*r2d) )
        print('%10s  %25.15f  %25.15f' % ('tcRad: ', tcRad, tcRad*r2d) )
        
    return tcLon,tcLat,tcRad

Functions



def histastro.coordinates.ecl2eq (lon, lat, eps)

Convert (geocentric) spherical ecliptical coordinates to spherical equatorial coordinates.

Arguments

lon : double
Ecliptical longitude (rad).
lat : double
Ecliptical latitude (rad).
eps : double
Obliquity of the ecliptic (rad).

Returns

tuple : double,double

tuple containing (ra, dec):

  • ra (double): Right ascension (rad).
  • dec (double): Declination (rad).

References

Expand source code
def ecl2eq(lon,lat, eps):
    """Convert (geocentric) spherical ecliptical coordinates to spherical equatorial coordinates.
    
    Arguments:
      lon (double):  Ecliptical longitude (rad).
      lat (double):  Ecliptical latitude (rad).
      eps (double):  Obliquity of the ecliptic (rad).
    
    Returns:
      tuple (double,double): tuple containing (ra, dec):
    
        - ra (double):   Right ascension (rad).
        - dec (double):  Declination (rad).
    
    References:
      - [Explanatory Supplement to the Astronomical Almanac 3rd Ed,
        Eq.14.43](https://aa.usno.navy.mil/publications/docs/exp_supp.php)

    """
    
    ra  = np.arctan2( np.sin(lon) * np.cos(eps)  -  np.tan(lat) * np.sin(eps),  np.cos(lon) ) % pi2
    dec =  np.arcsin( np.sin(lat) * np.cos(eps)  +  np.cos(lat) * np.sin(eps) * np.sin(lon) )
    
    return ra,dec


def histastro.coordinates.eq2ecl (ra, dec, eps)

Convert equatorial coordinates to ecliptical.

Arguments

ra : double
Right ascension (rad).
dec : double
Declination (rad).
eps : double
Obliquity of the ecliptic (rad).

Returns

tuple : double,double

tuple containing (lon, lat):

  • lon (double): Ecliptical longitude (rad).
  • lat (double): Ecliptical latitude (rad).
Expand source code
def eq2ecl(ra,dec, eps):
    """
    Convert equatorial coordinates to ecliptical.

    Arguments:
      ra (double):   Right ascension (rad).
      dec (double):  Declination (rad).
      eps (double):  Obliquity of the ecliptic (rad).
    
    Returns:
      tuple (double,double): tuple containing (lon, lat):
    
        - lon (double):  Ecliptical longitude (rad).
        - lat (double):  Ecliptical latitude (rad).
    
    """
    
    lon = np.arctan2( np.sin(ra)  * m.cos(eps) + np.tan(dec) * m.sin(eps),  np.cos(ra) ) % pi2
    lat =  np.arcsin( np.sin(dec) * m.cos(eps) - np.cos(dec) * m.sin(eps) * np.sin(ra) )
    
    return lon,lat


def histastro.coordinates.geoc2topoc_ecl (gcLon, gcLat, gcDist, gcRad, eps, lst, obsLat, obsEle=0, debug=False)

Convert spherical ecliptical coordinates from the geocentric to the topocentric system.

Arguments

gcLon : double
Geocentric ecliptic longitude (rad).
gcLat : double
Geocentric ecliptic latitude (rad).
gcDist : double
Geocentric distance (AU).
gcRad : double
Geocentric semi-diameter (rad).
eps : double

Obliquity of the ecliptic (rad).

lst : double

Local sidereal time (rad).

obsLat : double
Geographical latitude of the observer (rad).
obsEle : double
Altitude/elevation of the observer above sea level (metres, optional, default value = 0).
debug : double
Print debug output (True/False, optional, default value = True).

Returns

tuple : double,double,double

tuple containing (tcLon, tcLat, tcRad):

  • tcLon (double): Topocentric ecliptic longitude (rad).
  • tcLat (double): Topocentric ecliptic latitude (rad).
  • tcRad (double): Topocentric semi-diameter (rad).
Expand source code
def geoc2topoc_ecl(gcLon,gcLat, gcDist,gcRad, eps,lst, obsLat,obsEle=0, debug=False):
    """Convert spherical ecliptical coordinates from the geocentric to the topocentric system.
    
    Arguments:
      gcLon (double):   Geocentric ecliptic longitude (rad).
      gcLat (double):   Geocentric ecliptic latitude (rad).
      gcDist (double):  Geocentric distance (AU).
      gcRad (double):   Geocentric semi-diameter (rad).
      
      eps (double):     Obliquity of the ecliptic (rad).
      lst (double):     Local sidereal time (rad).
      
      obsLat (double):  Geographical latitude of the observer (rad).
      obsEle (double):  Altitude/elevation of the observer above sea level (metres, optional, default value = 0).
      
      debug (double):   Print debug output (True/False, optional, default value = True).
    
    Returns:
      tuple (double,double,double): tuple containing (tcLon, tcLat, tcRad):
    
        - tcLon (double):  Topocentric ecliptic longitude (rad).
        - tcLat (double):  Topocentric ecliptic latitude (rad).
        - tcRad (double):  Topocentric semi-diameter (rad).
    
    """
    
    # Meeus, Ch.11, p.82:
    Req = earthRad*1000      # Equatorial radius of the Earth in metres (same units as the elevation)
    #                        (https://earth-info.nga.mil/GandG/publications/tr8350.2/wgs84fin.pdf)
    RpolEq = 0.996647189335  # Rpol/Req = 1-f: flattening of the Earth - WGS84 ellipsoid 
    
    u  = m.atan(RpolEq*m.tan(obsLat))
    RsinPhi = RpolEq*m.sin(u) + obsEle/Req * m.sin(obsLat)
    RcosPhi = m.cos(u)        + obsEle/Req * m.cos(obsLat)
    
    sinHp = m.sin(earthRad/AU)/(gcDist/AU)  # Sine of the horizontal parallax, Meeus, Eq. 40.1
    
    # Meeus, Ch.40, p.282:
    N  = m.cos(gcLon)*m.cos(gcLat) - RcosPhi*sinHp*m.cos(lst)
    
    tcLon = m.atan2( m.sin(gcLon)*m.cos(gcLat) - sinHp*(RsinPhi*m.sin(eps) + RcosPhi*m.cos(eps)*m.sin(lst)) , N ) % pi2  # Topocentric longitude
    tcLat = m.atan((m.cos(tcLon)*(m.sin(gcLat) - sinHp*(RsinPhi*m.cos(eps) - RcosPhi*m.sin(eps)*m.sin(lst))))/N)         # Topocentric latitude
    tcRad = m.asin(m.cos(tcLon)*m.cos(tcLat)*m.sin(gcRad)/N)                                                   # Topocentric semi-diameter
    
    # print(gcDist, gcDist*gcRad/tcRad)
    
    if debug:
        print()
        print('geoc2topoc_ecl():')
        print('%10s  %25s  %25s' % ('', 'rad/km/...','deg'))
        print()
        print('%10s  %25.15f' % ('Req: ', Req) )
        print('%10s  %25.15f' % ('RpolEq: ', RpolEq) )
        print()
        print('%10s  %25.15f  %25.15f' % ('u: ', u, u*r2d) )
        print('%10s  %25.15f' % ('RsinPhi: ', RsinPhi) )
        print('%10s  %25.15f' % ('RcosPhi: ', RcosPhi) )
        print()
        print('%10s  %25.15f' % ('sinHp: ', sinHp) )
        print('%10s  %25.15f  %25.15f' % ('N: ', N, N*r2d) )
        print()
        print('%10s  %25.15f  %25.15f' % ('tcLon: ', tcLon, tcLon*r2d) )
        print('%10s  %25.15f  %25.15f' % ('tcLat: ', tcLat, tcLat*r2d) )
        print('%10s  %25.15f  %25.15f' % ('tcRad: ', tcRad, tcRad*r2d) )
        
    return tcLon,tcLat,tcRad


def histastro.coordinates.obliquity (jd)

Compute the obliquity of the ecliptic in radians from the JD(E).

Arguments

jd : double
Julian day (days).

Returns

double
eps: Obliquity of the ecliptic (rad).

References

  • Seidelman 1992, Eq. 3.222-1.
Expand source code
def obliquity(jd):
    """
    Compute the obliquity of the ecliptic in radians from the JD(E).
    
    Arguments:
      jd (double):  Julian day (days).
    
    Returns:
      double:  eps: Obliquity of the ecliptic (rad).
    
    References:
      - Seidelman 1992, Eq. 3.222-1.
    
    """
    
    tJC = dt.jd2tjc(jd)  # Time in Julian centuries since J2000.0
    eps = 0.409092804 - 2.269655e-4*tJC - 2.86e-9*tJC**2 + 8.78967e-9*tJC**3  # Obliquity of the ecliptic (rad)
    
    return eps


def histastro.coordinates.par2horiz (ha, dec, phi)

Convert parallactic coordinates to horizontal.

Arguments

ha : double
Hour angle (rad).
dec : double
Declination (rad).
phi : double
Geographical latitude (rad, N>0).

Returns

tuple : double,double

tuple containing (az, alt):

  • az (double): Azimuth (rad, S=0).
  • alt (double): Altitude (rad).
Expand source code
def par2horiz(ha,dec, phi):
    """Convert parallactic coordinates to horizontal.

    Arguments:
      ha (double):   Hour angle (rad).
      dec (double):  Declination (rad).
      phi (double):  Geographical latitude (rad, N>0).
    
    Returns:
      tuple (double,double): tuple containing (az, alt):
    
        - az (double):   Azimuth (rad, S=0).
        - alt (double):  Altitude (rad).
    
    """
    
    az  = np.arctan2( np.sin(ha),   np.cos(ha) * m.sin(phi) - np.tan(dec) * m.cos(phi) ) % pi2
    alt = np.arcsin(  np.sin(dec) * m.sin(phi) + np.cos(ha) * np.cos(dec) * m.cos(phi) )
    
    return az,alt


def histastro.coordinates.precessHip (jd, ra, dec)

Compute precession in equatorial coordinates from the Hipparcos equinox (J2000) to that of the specified JD.

Arguments

jd : double
Julian day (days).
ra : double
Right ascension (rad).
dec : double
Declination (rad).

Returns

tuple : double,double

tuple containing (raTarget, decTarget):

  • raNew (double): Right ascension for the target equinox (rad).
  • decNew (double): Declination for the target equinox (rad).
Expand source code
def precessHip(jd, ra,dec):
    """Compute precession in equatorial coordinates from the Hipparcos equinox (J2000) to that of the specified JD.
    
    Arguments:
      jd (double):   Julian day (days).
      ra (double):   Right ascension (rad).
      dec (double):  Declination (rad).
    
    Returns:
      tuple (double,double): tuple containing (raTarget, decTarget):
    
        - raNew (double):   Right ascension for the target equinox (rad).
        - decNew (double):  Declination for the target equinox (rad).
    
    """
    
    tJC  = dt.jd2tjc(jd)  # Time in Julian centuries since J2000.0
    tJC2 = tJC**2
    tJC3 = tJC*tJC2
    
    zeta  = (2306.2181*tJC + 0.30188*tJC2 + 0.017998*tJC3)*as2r
    z     = (2306.2181*tJC + 1.09468*tJC2 + 0.018203*tJC3)*as2r
    theta = (2004.3109*tJC - 0.42665*tJC2 - 0.041833*tJC3)*as2r
    
    raNew  = (np.arctan2( np.sin(ra + zeta) * np.cos(dec),  np.cos(ra + zeta) * m.cos(theta) * np.cos(dec) - m.sin(theta) * np.sin(dec) ) + z) % pi2
    decNew = np.arcsin( np.cos(ra + zeta) * m.sin(theta) * np.cos(dec)  +  m.cos(theta) * np.sin(dec) )
    
    return raNew,decNew


def histastro.coordinates.properMotion (startJD, targetJD, ra, dec, pma, pmd)

Compute the proper motion from startJD to targetJD for the given positions and proper motions.

Arguments

startJD : double
Julian day of the initial epoch (days).
targetJD : double
Julian day of the target epoch (days).
ra : double

Right ascension (numpy array, rad).

dec : double

Declination (numpy array, rad).

pma : double

Proper motion in right ascension (numpy array, rad/yr).

pmd : double

Proper motion in declination (numpy array, rad/yr).

Returns

tuple : double,double

tuple containing (raTarget, decTarget):

  • raTarget (double): Right ascension for the target epoch (rad).
  • decTarget (double): Declination for the target epoch (rad).
Expand source code
def properMotion(startJD,targetJD, ra,dec, pma,pmd):
    """Compute the proper motion from startJD to targetJD for the given positions and proper motions.
    
    Arguments:
      startJD (double):   Julian day of the initial epoch (days).
      targetJD (double):  Julian day of the target epoch (days).
    
      ra (double):        Right ascension (numpy array, rad).
      dec (double):       Declination (numpy array, rad).
    
      pma (double):       Proper motion in right ascension (numpy array, rad/yr).
      pmd (double):       Proper motion in declination (numpy array, rad/yr).

    Returns:
      tuple (double,double): tuple containing (raTarget, decTarget):
    
        - raTarget (double):   Right ascension for the target epoch (rad).
        - decTarget (double):  Declination for the target epoch (rad).

    """
    
    dtYr   = (targetJD - startJD)/365.25
    raTarget  = ra  + pma*dtYr / np.cos(dec)
    decTarget = dec + pmd*dtYr
    
    return raTarget,decTarget