Position of the Moon (topocentric) using Astrometry.jl (SOFA/ERFA) routines

I added the alt/az calculation and compared to AstroPy by using AstroPy to generate a dataset of 24 hours.

What you can see is the moon function (geocentric) which ignores the Earth location and the slightly extended moon_topocentric function (topocentric). The differences are periodic and it seems to me that I am not at the right “location” on Earth.

The azimuth and right ascension are correct twice a day :laughing:

Here is the current code

function moon_topocentric2(lon, lat, dt::DateTime; height=0.0)
    # UTC as JD
    utc1, utc2 = datetime2julian(dt), 0.0
    
    # Earth orientation (use IERS values for high precision)
    dut1 = 0.0
    xp = 0.0
    yp = 0.0
    
    # Disable refraction
    phpa = 0.0   # pressure (hPa)
    tc   = 0.0   # temperature (C)
    rh   = 0.0   # relative humidity
    wl   = 0.0   # wavelength (micron)
    
    # Build astrometry context
    astrom, eo = SOFA.apco13(
        utc1, utc2,
        dut1,
        lon, lat, height,
        xp, yp,
        phpa, tc, rh, wl
    )
    
    # TT for Moon ephemeris
    tai1, tai2 = SOFA.utctai(utc1, utc2)
    tt1, tt2   = SOFA.taitt(tai1, tai2)
    
    # Moon geocentric GCRS position (AU and AU/day)
    pm, vm = SOFA.moon98(tt1, tt2)
    
    # Calculate distance in AU
    distance_au = sqrt(pm[1]^2 + pm[2]^2 + pm[3]^2)
    
    # Parallax in arcseconds: π = 1 AU / distance
    parallax_arcsec = 1.0 / distance_au
    
    # Cartesian → spherical (GCRS)
    ra_gc, dec_gc = SOFA.c2s(pm)
    
    # Calculate radial velocity (AU/day)
    rv_au_day = (pm[1]*vm[1] + pm[2]*vm[2] + pm[3]*vm[3]) / distance_au
    # Convert to km/s (1 AU/day ≈ 1731.46 km/s)
    rv_km_s = rv_au_day * 1731.46
    
    # GCRS → topocentric apparent place with parallax
    # Note: For Moon, proper motion during the observation is negligible
    # compared to parallax effect, so we can use 0.0 for pmra/pmdec
    ra_topo, dec_topo = SOFA.atciq(
        ra_gc, dec_gc,
        0.0, 0.0,           # proper motion (negligible for Moon)
        parallax_arcsec,    # parallax (arcsec) - THIS IS THE KEY!
        rv_km_s,            # radial velocity (km/s)
        astrom
    )
    
    moon_dist_au = sqrt(sum(pm.^2))
    # 180 × 3600 / π, for reference ;)
    parallax = 1.0 / (moon_dist_au * 206264.806247)  # parallax in arcseconds
    moonobs = SOFA.atco13(ra_topo, dec_topo, 0.0, 0.0, parallax, 0.0, utc1, utc2, 0.0, lon, lat, height, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)

    az, el = moonobs.azi, π/2 - moonobs.zen

    return (ra=mod2pi(ra_topo), dec=dec_topo, distance_au = distance_au, azimuth=az, elevation=el)
end