mirror of
https://github.com/SatDump/SatDump
synced 2026-08-13 17:47:30 -04:00
733 lines
26 KiB
C
733 lines
26 KiB
C
/**
|
|
* @file
|
|
*
|
|
* @date Created on Mar 5, 2025
|
|
* @author G. Kaplan and Attila Kovacs
|
|
*
|
|
* Various transformations between different coordinate systems used in astronomy, such as
|
|
* equatorial, ecliptic, Galactic, or local horizontal coordinate systems.
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
/// \cond PRIVATE
|
|
#define __NOVAS_INTERNAL_API__ ///< Use definitions meant for internal use by SuperNOVAS only
|
|
#include "novas.h"
|
|
/// \endcond
|
|
|
|
|
|
/**
|
|
* Convert ecliptic longitude and latitude to right ascension and declination. To convert
|
|
* GCRS ecliptic coordinates (mean ecliptic and equinox of J2000.0), set 'coord_sys' to
|
|
* NOVAS_GCRS_EQUATOR(2); in this case the value of 'jd_tt' can be set to anything, since
|
|
* J2000.0 is assumed. Otherwise, all input coordinates are dynamical at'jd_tt'.
|
|
*
|
|
* @param jd_tt [day] Terrestrial Time (TT) based Julian date. (Unused if 'coord_sys'
|
|
* is NOVAS_GCRS_EQUATOR[2])
|
|
* @param coord_sys The astrometric reference system of the coordinates. If 'coord_sys' is
|
|
* NOVAS_GCRS_EQUATOR(2), the input GCRS coordinates are converted to
|
|
* J2000 ecliptic coordinates.
|
|
* @param accuracy NOVAS_FULL_ACCURACY (0) or NOVAS_REDUCED_ACCURACY (1)
|
|
* @param elon [deg] Ecliptic longitude in degrees, referred to specified ecliptic and
|
|
* equinox of date.
|
|
* @param elat [deg] Ecliptic latitude in degrees, referred to specified ecliptic and
|
|
* equinox of date.
|
|
* @param[out] ra [h] Right ascension in hours, referred to specified equator and equinox
|
|
* of date.
|
|
* @param[out] dec [deg] Declination in degrees, referred to specified equator and equinox
|
|
* of date.
|
|
|
|
* @return 0 if successful, or else 1 if the value of 'coord_sys' is invalid.
|
|
*
|
|
* @since 1.0
|
|
* @author Attila Kovacs
|
|
*
|
|
* @sa ecl2equ_vec(), equ2ecl()
|
|
*/
|
|
int ecl2equ(double jd_tt, enum novas_equator_type coord_sys, enum novas_accuracy accuracy, double elon, double elat,
|
|
double *restrict ra, double *restrict dec) {
|
|
static const char *fn = "ecl2equ";
|
|
double coslat, pos[3], xyproj;
|
|
|
|
if(!ra || !dec)
|
|
return novas_error(-1, EINVAL, fn, "NULL output pointer: ra=%p, dec=%p", ra, dec);
|
|
|
|
// Form position vector in equatorial system from input coordinates.
|
|
elon *= DEGREE;
|
|
elat *= DEGREE;
|
|
coslat = cos(elat);
|
|
|
|
pos[0] = coslat * cos(elon);
|
|
pos[1] = coslat * sin(elon);
|
|
pos[2] = sin(elat);
|
|
|
|
// Convert the vector from equatorial to ecliptic system.
|
|
prop_error(fn, ecl2equ_vec(jd_tt, coord_sys, accuracy, pos, pos), 0);
|
|
|
|
// Decompose ecliptic vector into ecliptic longitude and latitude.
|
|
xyproj = sqrt(pos[0] * pos[0] + pos[1] * pos[1]);
|
|
*ra = xyproj ? atan2(pos[1], pos[0]) / HOURANGLE : 0.0;
|
|
if(*ra < 0.0)
|
|
*ra += DAY_HOURS;
|
|
|
|
*dec = atan2(pos[2], xyproj) / DEGREE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Converts galactic longitude and latitude to ICRS right ascension and declination.
|
|
*
|
|
* REFERENCES:
|
|
* <ol>
|
|
* <li>Hipparcos and Tycho Catalogues, Vol. 1, Section 1.5.3.</li>
|
|
* </ol>
|
|
*
|
|
* @param glon [deg] Galactic longitude in degrees.
|
|
* @param glat [deg] Galactic latitude in degrees.
|
|
* @param[out] ra [h] ICRS right ascension in hours.
|
|
* @param[out] dec [deg] ICRS declination in degrees.
|
|
|
|
*
|
|
* @return 0 if successful, or -1 if either of the output pointer arguments
|
|
* are NULL.
|
|
*
|
|
* @since 1.0
|
|
* @author Attila Kovacs
|
|
*
|
|
* @sa equ2gal()
|
|
*/
|
|
int gal2equ(double glon, double glat, double *restrict ra, double *restrict dec) {
|
|
double pos1[3], pos2[3], xyproj, coslat;
|
|
|
|
// Rotation matrix A_g from Hipparcos documentation eq. 1.5.11.
|
|
// AK: Transposed compared to NOVAS C 3.1 for dot product handling.
|
|
static const double ag[3][3] = { //
|
|
{ -0.0548755604, +0.4941094279, -0.8676661490 }, //
|
|
{ -0.8734370902, -0.4448296300, -0.1980763734 }, //
|
|
{ -0.4838350155, +0.7469822445, +0.4559837762 } };
|
|
|
|
if(!ra || !dec)
|
|
return novas_error(-1, EINVAL, "gal2equ", "NULL output pointer: ra=%p, dec=%p", ra, dec);
|
|
|
|
// Form position vector in equatorial system from input coordinates
|
|
glon *= DEGREE;
|
|
glat *= DEGREE;
|
|
coslat = cos(glat);
|
|
|
|
pos1[0] = coslat * cos(glon);
|
|
pos1[1] = coslat * sin(glon);
|
|
pos1[2] = sin(glat);
|
|
|
|
// Rotate position vector to galactic system, using Hipparcos documentation eq. 1.5.13.
|
|
pos2[0] = novas_vdot(ag[0], pos1);
|
|
pos2[1] = novas_vdot(ag[1], pos1);
|
|
pos2[2] = novas_vdot(ag[2], pos1);
|
|
|
|
// Decompose galactic vector into longitude and latitude.
|
|
xyproj = sqrt(pos2[0] * pos2[0] + pos2[1] * pos2[1]);
|
|
*ra = xyproj ? atan2(pos2[1], pos2[0]) / HOURANGLE : 0.0;
|
|
if(*ra < 0.0)
|
|
*ra += DAY_HOURS;
|
|
|
|
*dec = atan2(pos2[2], xyproj) / DEGREE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Converts a position vector in the Earth-fixed ITRS frame to astrometric (unrefracted) azimuth
|
|
* and zenith angles at the specified observer location.
|
|
*
|
|
* @param location Observer location on Earth
|
|
* @param itrs 3-vector position in Earth-fixed ITRS frame
|
|
* @param[out] az [deg] astrometric (unrefracted) azimuth angle at observer location [0:360].
|
|
* It may be NULL if not required.
|
|
* @param[out] za [deg] astrometric (unrefracted) zenith angle at observer location [0:180].
|
|
* It may be NULL if not required.
|
|
* @return 0 if successful, or else -1 if the location or the input vector is NULL.
|
|
*
|
|
* @since 1.0
|
|
* @author Attila Kovacs
|
|
*
|
|
* @sa hor_to_itrs(), cirs_to_itrs(), tod_to_itrs(), refract_astro()
|
|
*/
|
|
int itrs_to_hor(const on_surface *restrict location, const double *restrict itrs, double *restrict az, double *restrict za) {
|
|
double uze[3], une[3], uwe[3];
|
|
double lat, lon, coslat, sinlat, coslon, sinlon;
|
|
double pn, pw, pz, proj;
|
|
|
|
// Default output values in case of error return.
|
|
if(az)
|
|
*az = NAN;
|
|
if(za)
|
|
*za = NAN;
|
|
|
|
if(!location || !itrs)
|
|
return novas_error(-1, EINVAL, "itrs_to_hor", "NULL input location=%p or ITRS pos=%p", location, itrs);
|
|
|
|
lat = location->latitude * DEGREE;
|
|
lon = location->longitude * DEGREE;
|
|
|
|
coslat = cos(lat);
|
|
sinlat = sin(lat);
|
|
coslon = cos(lon);
|
|
sinlon = sin(lon);
|
|
|
|
// Define vector toward local north in Earth-fixed system (x axis).
|
|
une[0] = -sinlat * coslon;
|
|
une[1] = -sinlat * sinlon;
|
|
une[2] = coslat;
|
|
|
|
// Define vector toward local west in Earth-fixed system (y axis).
|
|
uwe[0] = sinlon;
|
|
uwe[1] = -coslon;
|
|
uwe[2] = 0.0;
|
|
|
|
// Define vector toward local zenith in Earth-fixed system (z axis).
|
|
uze[0] = coslat * coslon;
|
|
uze[1] = coslat * sinlon;
|
|
uze[2] = sinlat;
|
|
|
|
// Obtain vectors in celestial system.
|
|
|
|
// Compute coordinates of object w.r.t orthonormal basis.
|
|
|
|
// Compute components of 'p' - projections of 'p' onto rotated
|
|
// Earth-fixed basis vectors.
|
|
pn = novas_vdot(itrs, une);
|
|
pw = novas_vdot(itrs, uwe);
|
|
pz = novas_vdot(itrs, uze);
|
|
|
|
// Compute azimuth and zenith distance.
|
|
proj = sqrt(pn * pn + pw * pw);
|
|
|
|
if(az) {
|
|
*az = proj > 0.0 ? -atan2(pw, pn) / DEGREE : 0.0;
|
|
if(*az < 0.0)
|
|
*az += DEG360;
|
|
}
|
|
|
|
if(za)
|
|
*za = atan2(proj, pz) / DEGREE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Converts astrometric (unrefracted) azimuth and zenith angles at the specified observer location
|
|
* to a unit position vector in the Earth-fixed ITRS frame.
|
|
*
|
|
* @param location Observer location on Earth
|
|
* @param az [deg] astrometric (unrefracted) azimuth angle at observer location [0:360].
|
|
* It may be NULL if not required.
|
|
* @param za [deg] astrometric (unrefracted) zenith angle at observer location [0:180]. It
|
|
* may be NULL if not required.
|
|
* @param[out] itrs Unit 3-vector direction in Earth-fixed ITRS frame
|
|
* @return 0 if successful, or else -1 if the location or the input vector is NULL.
|
|
*
|
|
* @since 1.0
|
|
* @author Attila Kovacs
|
|
*
|
|
* @sa itrs_to_hor(), itrs_to_cirs(), itrs_to_tod(), refract()
|
|
*/
|
|
int hor_to_itrs(const on_surface *restrict location, double az, double za, double *restrict itrs) {
|
|
double in[3], uze[3], une[3], uwe[3];
|
|
double sinza;
|
|
double lat, lon, coslat, sinlat, coslon, sinlon;
|
|
|
|
if(!location || !itrs)
|
|
return novas_error(-1, EINVAL, "hor_to_itrs", "NULL input location=%p or output itrs=%p position", location, itrs);
|
|
|
|
az *= -DEGREE;
|
|
za *= DEGREE;
|
|
|
|
sinza = sin(za);
|
|
in[0] = sinza * cos(az);
|
|
in[1] = sinza * sin(az);
|
|
in[2] = cos(za);
|
|
|
|
lat = location->latitude * DEGREE;
|
|
lon = location->longitude * DEGREE;
|
|
|
|
coslat = cos(lat);
|
|
sinlat = sin(lat);
|
|
coslon = cos(lon);
|
|
sinlon = sin(lon);
|
|
|
|
// Define vector toward local zenith in Earth-fixed system (z axis).
|
|
uze[0] = coslat * coslon;
|
|
uze[1] = coslat * sinlon;
|
|
uze[2] = sinlat;
|
|
|
|
// Define vector toward local north in Earth-fixed system (x axis).
|
|
une[0] = -sinlat * coslon;
|
|
une[1] = -sinlat * sinlon;
|
|
une[2] = coslat;
|
|
|
|
// Define vector toward local west in Earth-fixed system (y axis).
|
|
uwe[0] = sinlon;
|
|
uwe[1] = -coslon;
|
|
uwe[2] = 0.0;
|
|
|
|
// Calculate ITRS positions from NWZ components at ITRS origin
|
|
itrs[0] = une[0] * in[0] + uwe[0] * in[1] + uze[0] * in[2];
|
|
itrs[1] = une[1] * in[0] + uwe[1] * in[1] + uze[1] * in[2];
|
|
itrs[2] = une[2] * in[0] + uwe[2] * in[1] + uze[2] * in[2];
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Converts ICRS right ascension and declination to galactic longitude and latitude.
|
|
*
|
|
* REFERENCES:
|
|
* <ol>
|
|
* <li>Hipparcos and Tycho Catalogues, Vol. 1, Section 1.5.3.</li>
|
|
* </ol>
|
|
*
|
|
* @param ra [h] ICRS right ascension in hours.
|
|
* @param dec [deg] ICRS declination in degrees.
|
|
* @param[out] glon [deg] Galactic longitude in degrees.
|
|
* @param[out] glat [deg] Galactic latitude in degrees.
|
|
*
|
|
* @return 0 if successful, or -1 if either of the output pointer arguments
|
|
* are NULL.
|
|
*
|
|
* @sa gal2equ()
|
|
*/
|
|
int equ2gal(double ra, double dec, double *restrict glon, double *restrict glat) {
|
|
double pos1[3], pos2[3], xyproj, cosd;
|
|
|
|
// Rotation matrix A_g from Hipparcos documentation eq. 1.5.11.
|
|
// AK: Transposed compared to NOVAS C 3.1 for dot product handling.
|
|
static const double ag[3][3] = { //
|
|
{ -0.0548755604, -0.8734370902, -0.4838350155 }, //
|
|
{ +0.4941094279, -0.4448296300, +0.7469822445 }, //
|
|
{ -0.8676661490, -0.1980763734, +0.4559837762 } };
|
|
|
|
if(!glon || !glat)
|
|
return novas_error(-1, EINVAL, "equ2gal", "NULL output pointer: glon=%p, glat=%p", glon, glat);
|
|
|
|
// Form position vector in equatorial system from input coordinates
|
|
ra *= HOURANGLE;
|
|
dec *= DEGREE;
|
|
cosd = cos(dec);
|
|
|
|
pos1[0] = cosd * cos(ra);
|
|
pos1[1] = cosd * sin(ra);
|
|
pos1[2] = sin(dec);
|
|
|
|
// Rotate position vector to galactic system, using Hipparcos documentation eq. 1.5.13.
|
|
pos2[0] = novas_vdot(ag[0], pos1);
|
|
pos2[1] = novas_vdot(ag[1], pos1);
|
|
pos2[2] = novas_vdot(ag[2], pos1);
|
|
|
|
// Decompose galactic vector into longitude and latitude.
|
|
xyproj = sqrt(pos2[0] * pos2[0] + pos2[1] * pos2[1]);
|
|
*glon = xyproj ? atan2(pos2[1], pos2[0]) / DEGREE : 0.0;
|
|
if(*glon < 0.0)
|
|
*glon += DEG360;
|
|
|
|
*glat = atan2(pos2[2], xyproj) / DEGREE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Convert right ascension and declination to ecliptic longitude and latitude. To convert
|
|
* GCRS RA and dec to ecliptic coordinates (mean ecliptic and equinox of J2000.0), set
|
|
* 'coord_sys' to NOVAS_GCRS_EQUATOR(2); in this case the value of 'jd_tt' can be set to
|
|
* anything, since J2000.0 is assumed. Otherwise, all input coordinates are dynamical at
|
|
* 'jd_tt'.
|
|
*
|
|
* @param jd_tt [day] Terrestrial Time (TT) based Julian date. (Unused if 'coord_sys'
|
|
* is NOVAS_GCRS_EQUATOR[2])
|
|
* @param coord_sys The astrometric reference system of the coordinates. If 'coord_sys' is
|
|
* NOVAS_GCRS_EQUATOR(2), the input GCRS coordinates are converted to
|
|
* J2000 ecliptic coordinates.
|
|
* @param accuracy NOVAS_FULL_ACCURACY (0) or NOVAS_REDUCED_ACCURACY (1)
|
|
* @param ra [h] Right ascension in hours, referred to specified equator and equinox
|
|
* of date.
|
|
* @param dec [deg] Declination in degrees, referred to specified equator and equinox
|
|
* of date.
|
|
* @param[out] elon [deg] Ecliptic longitude in degrees, referred to specified ecliptic and
|
|
* equinox of date.
|
|
* @param[out] elat [deg] Ecliptic latitude in degrees, referred to specified ecliptic and
|
|
* equinox of date.
|
|
* @return 0 if successful, or else 1 if the value of 'coord_sys' is invalid.
|
|
*
|
|
* @sa equ2ecl_vec(), ecl2equ()
|
|
*/
|
|
short equ2ecl(double jd_tt, enum novas_equator_type coord_sys, enum novas_accuracy accuracy, double ra, double dec,
|
|
double *restrict elon, double *restrict elat) {
|
|
static const char *fn = "equ2ecl";
|
|
double cosd, pos[3], xyproj;
|
|
|
|
if(!elon || !elat)
|
|
return novas_error(-1, EINVAL, fn, "NULL output pointer: elon=%p, elat=%p", elon, elat);
|
|
|
|
// Form position vector in equatorial system from input coordinates.
|
|
ra *= HOURANGLE;
|
|
dec *= DEGREE;
|
|
cosd = cos(dec);
|
|
|
|
pos[0] = cosd * cos(ra);
|
|
pos[1] = cosd * sin(ra);
|
|
pos[2] = sin(dec);
|
|
|
|
// Convert the vector from equatorial to ecliptic system.
|
|
prop_error(fn, equ2ecl_vec(jd_tt, coord_sys, accuracy, pos, pos), 0);
|
|
|
|
// Decompose ecliptic vector into ecliptic longitude and latitude.
|
|
xyproj = sqrt(pos[0] * pos[0] + pos[1] * pos[1]);
|
|
|
|
*elon = (xyproj > 0.0) ? atan2(pos[1], pos[0]) / DEGREE : 0.0;
|
|
if(*elon < 0.0)
|
|
*elon += DEG360;
|
|
|
|
*elat = atan2(pos[2], xyproj) / DEGREE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Converts an equatorial position vector to an ecliptic position vector. To convert
|
|
* ICRS RA and dec to ecliptic coordinates (mean ecliptic and equinox of J2000.0), set
|
|
* 'coord_sys' to NOVAS_GCRS_EQUATOR(2); in this case the value of 'jd_tt' can be set
|
|
* to anything, since J2000.0 is assumed. Otherwise, all input coordinates are dynamical
|
|
* at 'jd_tt'.
|
|
*
|
|
* @param jd_tt [day] Terrestrial Time (TT) based Julian date. (Unused if 'coord_sys'
|
|
* is NOVAS_GCRS_EQUATOR[2])
|
|
* @param coord_sys The astrometric reference system type of the coordinates.
|
|
* @param accuracy NOVAS_FULL_ACCURACY (0) or NOVAS_REDUCED_ACCURACY (1)
|
|
* @param in Position vector, referred to specified equator and equinox of date.
|
|
* @param[out] out Position vector, referred to specified ecliptic and equinox of date.
|
|
* It can be the same vector as the input. If 'coord_sys' is
|
|
* NOVAS_GCRS_EQUATOR(2), the input GCRS coordinates are converted to
|
|
* J2000 ecliptic coordinates.
|
|
* @return 0 if successful, -1 if either vector argument is NULL or the accuracy
|
|
* is invalid, or else 1 if the value of 'coord_sys' is invalid.
|
|
*
|
|
* @sa equ2ecl(), ecl2equ_vec()
|
|
*/
|
|
short equ2ecl_vec(double jd_tt, enum novas_equator_type coord_sys, enum novas_accuracy accuracy,
|
|
const double *in, double *out) {
|
|
static const char *fn = "equ2ecl_vec";
|
|
double pos0[3], obl, c, s;
|
|
|
|
if(!in || !out)
|
|
return novas_error(-1, EINVAL, fn, "NULL input or output 3-vector: in=%p, out=%p", in, out);
|
|
|
|
if(accuracy != NOVAS_FULL_ACCURACY && accuracy != NOVAS_REDUCED_ACCURACY)
|
|
return novas_error(-1, EINVAL, fn, "invalid accuracy: %d", accuracy);
|
|
|
|
// Get obliquity, depending upon the "system" of the input coordinates.
|
|
switch(coord_sys) {
|
|
case NOVAS_MEAN_EQUATOR: // Input: mean equator and equinox of date
|
|
case NOVAS_TRUE_EQUATOR: { // Input: true equator and equinox of date
|
|
double oblm = NAN, oblt = NAN;
|
|
|
|
// For these calculations we can assume TDB = TT (< 2 ms difference)
|
|
const double jd_tdb = jd_tt;
|
|
|
|
memcpy(pos0, in, sizeof(pos0));
|
|
|
|
e_tilt(jd_tdb, accuracy, &oblm, &oblt, NULL, NULL, NULL);
|
|
|
|
obl = (coord_sys == NOVAS_MEAN_EQUATOR ? oblm : oblt) * DEGREE;
|
|
break;
|
|
}
|
|
|
|
case NOVAS_GCRS_EQUATOR: /* Input: GCRS */{
|
|
static THREAD_LOCAL enum novas_accuracy acc_2000 = -1;
|
|
static THREAD_LOCAL double ob2000;
|
|
|
|
frame_tie(in, ICRS_TO_J2000, pos0);
|
|
|
|
if(accuracy != acc_2000) {
|
|
ob2000 = mean_obliq(JD_J2000);
|
|
acc_2000 = accuracy;
|
|
}
|
|
|
|
obl = ob2000 * ARCSEC;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
return novas_error(1, EINVAL, fn, "invalid equator type: %d", coord_sys);
|
|
}
|
|
|
|
c = cos(obl);
|
|
s = sin(obl);
|
|
|
|
// Rotate position vector to ecliptic system.
|
|
out[0] = pos0[0];
|
|
out[1] = pos0[1] * c + pos0[2] * s;
|
|
out[2] = -pos0[1] * s + pos0[2] * c;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Converts an ecliptic position vector to an equatorial position vector. To convert
|
|
* ecliptic coordinates (mean ecliptic and equinox of J2000.0) to GCRS RA and dec to, set
|
|
* 'coord_sys' to NOVAS_GCRS_EQUATOR(2); in this case the value of 'jd_tt' can be set to
|
|
* anything, since J2000.0 is assumed. Otherwise, all input coordinates are dynamical at
|
|
* 'jd_tt'.
|
|
*
|
|
* @param jd_tt [day] Terrestrial Time (TT) based Julian date. (Unused if 'coord_sys'
|
|
* is NOVAS_GCRS_EQUATOR[2])
|
|
* @param coord_sys The astrometric reference system type of the coordinates
|
|
* @param accuracy NOVAS_FULL_ACCURACY (0) or NOVAS_REDUCED_ACCURACY (1)
|
|
* @param in Position vector, referred to specified ecliptic and equinox of date.
|
|
* @param[out] out Position vector, referred to specified equator and equinox of date. It
|
|
* can be the same vector as the input.
|
|
* @return 0 if successful, -1 if either vector argument is NULL or the accuracy
|
|
* is invalid, or else 1 if the value of 'coord_sys' is invalid.
|
|
*
|
|
* @sa ecl2equ(), equ2ecl_vec()
|
|
*/
|
|
short ecl2equ_vec(double jd_tt, enum novas_equator_type coord_sys, enum novas_accuracy accuracy,
|
|
const double *in, double *out) {
|
|
static const char *fn = "ecl2equ_vec";
|
|
double obl = 0.0, c, s, x, y, z;
|
|
|
|
if(!in || !out)
|
|
return novas_error(-1, EINVAL, fn, "NULL input or output 3-vector: in=%p, out=%p", in, out);
|
|
|
|
if(accuracy != NOVAS_FULL_ACCURACY && accuracy != NOVAS_REDUCED_ACCURACY)
|
|
return novas_error(-1, EINVAL, fn, "invalid accuracy: %d", accuracy);
|
|
|
|
// Get obliquity, depending upon the "system" of the input coordinates.
|
|
switch(coord_sys) {
|
|
|
|
case NOVAS_MEAN_EQUATOR: // Output: mean equator and equinox of date
|
|
case NOVAS_TRUE_EQUATOR: { // Output: true equator and equinox of date
|
|
double oblm = NAN, oblt = NAN;
|
|
|
|
// For these calculations we can assume TDB = TT (< 2 ms difference)
|
|
const double jd_tdb = jd_tt;
|
|
|
|
e_tilt(jd_tdb, accuracy, &oblm, &oblt, NULL, NULL, NULL);
|
|
obl = (coord_sys == NOVAS_MEAN_EQUATOR ? oblm : oblt) * DEGREE;
|
|
break;
|
|
}
|
|
|
|
case NOVAS_GCRS_EQUATOR: { // Output: GCRS
|
|
static THREAD_LOCAL double ob2000;
|
|
|
|
if(ob2000 == 0.0) {
|
|
ob2000 = mean_obliq(JD_J2000);
|
|
}
|
|
|
|
obl = ob2000 * ARCSEC;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
return novas_error(1, EINVAL, fn, "invalid equator type: %d", coord_sys);
|
|
}
|
|
|
|
x = in[0];
|
|
y = in[1];
|
|
z = in[2];
|
|
|
|
c = cos(obl);
|
|
s = sin(obl);
|
|
|
|
// Rotate position vector to ecliptic system.
|
|
out[0] = x;
|
|
out[1] = y * c - z * s;
|
|
out[2] = y * s + z * c;
|
|
|
|
// Case where output vector is to be in ICRS, rotate from dynamical
|
|
// system to ICRS.
|
|
if(coord_sys == NOVAS_GCRS_EQUATOR) {
|
|
frame_tie(out, J2000_TO_ICRS, out);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @deprecated You should use the frame-based `novas_app_to_hor()` instead, or else the
|
|
* more explicit (less ambiguous) `tod_to_itrs()` followed by `itrs_to_hor()`,
|
|
* and possibly following it with an atmospheric refraction correction if
|
|
* appropriate.
|
|
*
|
|
* Transforms topocentric (TOD) right ascension and declination to zenith distance and
|
|
* azimuth. This method should not be used to convert CIRS apparent coordinates (IAU
|
|
* 2000 standard) -- for those you should use cirs_to_itrs() followed by itrs_to_hor()
|
|
* instead.
|
|
*
|
|
* It uses a method that properly accounts for polar motion, which is significant at the
|
|
* sub-arcsecond level. This function can also adjust coordinates for atmospheric
|
|
* refraction.
|
|
*
|
|
* NOTES:
|
|
* <ul>
|
|
* <li>'xp' and 'yp' can be set to zero if sub-arcsecond accuracy is not needed.</li>
|
|
* <li> The directions 'zd'= 0 (zenith) and 'az'= 0 (north) are here considered fixed in
|
|
* the terrestrial system. Specifically, the zenith is along the geodetic normal, and north
|
|
* is toward the ITRS pole.</li>
|
|
* <li>If 'ref_option' is NOVAS_STANDARD_ATMOSPHERE (1), then 'rar'='ra' and 'decr'='dec'.
|
|
* </ul>
|
|
*
|
|
* REFERENCES:
|
|
* <ol>
|
|
* <li>Kaplan, G. (2008). USNO/AA Technical Note of 28 Apr 2008, "Refraction as a Vector."</li>
|
|
* </ol>
|
|
*
|
|
* @param jd_ut1 [day] UT1 based Julian date
|
|
* @param ut1_to_tt [s] TT - UT1 Time difference in seconds
|
|
* @param accuracy NOVAS_FULL_ACCURACY (0) or NOVAS_REDUCED_ACCURACY (1)
|
|
* @param xp [arcsec] Conventionally-defined x coordinate of celestial intermediate
|
|
* pole with respect to ITRS reference pole, e.g. from IERS Bulletin A.
|
|
* If you have defined pole offsets to be incorporated into the TOD input
|
|
* coordinates (pre-IAU2000 method) via `cel_pole()`, then you should set this
|
|
* to 0.
|
|
* @param yp [arcsec] Conventionally-defined y coordinate of celestial intermediate
|
|
* pole with respect to ITRS reference pole, e.g. from IERS Bulletin A. If you
|
|
* have defined pole offsets to be incorporated into the TOD input coordinates
|
|
* (pre-IAU2000 method) via `cel_pole()`, then you should set this to 0.
|
|
* @param location The observer location
|
|
* @param ra [h] Topocentric apparent (TOD) right ascension of object of interest,
|
|
* referred to true equator and equinox of date.
|
|
* @param dec [deg] Topocentric apparent (TOD) declination of object of interest, referred
|
|
* to true equator and equinox of date.
|
|
* @param ref_option Refraction model to use. E.g., NOVAS_STANDARD_ATMOSPHERE (1), or
|
|
* NOVAS_WEATHER_AT_LOCATION (2) if to use the weather.
|
|
* @param[out] zd [deg] Topocentric zenith distance in degrees (unrefracted).
|
|
* @param[out] az [deg] Topocentric azimuth (measured east from north) in degrees.
|
|
* @param[out] rar [h] Topocentric right ascension of object of interest, in hours, referred
|
|
* to true equator and equinox of date, affected by refraction if 'ref_option'
|
|
* is non-zero. (It may be NULL if not required)
|
|
* @param[out] decr [deg] Topocentric declination of object of interest, in degrees, referred
|
|
* to true equator and equinox of date. (It may be NULL if not required)
|
|
* @return 0 if successful, or -1 if one of the 'zd' or 'az' output pointers are NULL.
|
|
*
|
|
* @sa itrs_to_hor(), tod_to_itrs(), NOVAS_TOD
|
|
*/
|
|
int equ2hor(double jd_ut1, double ut1_to_tt, enum novas_accuracy accuracy, double xp, double yp,
|
|
const on_surface *restrict location, double ra, double dec, enum novas_refraction_model ref_option,
|
|
double *restrict zd, double *restrict az, double *restrict rar, double *restrict decr) {
|
|
|
|
double lon, lat, sinlat, coslat, sinlon, coslon, cosdec;
|
|
double uze[3], une[3], uwe[3], uz[3], un[3], uw[3], p[3];
|
|
double pz, pn, pw, proj, pr[3];
|
|
|
|
// Default output values in case of error return;
|
|
if(az)
|
|
*az = NAN;
|
|
if(zd)
|
|
*zd = NAN;
|
|
if(rar)
|
|
*rar = ra;
|
|
if(decr)
|
|
*decr = dec;
|
|
|
|
if(!location || !zd || !az)
|
|
return novas_error(-1, EINVAL, "equ2hor", "NULL input observer location=%p, output az=%p, or zd=%p pointer", location, az, zd);
|
|
|
|
lon = location->longitude * DEGREE;
|
|
lat = location->latitude * DEGREE;
|
|
|
|
sinlat = sin(lat);
|
|
coslat = cos(lat);
|
|
sinlon = sin(lon);
|
|
coslon = cos(lon);
|
|
|
|
ra *= HOURANGLE;
|
|
dec *= DEGREE;
|
|
|
|
cosdec = cos(dec);
|
|
|
|
// Define unit vector 'p' toward object in celestial system
|
|
// (wrt equator and equinox of date).
|
|
p[0] = cosdec * cos(ra);
|
|
p[1] = cosdec * sin(ra);
|
|
p[2] = sin(dec);
|
|
|
|
// Set up orthonormal basis vectors in local Earth-fixed system.
|
|
|
|
// Define vector toward local zenith in Earth-fixed system (z axis).
|
|
uze[0] = coslat * coslon;
|
|
uze[1] = coslat * sinlon;
|
|
uze[2] = sinlat;
|
|
|
|
// Define vector toward local north in Earth-fixed system (x axis).
|
|
une[0] = -sinlat * coslon;
|
|
une[1] = -sinlat * sinlon;
|
|
une[2] = coslat;
|
|
|
|
// Define vector toward local west in Earth-fixed system (y axis).
|
|
uwe[0] = sinlon;
|
|
uwe[1] = -coslon;
|
|
uwe[2] = 0.0;
|
|
|
|
// Obtain vectors in celestial system.
|
|
|
|
// Rotate Earth-fixed orthonormal basis vectors to celestial system
|
|
// (wrt equator and equinox of date).
|
|
ter2cel(jd_ut1, 0.0, ut1_to_tt, EROT_GST, accuracy, NOVAS_DYNAMICAL_CLASS, xp, yp, uze, uz);
|
|
ter2cel(jd_ut1, 0.0, ut1_to_tt, EROT_GST, accuracy, NOVAS_DYNAMICAL_CLASS, xp, yp, une, un);
|
|
ter2cel(jd_ut1, 0.0, ut1_to_tt, EROT_GST, accuracy, NOVAS_DYNAMICAL_CLASS, xp, yp, uwe, uw);
|
|
|
|
// Compute coordinates of object w.r.t orthonormal basis.
|
|
|
|
// Compute components of 'p' - projections of 'p' onto rotated
|
|
// Earth-fixed basis vectors.
|
|
pz = novas_vdot(p, uz);
|
|
pn = novas_vdot(p, un);
|
|
pw = novas_vdot(p, uw);
|
|
|
|
// Compute azimuth and zenith distance.
|
|
proj = sqrt(pn * pn + pw * pw);
|
|
*az = proj ? -atan2(pw, pn) / DEGREE : 0.0;
|
|
if(*az < 0.0)
|
|
*az += DEG360;
|
|
|
|
*zd = atan2(proj, pz) / DEGREE;
|
|
|
|
// Apply atmospheric refraction if requested.
|
|
if(ref_option) {
|
|
// Get refraction in zenith distance.
|
|
const double zd0 = *zd;
|
|
const double refr = refract_astro(location, ref_option, zd0);
|
|
|
|
// Apply refraction to celestial coordinates of object.
|
|
if(refr) {
|
|
// Shift position vector of object in celestial system to account
|
|
// for refraction (see USNO/AA Technical Note 1998-09).
|
|
const double sinzd = sin(*zd * DEGREE);
|
|
const double coszd = cos(*zd * DEGREE);
|
|
const double sinzd0 = sin(zd0 * DEGREE);
|
|
const double coszd0 = cos(zd0 * DEGREE);
|
|
|
|
int j;
|
|
|
|
*zd -= refr;
|
|
|
|
// Compute refracted position vector.
|
|
for(j = 3; --j >= 0;)
|
|
pr[j] = ((p[j] - coszd0 * uz[j]) / sinzd0) * sinzd + uz[j] * coszd;
|
|
|
|
// Compute refracted right ascension and declination.
|
|
proj = sqrt(pr[0] * pr[0] + pr[1] * pr[1]);
|
|
|
|
if(rar) {
|
|
*rar = proj ? atan2(pr[1], pr[0]) / HOURANGLE : 0.0;
|
|
if(*rar < 0.0)
|
|
*rar += DAY_HOURS;
|
|
}
|
|
|
|
if(decr)
|
|
*decr = atan2(pr[2], proj) / DEGREE;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|